Merge "Suppress immersive mode confirmation if navbar is empty" into nyc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index d5f36bd..d3ee682 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19652,6 +19652,7 @@
     field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
     field public static final int ENCODING_AC3 = 5; // 0x5
     field public static final int ENCODING_DEFAULT = 1; // 0x1
+    field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
     field public static final int ENCODING_E_AC3 = 6; // 0x6
diff --git a/api/system-current.txt b/api/system-current.txt
index 16c75cc..13ad2d6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21160,6 +21160,7 @@
     field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
     field public static final int ENCODING_AC3 = 5; // 0x5
     field public static final int ENCODING_DEFAULT = 1; // 0x1
+    field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
     field public static final int ENCODING_E_AC3 = 6; // 0x6
diff --git a/api/test-current.txt b/api/test-current.txt
index 7fdbb64..c6359dd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -19722,6 +19722,7 @@
     field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
     field public static final int ENCODING_AC3 = 5; // 0x5
     field public static final int ENCODING_DEFAULT = 1; // 0x1
+    field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
     field public static final int ENCODING_E_AC3 = 6; // 0x6
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 7c8842c..3cf13d9 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -3,14 +3,16 @@
 
 LOCAL_SRC_FILES:= \
     bootanimation_main.cpp \
-    AudioPlayer.cpp \
+    audioplay.cpp \
     BootAnimation.cpp
 
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
-LOCAL_C_INCLUDES += external/tinyalsa/include
+LOCAL_C_INCLUDES += \
+    external/tinyalsa/include \
+    frameworks/wilhelm/include
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
@@ -23,6 +25,7 @@
     libEGL \
     libGLESv1_CM \
     libgui \
+    libOpenSLES \
     libtinyalsa
 
 LOCAL_MODULE:= bootanimation
@@ -33,4 +36,8 @@
 LOCAL_32_BIT_ONLY := true
 endif
 
+# get asserts to work
+APP_OPTIM := debug
+LOCAL_CFLAGS += -UNDEBUG
+
 include $(BUILD_EXECUTABLE)
diff --git a/cmds/bootanimation/AudioPlayer.cpp b/cmds/bootanimation/AudioPlayer.cpp
deleted file mode 100644
index 2932130..0000000
--- a/cmds/bootanimation/AudioPlayer.cpp
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_NDEBUG 0
-#define LOG_TAG "BootAnim_AudioPlayer"
-
-#include "AudioPlayer.h"
-
-#include <androidfw/ZipFileRO.h>
-#include <tinyalsa/asoundlib.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-#define ID_RIFF 0x46464952
-#define ID_WAVE 0x45564157
-#define ID_FMT  0x20746d66
-#define ID_DATA 0x61746164
-
-// Maximum line length for audio_conf.txt
-// We only accept lines less than this length to avoid overflows using sscanf()
-#define MAX_LINE_LENGTH 1024
-
-struct riff_wave_header {
-    uint32_t riff_id;
-    uint32_t riff_sz;
-    uint32_t wave_id;
-};
-
-struct chunk_header {
-    uint32_t id;
-    uint32_t sz;
-};
-
-struct chunk_fmt {
-    uint16_t audio_format;
-    uint16_t num_channels;
-    uint32_t sample_rate;
-    uint32_t byte_rate;
-    uint16_t block_align;
-    uint16_t bits_per_sample;
-};
-
-
-namespace android {
-
-AudioPlayer::AudioPlayer()
-    :   mCard(-1),
-        mDevice(-1),
-        mPeriodSize(0),
-        mPeriodCount(0),
-        mCurrentFile(NULL)
-{
-}
-
-AudioPlayer::~AudioPlayer() {
-}
-
-static bool setMixerValue(struct mixer* mixer, const char* name, const char* values)
-{
-    if (!mixer) {
-        ALOGE("no mixer in setMixerValue");
-        return false;
-    }
-    struct mixer_ctl *ctl = mixer_get_ctl_by_name(mixer, name);
-    if (!ctl) {
-        ALOGE("mixer_get_ctl_by_name failed for %s", name);
-        return false;
-    }
-
-    enum mixer_ctl_type type = mixer_ctl_get_type(ctl);
-    int numValues = mixer_ctl_get_num_values(ctl);
-    int intValue;
-    char stringValue[MAX_LINE_LENGTH];
-
-    for (int i = 0; i < numValues && values; i++) {
-        // strip leading space
-        while (*values == ' ') values++;
-        if (*values == 0) break;
-
-        switch (type) {
-            case MIXER_CTL_TYPE_BOOL:
-            case MIXER_CTL_TYPE_INT:
-                if (sscanf(values, "%d", &intValue) == 1) {
-                    if (mixer_ctl_set_value(ctl, i, intValue) != 0) {
-                        ALOGE("mixer_ctl_set_value failed for %s %d", name, intValue);
-                    }
-                } else {
-                    ALOGE("Could not parse %s as int for %s", values, name);
-                }
-                break;
-            case MIXER_CTL_TYPE_ENUM:
-                if (sscanf(values, "%s", stringValue) == 1) {
-                    if (mixer_ctl_set_enum_by_string(ctl, stringValue) != 0) {
-                        ALOGE("mixer_ctl_set_enum_by_string failed for %s %s", name, stringValue);
-                    }
-                } else {
-                    ALOGE("Could not parse %s as enum for %s", values, name);
-                }
-                break;
-            default:
-                ALOGE("unsupported mixer type %d for %s", type, name);
-                break;
-        }
-
-        values = strchr(values, ' ');
-    }
-
-    return true;
-}
-
-
-/*
- * Parse the audio configuration file.
- * The file is named audio_conf.txt and must begin with the following header:
- *
- * card=<ALSA card number>
- * device=<ALSA device number>
- * period_size=<period size>
- * period_count=<period count>
- *
- * This header is followed by zero or more mixer settings, each with the format:
- * mixer "<name>" = <value list>
- * Since mixer names can contain spaces, the name must be enclosed in double quotes.
- * The values in the value list can be integers, booleans (represented by 0 or 1)
- * or strings for enum values.
- */
-bool AudioPlayer::init(const char* config)
-{
-    int tempInt;
-    struct mixer* mixer = NULL;
-    char    name[MAX_LINE_LENGTH];
-
-    for (;;) {
-        const char* endl = strstr(config, "\n");
-        if (!endl) break;
-        String8 line(config, endl - config);
-        if (line.length() >= MAX_LINE_LENGTH) {
-            ALOGE("Line too long in audio_conf.txt");
-            return false;
-        }
-        const char* l = line.string();
-
-        if (sscanf(l, "card=%d", &tempInt) == 1) {
-            ALOGD("card=%d", tempInt);
-            mCard = tempInt;
-
-            mixer = mixer_open(mCard);
-            if (!mixer) {
-                ALOGE("could not open mixer for card %d", mCard);
-                return false;
-            }
-        } else if (sscanf(l, "device=%d", &tempInt) == 1) {
-            ALOGD("device=%d", tempInt);
-            mDevice = tempInt;
-        } else if (sscanf(l, "period_size=%d", &tempInt) == 1) {
-            ALOGD("period_size=%d", tempInt);
-            mPeriodSize = tempInt;
-        } else if (sscanf(l, "period_count=%d", &tempInt) == 1) {
-            ALOGD("period_count=%d", tempInt);
-            mPeriodCount = tempInt;
-        } else if (sscanf(l, "mixer \"%[0-9a-zA-Z _]s\"", name) == 1) {
-            const char* values = strchr(l, '=');
-            if (values) {
-                values++;   // skip '='
-                ALOGD("name: \"%s\" = %s", name, values);
-                setMixerValue(mixer, name, values);
-            } else {
-                ALOGE("values missing for name: \"%s\"", name);
-            }
-        }
-        config = ++endl;
-    }
-
-    mixer_close(mixer);
-
-    if (mCard >= 0 && mDevice >= 0) {
-        return true;
-    }
-
-    return false;
-}
-
-void AudioPlayer::playFile(FileMap* fileMap) {
-    // stop any currently playing sound
-    requestExitAndWait();
-
-    mCurrentFile = fileMap;
-    run("bootanim audio", PRIORITY_URGENT_AUDIO);
-}
-
-bool AudioPlayer::threadLoop()
-{
-    struct pcm_config config;
-    struct pcm *pcm = NULL;
-    bool moreChunks = true;
-    const struct chunk_fmt* chunkFmt = NULL;
-    int bufferSize;
-    const uint8_t* wavData;
-    size_t wavLength;
-    const struct riff_wave_header* wavHeader;
-
-    if (mCurrentFile == NULL) {
-        ALOGE("mCurrentFile is NULL");
-        return false;
-     }
-
-    wavData = (const uint8_t *)mCurrentFile->getDataPtr();
-    if (!wavData) {
-        ALOGE("Could not access WAV file data");
-        goto exit;
-    }
-    wavLength = mCurrentFile->getDataLength();
-
-    wavHeader = (const struct riff_wave_header *)wavData;
-    if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
-        (wavHeader->wave_id != ID_WAVE)) {
-        ALOGE("Error: audio file is not a riff/wave file\n");
-        goto exit;
-    }
-    wavData += sizeof(*wavHeader);
-    wavLength -= sizeof(*wavHeader);
-
-    do {
-        const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData;
-        if (wavLength < sizeof(*chunkHeader)) {
-            ALOGE("EOF reading chunk headers");
-            goto exit;
-        }
-
-        wavData += sizeof(*chunkHeader);
-        wavLength -=  sizeof(*chunkHeader);
-
-        switch (chunkHeader->id) {
-            case ID_FMT:
-                chunkFmt = (const struct chunk_fmt *)wavData;
-                wavData += chunkHeader->sz;
-                wavLength -= chunkHeader->sz;
-                break;
-            case ID_DATA:
-                /* Stop looking for chunks */
-                moreChunks = 0;
-                break;
-            default:
-                /* Unknown chunk, skip bytes */
-                wavData += chunkHeader->sz;
-                wavLength -= chunkHeader->sz;
-        }
-    } while (moreChunks);
-
-    if (!chunkFmt) {
-        ALOGE("format not found in WAV file");
-        goto exit;
-    }
-
-
-    memset(&config, 0, sizeof(config));
-    config.channels = chunkFmt->num_channels;
-    config.rate = chunkFmt->sample_rate;
-    config.period_size = mPeriodSize;
-    config.period_count = mPeriodCount;
-    config.start_threshold = mPeriodSize / 4;
-    config.stop_threshold = INT_MAX;
-    config.avail_min = config.start_threshold;
-    if (chunkFmt->bits_per_sample != 16) {
-        ALOGE("only 16 bit WAV files are supported");
-        goto exit;
-    }
-    config.format = PCM_FORMAT_S16_LE;
-
-    pcm = pcm_open(mCard, mDevice, PCM_OUT, &config);
-    if (!pcm || !pcm_is_ready(pcm)) {
-        ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm));
-        goto exit;
-    }
-
-    bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
-
-    while (wavLength > 0) {
-        if (exitPending()) goto exit;
-        size_t count = bufferSize;
-        if (count > wavLength)
-            count = wavLength;
-
-        if (pcm_write(pcm, wavData, count)) {
-            ALOGE("pcm_write failed (%s)", pcm_get_error(pcm));
-            goto exit;
-        }
-        wavData += count;
-        wavLength -= count;
-    }
-
-exit:
-    if (pcm)
-        pcm_close(pcm);
-    delete mCurrentFile;
-    mCurrentFile = NULL;
-    return false;
-}
-
-} // namespace android
diff --git a/cmds/bootanimation/AudioPlayer.h b/cmds/bootanimation/AudioPlayer.h
deleted file mode 100644
index 1def0ae..0000000
--- a/cmds/bootanimation/AudioPlayer.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _BOOTANIMATION_AUDIOPLAYER_H
-#define _BOOTANIMATION_AUDIOPLAYER_H
-
-#include <utils/Thread.h>
-#include <utils/FileMap.h>
-
-namespace android {
-
-class AudioPlayer : public Thread
-{
-public:
-                AudioPlayer();
-    virtual     ~AudioPlayer();
-    bool        init(const char* config);
-
-    void        playFile(FileMap* fileMap);
-
-private:
-    virtual bool        threadLoop();
-
-private:
-    int                 mCard;      // ALSA card to use
-    int                 mDevice;    // ALSA device to use
-    int                 mPeriodSize;
-    int                 mPeriodCount;
-
-    FileMap*            mCurrentFile;
-};
-
-} // namespace android
-
-#endif // _BOOTANIMATION_AUDIOPLAYER_H
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index a8dc3f6..4098772 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -58,7 +58,7 @@
 #include <EGL/eglext.h>
 
 #include "BootAnimation.h"
-#include "AudioPlayer.h"
+#include "audioplay.h"
 
 namespace android {
 
@@ -72,6 +72,8 @@
 static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
 static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
 static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_accurate";
+// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
+static const long long ACCURATE_TIME_EPOCH = 946684800000;
 static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
 static const int ANIM_ENTRY_NAME_MAX = 256;
 
@@ -106,9 +108,7 @@
     // might be blocked on a condition variable that will never be updated.
     kill( getpid(), SIGKILL );
     requestExit();
-    if (mAudioPlayer != NULL) {
-        mAudioPlayer->requestExit();
-    }
+    audioplay::destroy();
 }
 
 status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
@@ -400,9 +400,6 @@
     int exitnow = atoi(value);
     if (exitnow) {
         requestExit();
-        if (mAudioPlayer != NULL) {
-            mAudioPlayer->requestExit();
-        }
     }
 }
 
@@ -524,16 +521,6 @@
     }
     char const* s = desString.string();
 
-    // Create and initialize an AudioPlayer if we have an audio_conf.txt file
-    String8 audioConf;
-    if (readFile(animation.zip, "audio_conf.txt", audioConf)) {
-        mAudioPlayer = new AudioPlayer;
-        if (!mAudioPlayer->init(audioConf.string())) {
-            ALOGE("mAudioPlayer.init failed");
-            mAudioPlayer = NULL;
-        }
-    }
-
     // Parse the description file
     for (;;) {
         const char* endl = strstr(s, "\n");
@@ -564,7 +551,7 @@
             part.pause = pause;
             part.path = path;
             part.clockPosY = clockPosY;
-            part.audioFile = NULL;
+            part.audioData = NULL;
             part.animation = NULL;
             if (!parseColor(color, part.backgroundColor)) {
                 ALOGE("> invalid color '#%s'", color);
@@ -580,7 +567,7 @@
             part.playUntilComplete = false;
             part.count = 1;
             part.pause = 0;
-            part.audioFile = NULL;
+            part.audioData = NULL;
             part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
             if (part.animation != NULL)
                 animation.parts.add(part);
@@ -601,6 +588,7 @@
         return false;
     }
 
+    bool hasAudio = false;
     ZipEntryRO entry;
     char name[ANIM_ENTRY_NAME_MAX];
     while ((entry = zip->nextEntry(cookie)) != NULL) {
@@ -624,8 +612,10 @@
                             if (map) {
                                 Animation::Part& part(animation.parts.editItemAt(j));
                                 if (leaf == "audio.wav") {
+                                    hasAudio = true;
                                     // a part may have at most one audio file
-                                    part.audioFile = map;
+                                    part.audioData = (uint8_t *)map->getDataPtr();
+                                    part.audioLength = map->getDataLength();
                                 } else if (leaf == "trim.txt") {
                                     part.trimData.setTo((char const*)map->getDataPtr(),
                                                         map->getDataLength());
@@ -640,6 +630,8 @@
                                     part.frames.add(frame);
                                 }
                             }
+                        } else {
+                            ALOGE("bootanimation.zip is compressed; must be only stored");
                         }
                     }
                 }
@@ -673,6 +665,12 @@
         }
     }
 
+    // Create and initialize audioplay if there is a wav file in any of the animations.
+    if (hasAudio) {
+        ALOGD("found audio.wav, creating playback engine");
+        audioplay::create();
+    }
+
     zip->endIteration(cookie);
 
     return true;
@@ -777,8 +775,9 @@
                 break;
 
             // only play audio file the first time we animate the part
-            if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
-                mAudioPlayer->playFile(part.audioFile);
+            if (r == 0 && part.audioData) {
+                ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength);
+                audioplay::playClip(part.audioData, part.audioLength);
             }
 
             glClearColor(
@@ -865,6 +864,11 @@
             }
         }
     }
+
+    // we've finally played everything we're going to play
+    audioplay::setPlaying(false);
+    audioplay::destroy();
+
     return true;
 }
 
@@ -930,8 +934,9 @@
         clock_gettime(CLOCK_REALTIME, &now);
         // Match the Java timestamp format
         long long rtcNow = (now.tv_sec * 1000LL) + (now.tv_nsec / 1000000LL);
-        if (lastChangedTime > rtcNow - MAX_TIME_IN_PAST
-            && lastChangedTime < rtcNow + MAX_TIME_IN_FUTURE) {
+        if (ACCURATE_TIME_EPOCH < rtcNow
+            && lastChangedTime > (rtcNow - MAX_TIME_IN_PAST)
+            && lastChangedTime < (rtcNow + MAX_TIME_IN_FUTURE)) {
             mTimeIsAccurate = true;
         }
       }
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 85724f4..a53216e 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -30,7 +30,6 @@
 
 namespace android {
 
-class AudioPlayer;
 class Surface;
 class SurfaceComposerClient;
 class SurfaceControl;
@@ -98,7 +97,8 @@
             SortedVector<Frame> frames;
             bool playUntilComplete;
             float backgroundColor[3];
-            FileMap* audioFile;
+            uint8_t* audioData;
+            int audioLength;
             Animation* animation;
         };
         int fps;
@@ -124,7 +124,6 @@
     void checkExit();
 
     sp<SurfaceComposerClient>       mSession;
-    sp<AudioPlayer>                 mAudioPlayer;
     AssetManager mAssets;
     Texture     mAndroid[2];
     Texture     mClock;
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index e4c52f7..9ea6fea 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -67,34 +67,8 @@
 
 ## audio.wav
 
-Each part may optionally play a `wav` sample when it starts. To enable this for an animation,
-you must also include a `audio_conf.txt` file in the ZIP archive. Its format is as follows:
-
-    card=<ALSA card number>
-    device=<ALSA device number>
-    period_size=<period size>
-    period_count=<period count>
-
-This header is followed by zero or more mixer settings, each with the format:
-
-    mixer "<name>" = <value list>
-
-Here's an example `audio_conf.txt` from Shamu:
-
-    card=0
-    device=15
-    period_size=1024
-    period_count=4
-
-    mixer "QUAT_MI2S_RX Audio Mixer MultiMedia5" = 1
-    mixer "Playback Channel Map" = 0 220 157 195 0 0 0 0
-    mixer "QUAT_MI2S_RX Channels" = Two
-    mixer "BOOST_STUB Right Mixer right" = 1
-    mixer "BOOST_STUB Left Mixer left" = 1
-    mixer "Compress Playback 9 Volume" = 80 80
-
-You will probably need to get these mixer names and values out of `audio_platform_info.xml`
-and `mixer_paths.xml` for your device.
+Each part may optionally play a `wav` sample when it starts. To enable this, add a file
+with the name `audio.wav` in the part directory.
 
 ## exiting
 
diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp
new file mode 100644
index 0000000..e20ef0c
--- /dev/null
+++ b/cmds/bootanimation/audioplay.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// cribbed from samples/native-audio
+
+#include "audioplay.h"
+
+#define CHATTY ALOGD
+
+#include <assert.h>
+#include <string.h>
+
+#include <utils/Log.h>
+
+// for native audio
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+namespace audioplay {
+namespace {
+
+// engine interfaces
+static SLObjectItf engineObject = NULL;
+static SLEngineItf engineEngine;
+
+// output mix interfaces
+static SLObjectItf outputMixObject = NULL;
+
+// buffer queue player interfaces
+static SLObjectItf bqPlayerObject = NULL;
+static SLPlayItf bqPlayerPlay;
+static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
+static SLMuteSoloItf bqPlayerMuteSolo;
+static SLVolumeItf bqPlayerVolume;
+
+// pointer and size of the next player buffer to enqueue, and number of remaining buffers
+static const uint8_t* nextBuffer;
+static unsigned nextSize;
+
+static const uint32_t ID_RIFF = 0x46464952;
+static const uint32_t ID_WAVE = 0x45564157;
+static const uint32_t ID_FMT  = 0x20746d66;
+static const uint32_t ID_DATA = 0x61746164;
+
+struct RiffWaveHeader {
+    uint32_t riff_id;
+    uint32_t riff_sz;
+    uint32_t wave_id;
+};
+
+struct ChunkHeader {
+    uint32_t id;
+    uint32_t sz;
+};
+
+struct ChunkFormat {
+    uint16_t audio_format;
+    uint16_t num_channels;
+    uint32_t sample_rate;
+    uint32_t byte_rate;
+    uint16_t block_align;
+    uint16_t bits_per_sample;
+};
+
+// this callback handler is called every time a buffer finishes playing
+void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
+    (void)bq;
+    (void)context;
+    assert(bq == bqPlayerBufferQueue);
+    assert(NULL == context);
+    audioplay::setPlaying(false);
+}
+
+bool hasPlayer() {
+    return (engineObject != NULL && bqPlayerObject != NULL);
+}
+
+// create the engine and output mix objects
+void createEngine() {
+    SLresult result;
+
+    // create engine
+    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // realize the engine
+    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // get the engine interface, which is needed in order to create other objects
+    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // create output mix, with environmental reverb specified as a non-required interface
+    const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
+    const SLboolean req[1] = {SL_BOOLEAN_FALSE};
+    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // realize the output mix
+    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+}
+
+// create buffer queue audio player
+void createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
+    SLresult result;
+
+    // configure audio source
+    SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
+
+    SLDataFormat_PCM format_pcm = {
+        SL_DATAFORMAT_PCM,
+        chunkFormat->num_channels,
+        chunkFormat->sample_rate * 1000,  // convert to milliHz
+        chunkFormat->bits_per_sample,
+        16,
+        SL_SPEAKER_FRONT_CENTER,
+        SL_BYTEORDER_LITTLEENDIAN
+    };
+    SLDataSource audioSrc = {&loc_bufq, &format_pcm};
+
+    // configure audio sink
+    SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
+    SLDataSink audioSnk = {&loc_outmix, NULL};
+
+    // create audio player
+    const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
+    const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
+            2, ids, req);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // realize the player
+    result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // get the play interface
+    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // get the buffer queue interface
+    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
+            &bqPlayerBufferQueue);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // register callback on the buffer queue
+    result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+#if 0   // mute/solo is not supported for sources that are known to be mono, as this is
+    // get the mute/solo interface
+    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+#endif
+
+    // get the volume interface
+    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
+    assert(SL_RESULT_SUCCESS == result);
+    (void)result;
+
+    // set the player's state to playing
+    audioplay::setPlaying(true);
+    CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
+}
+
+} // namespace
+
+void create() {
+    createEngine();
+}
+
+bool playClip(const uint8_t* buf, int size) {
+    // Parse the WAV header
+    nextBuffer = buf;
+    nextSize = size;
+    const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)buf;
+    if (nextSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
+        (wavHeader->wave_id != ID_WAVE)) {
+        ALOGE("Error: audio file is not a riff/wave file\n");
+        return false;
+    }
+    nextBuffer += sizeof(*wavHeader);
+    nextSize -= sizeof(*wavHeader);
+
+    const ChunkFormat* chunkFormat = nullptr;
+    while (true) {
+        const ChunkHeader* chunkHeader = (const ChunkHeader*)nextBuffer;
+        if (nextSize < sizeof(*chunkHeader)) {
+            ALOGE("EOF reading chunk headers");
+            return false;
+        }
+
+        nextBuffer += sizeof(*chunkHeader);
+        nextSize -= sizeof(*chunkHeader);
+
+        bool endLoop = false;
+        switch (chunkHeader->id) {
+            case ID_FMT:
+                chunkFormat = (const ChunkFormat*)nextBuffer;
+                nextBuffer += chunkHeader->sz;
+                nextSize -= chunkHeader->sz;
+                break;
+            case ID_DATA:
+                /* Stop looking for chunks */
+                endLoop = true;
+                break;
+            default:
+                /* Unknown chunk, skip bytes */
+                nextBuffer += chunkHeader->sz;
+                nextSize -= chunkHeader->sz;
+        }
+        if (endLoop) {
+            break;
+        }
+    }
+
+    if (!chunkFormat) {
+        ALOGE("format not found in WAV file");
+        return false;
+    }
+
+    // If this is the first clip, create the buffer based on this WAV's header.
+    // We assume all future clips with be in the same format.
+    if (bqPlayerBufferQueue == nullptr) {
+        createBufferQueueAudioPlayer(chunkFormat);
+    }
+
+    assert(bqPlayerBufferQueue != nullptr);
+    assert(buf != nullptr);
+
+    if (!hasPlayer()) {
+        ALOGD("cannot play clip %p without a player", buf);
+        return false;
+    }
+
+    CHATTY("playClip on player %p: buf=%p size=%d", bqPlayerBufferQueue, buf, size);
+
+    if (nextSize > 0) {
+        // here we only enqueue one buffer because it is a long clip,
+        // but for streaming playback we would typically enqueue at least 2 buffers to start
+        SLresult result;
+        result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
+        if (SL_RESULT_SUCCESS != result) {
+            return false;
+        }
+        audioplay::setPlaying(true);
+    }
+
+    return true;
+}
+
+// set the playing state for the buffer queue audio player
+void setPlaying(bool isPlaying) {
+    if (!hasPlayer()) return;
+
+    SLresult result;
+
+    if (NULL != bqPlayerPlay) {
+        // set the player's state
+        result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
+            isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
+        assert(SL_RESULT_SUCCESS == result);
+        (void)result;
+    }
+
+}
+
+void destroy() {
+    // destroy buffer queue audio player object, and invalidate all associated interfaces
+    if (bqPlayerObject != NULL) {
+        CHATTY("destroying audio player");
+        (*bqPlayerObject)->Destroy(bqPlayerObject);
+        bqPlayerObject = NULL;
+        bqPlayerPlay = NULL;
+        bqPlayerBufferQueue = NULL;
+        bqPlayerMuteSolo = NULL;
+        bqPlayerVolume = NULL;
+    }
+
+    // destroy output mix object, and invalidate all associated interfaces
+    if (outputMixObject != NULL) {
+        (*outputMixObject)->Destroy(outputMixObject);
+        outputMixObject = NULL;
+    }
+
+    // destroy engine object, and invalidate all associated interfaces
+    if (engineObject != NULL) {
+        CHATTY("destroying audio engine");
+        (*engineObject)->Destroy(engineObject);
+        engineObject = NULL;
+        engineEngine = NULL;
+    }
+}
+
+}  // namespace audioplay
diff --git a/cmds/bootanimation/audioplay.h b/cmds/bootanimation/audioplay.h
new file mode 100644
index 0000000..bdc0a1c
--- /dev/null
+++ b/cmds/bootanimation/audioplay.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef AUDIOPLAY_H_
+#define AUDIOPLAY_H_
+
+#include <string.h>
+
+namespace audioplay {
+
+void create();
+
+// Play a WAV pointed to by buf.  All clips are assumed to be in the same format.
+// playClip should not be called while a clip is still playing.
+bool playClip(const uint8_t* buf, int size);
+void setPlaying(bool isPlaying);
+void destroy();
+
+}
+
+#endif // AUDIOPLAY_H_
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6dd14fd..fc3596e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -165,9 +165,10 @@
             int userId);
 
     /**
-     * Create an {@link IIntentSender} to start an activity, as if {@code packageName} on
-     * user {@code userId} created it.
+     * Start an activity {@code intent} as if {@code packageName} on user {@code userId} did it.
+     *
+     * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
-    public abstract IIntentSender getActivityIntentSenderAsPackage(String packageName,
-            int userId, int requestCode, Intent intent, int flags, Bundle bOptions);
+    public abstract int startActivityAsPackage(String packageName,
+            int userId, Intent intent, Bundle bOptions);
 }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 79f2a1e..53da4e3 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -266,7 +266,7 @@
     }
 
     static class Globals extends IWallpaperManagerCallback.Stub {
-        private IWallpaperManager mService;
+        private final IWallpaperManager mService;
         private Bitmap mCachedWallpaper;
         private int mCachedWallpaperUserId;
         private Bitmap mDefaultWallpaper;
@@ -293,16 +293,16 @@
 
         public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
                 @SetWallpaperFlags int which, int userId) {
-            synchronized (this) {
-                if (mService != null) {
-                    try {
-                        if (!mService.isWallpaperSupported(context.getOpPackageName())) {
-                            return null;
-                        }
-                    } catch (RemoteException e) {
-                        throw e.rethrowFromSystemServer();
+            if (mService != null) {
+                try {
+                    if (!mService.isWallpaperSupported(context.getOpPackageName())) {
+                        return null;
                     }
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
                 }
+            }
+            synchronized (this) {
                 if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) {
                     return mCachedWallpaper;
                 }
@@ -317,17 +317,21 @@
                 if (mCachedWallpaper != null) {
                     return mCachedWallpaper;
                 }
-                if (returnDefault) {
-                    if (mDefaultWallpaper == null) {
-                        mDefaultWallpaper = getDefaultWallpaperLocked(context, which);
-                    }
-                    return mDefaultWallpaper;
-                }
-                return null;
             }
+            if (returnDefault) {
+                Bitmap defaultWallpaper = mDefaultWallpaper;
+                if (defaultWallpaper == null) {
+                    defaultWallpaper = getDefaultWallpaper(context, which);
+                    synchronized (this) {
+                        mDefaultWallpaper = defaultWallpaper;
+                    }
+                }
+                return defaultWallpaper;
+            }
+            return null;
         }
 
-        public void forgetLoadedWallpaper() {
+        void forgetLoadedWallpaper() {
             synchronized (this) {
                 mCachedWallpaper = null;
                 mCachedWallpaperUserId = 0;
@@ -362,7 +366,7 @@
             return null;
         }
 
-        private Bitmap getDefaultWallpaperLocked(Context context, @SetWallpaperFlags int which) {
+        private Bitmap getDefaultWallpaper(Context context, @SetWallpaperFlags int which) {
             InputStream is = openDefaultWallpaper(context, which);
             if (is != null) {
                 try {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7b46ccd..dc33671 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2510,6 +2510,8 @@
     /**
      * Result code for {@link #setStorageEncryption} and {@link #getStorageEncryptionStatus}:
      * indicating that encryption is active.
+     * <p>
+     * Also see {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}.
      */
     public static final int ENCRYPTION_STATUS_ACTIVE = 3;
 
@@ -2522,7 +2524,11 @@
 
     /**
      * Result code for {@link #getStorageEncryptionStatus}:
-     * indicating that encryption is active and the encryption key is tied to the user.
+     * indicating that encryption is active and the encryption key is tied to the user or profile.
+     * <p>
+     * This value is only returned to apps targeting API level 24 and above. For apps targeting
+     * earlier API levels, {@link #ENCRYPTION_STATUS_ACTIVE} is returned, even if the
+     * encryption key is specific to the user or profile.
      */
     public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5;
 
@@ -2649,7 +2655,7 @@
     /**
      * Called by an application that is administering the device to
      * determine the current encryption status of the device.
-     *
+     * <p>
      * Depending on the returned status code, the caller may proceed in different
      * ways.  If the result is {@link #ENCRYPTION_STATUS_UNSUPPORTED}, the
      * storage system does not support encryption.  If the
@@ -2657,13 +2663,14 @@
      * #ACTION_START_ENCRYPTION} to begin the process of encrypting or decrypting the
      * storage.  If the result is {@link #ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY}, the
      * storage system has enabled encryption but no password is set so further action
-     * may be required.  If the result is {@link #ENCRYPTION_STATUS_ACTIVATING} or
-     * {@link #ENCRYPTION_STATUS_ACTIVE}, no further action is required.
+     * may be required.  If the result is {@link #ENCRYPTION_STATUS_ACTIVATING},
+     * {@link #ENCRYPTION_STATUS_ACTIVE} or {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER},
+     * no further action is required.
      *
      * @return current status of encryption. The value will be one of
      * {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE},
      * {@link #ENCRYPTION_STATUS_ACTIVATING}, {@link #ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY},
-     * or {@link #ENCRYPTION_STATUS_ACTIVE}.
+     * {@link #ENCRYPTION_STATUS_ACTIVE}, or {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}.
      */
     public int getStorageEncryptionStatus() {
         throwIfParentInstance("getStorageEncryptionStatus");
@@ -6429,6 +6436,30 @@
         }
     }
 
+    /**
+     * @hide
+     * Writes that the provisioning configuration has been applied.
+     */
+    public void setDeviceProvisioningConfigApplied() {
+        try {
+            mService.setDeviceProvisioningConfigApplied();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * @return whether the provisioning configuration has been applied.
+     */
+    public boolean isDeviceProvisioningConfigApplied() {
+        try {
+            return mService.isDeviceProvisioningConfigApplied();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
     private void throwIfParentInstance(String functionName) {
         if (mParentInstance) {
             throw new SecurityException(functionName + " cannot be called on the parent instance");
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 4b793d1..1036f04 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -303,4 +303,6 @@
     void uninstallPackageWithActiveAdmins(String packageName);
 
     boolean isDeviceProvisioned();
+    boolean isDeviceProvisioningConfigApplied();
+    void setDeviceProvisioningConfigApplied();
 }
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index c19e638..430c7e7 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -51,7 +51,7 @@
             in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
     void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
             in UserHandle user);
-    void startShortcut(String callingPackage, String packageName, String id,
+    boolean startShortcut(String callingPackage, String packageName, String id,
             in Rect sourceBounds, in Bundle startActivityOptions, int userId);
 
     int getShortcutIconResId(String callingPackage, String packageName, String id,
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 2133222..29b2230 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -688,6 +689,9 @@
      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
      * @param startActivityOptions Options to pass to startActivity.
      * @param user The UserHandle of the profile.
+     *
+     * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
+     * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
      */
     public void startShortcut(@NonNull String packageName, @NonNull String shortcutId,
             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
@@ -705,6 +709,9 @@
      * @param shortcut The target shortcut.
      * @param sourceBounds The Rect containing the source bounds of the clicked icon.
      * @param startActivityOptions Options to pass to startActivity.
+     *
+     * @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
+     * the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
      */
     public void startShortcut(@NonNull ShortcutInfo shortcut,
             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
@@ -717,8 +724,12 @@
             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
             int userId) {
         try {
-            mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
+            final boolean success =
+                    mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
                     sourceBounds, startActivityOptions, userId);
+            if (!success) {
+                throw new ActivityNotFoundException("Shortcut could not be started");
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index de52f73..3f8bad1 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -73,11 +73,4 @@
      * any locks in this method.
      */
     public abstract void onSystemLocaleChangedNoLock();
-
-    /**
-     * Called by PM before sending package broadcasts to other components.  PM doesn't hold the PM
-     * lock, but do not take any locks in here anyway, and don't do any heavy tasks, as doing so
-     * would slow down all the package broadcasts.
-     */
-    public abstract void onPackageBroadcast(Intent intent);
 }
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 384ab1c..6e74f14 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -103,7 +103,7 @@
     private boolean isIPv6ULA() {
         if (address != null && address instanceof Inet6Address) {
             byte[] bytes = address.getAddress();
-            return ((bytes[0] & (byte)0xfc) == (byte)0xfc);
+            return ((bytes[0] & (byte)0xfe) == (byte)0xfc);
         }
         return false;
     }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a44a9ee..feb8b2b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -603,6 +603,17 @@
     public static final String DISALLOW_SET_USER_ICON = "no_set_user_icon";
 
     /**
+     * Specifies if a user is not allowed to enable the oem unlock setting. The default value is
+     * <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     * @hide
+     */
+    public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
+
+    /**
      * Allows apps in the parent profile to handle web links from the managed profile.
      *
      * This user restriction has an effect only in a managed profile.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 56610ed..5c2778d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9114,15 +9114,6 @@
         public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot";
 
         /**
-         * Whether toggling OEM unlock is disallowed. If disallowed, it is not possible to enable or
-         * disable OEM unlock.
-         * <p>
-         * Type: int (0: allow OEM unlock setting. 1: disallow OEM unlock)
-         * @hide
-         */
-        public static final String OEM_UNLOCK_DISALLOWED = "oem_unlock_disallowed";
-
-        /**
          * The maximum allowed notification enqueue rate in Hertz.
          *
          * Should be a float, and includes both posts and updates.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 6911b01..69960b0 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -20,6 +20,8 @@
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Parcel;
@@ -118,6 +120,7 @@
     private static final String RULE_ATT_ZEN = "zen";
     private static final String RULE_ATT_CONDITION_ID = "conditionId";
     private static final String RULE_ATT_CREATION_TIME = "creationTime";
+    private static final String RULE_ATT_ENABLER = "enabler";
 
     public boolean allowCalls = DEFAULT_ALLOW_CALLS;
     public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
@@ -502,6 +505,7 @@
         rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
         rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
         rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
+        rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
         rt.condition = readConditionXml(parser);
         return rt;
     }
@@ -520,6 +524,9 @@
             out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
         }
         out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
+        if (rule.enabler != null) {
+            out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
+        }
         if (rule.condition != null) {
             writeConditionXml(rule.condition, out);
         }
@@ -989,6 +996,25 @@
         return UUID.randomUUID().toString().replace("-", "");
     }
 
+    private static String getOwnerCaption(Context context, String owner) {
+        final PackageManager pm = context.getPackageManager();
+        try {
+            final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
+            if (info != null) {
+                final CharSequence seq = info.loadLabel(pm);
+                if (seq != null) {
+                    final String str = seq.toString().trim();
+                    if (str.length() > 0) {
+                        return str;
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            Slog.w(TAG, "Error loading owner caption", e);
+        }
+        return "";
+    }
+
     public static String getConditionSummary(Context context, ZenModeConfig config,
             int userHandle, boolean shortVersion) {
         return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
@@ -997,23 +1023,28 @@
     private static String getConditionLine(Context context, ZenModeConfig config,
             int userHandle, boolean useLine1, boolean shortVersion) {
         if (config == null) return "";
+        String summary = "";
         if (config.manualRule != null) {
             final Uri id = config.manualRule.conditionId;
-            if (id == null) {
-                return context.getString(com.android.internal.R.string.zen_mode_forever);
+            if (config.manualRule.enabler != null) {
+                summary = getOwnerCaption(context, config.manualRule.enabler);
+            } else {
+                if (id == null) {
+                    summary = context.getString(com.android.internal.R.string.zen_mode_forever);
+                } else {
+                    final long time = tryParseCountdownConditionId(id);
+                    Condition c = config.manualRule.condition;
+                    if (time > 0) {
+                        final long now = System.currentTimeMillis();
+                        final long span = time - now;
+                        c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
+                                userHandle, shortVersion);
+                    }
+                    final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
+                    summary = TextUtils.isEmpty(rt) ? "" : rt;
+                }
             }
-            final long time = tryParseCountdownConditionId(id);
-            Condition c = config.manualRule.condition;
-            if (time > 0) {
-                final long now = System.currentTimeMillis();
-                final long span = time - now;
-                c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
-                        userHandle, shortVersion);
-            }
-            final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
-            return TextUtils.isEmpty(rt) ? "" : rt;
         }
-        String summary = "";
         for (ZenRule automaticRule : config.automaticRules.values()) {
             if (automaticRule.isAutomaticActive()) {
                 if (summary.isEmpty()) {
@@ -1023,6 +1054,7 @@
                             .getString(R.string.zen_mode_rule_name_combination, summary,
                                     automaticRule.name);
                 }
+
             }
         }
         return summary;
@@ -1038,6 +1070,7 @@
         public ComponentName component;  // optional
         public String id;                // required for automatic (unique)
         public long creationTime;        // required for automatic
+        public String enabler;          // package name, only used for manual rules.
 
         public ZenRule() { }
 
@@ -1055,6 +1088,9 @@
                 id = source.readString();
             }
             creationTime = source.readLong();
+            if (source.readInt() == 1) {
+                enabler = source.readString();
+            }
         }
 
         @Override
@@ -1083,6 +1119,12 @@
                 dest.writeInt(0);
             }
             dest.writeLong(creationTime);
+            if (enabler != null) {
+                dest.writeInt(1);
+                dest.writeString(enabler);
+            } else {
+                dest.writeInt(0);
+            }
         }
 
         @Override
@@ -1097,6 +1139,7 @@
                     .append(",component=").append(component)
                     .append(",id=").append(id)
                     .append(",creationTime=").append(creationTime)
+                    .append(",enabler=").append(enabler)
                     .append(']').toString();
         }
 
@@ -1143,6 +1186,9 @@
             if (creationTime != to.creationTime) {
                 d.addLine(item, "creationTime", creationTime, to.creationTime);
             }
+            if (enabler != to.enabler) {
+                d.addLine(item, "enabler", enabler, to.enabler);
+            }
         }
 
         @Override
@@ -1158,13 +1204,14 @@
                     && Objects.equals(other.condition, condition)
                     && Objects.equals(other.component, component)
                     && Objects.equals(other.id, id)
-                    && other.creationTime == creationTime;
+                    && other.creationTime == creationTime
+                    && Objects.equals(other.enabler, enabler);
         }
 
         @Override
         public int hashCode() {
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
-                    component, id, creationTime);
+                    component, id, creationTime, enabler);
         }
 
         public boolean isAutomaticActive() {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e3ff54d..9bc0bb4 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -306,6 +306,11 @@
     boolean isRotationFrozen();
 
     /**
+     * Screenshot the current wallpaper layer, including the whole screen.
+     */
+    Bitmap screenshotWallpaper();
+
+    /**
      * Used only for assist -- request a screenshot of the current application.
      */
     boolean requestAssistScreenshot(IAssistScreenshotReceiver receiver);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d4ac300..a64827a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3744,7 +3744,8 @@
     /**
      * Flag indicating that a drag can cross window boundaries.  When
      * {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object, int)} is called
-     * with this flag set, all visible applications will be able to participate
+     * with this flag set, all visible applications with targetSdkVersion >=
+     * {@link android.os.Build.VERSION_CODES#N API 24} will be able to participate
      * in the drag operation and receive the dragged content.
      *
      * If this is the only flag set, then the drag recipient will only have access to text data
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 7d7b880..b65f933 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2316,22 +2316,24 @@
     }
 
     /**
-     * Get a view and have it show the data associated with the specified
-     * position. This is called when we have already discovered that the view is
-     * not available for reuse in the recycle bin. The only choices left are
+     * Gets a view and have it show the data associated with the specified
+     * position. This is called when we have already discovered that the view
+     * is not available for reuse in the recycle bin. The only choices left are
      * converting an old view or making a new one.
      *
-     * @param position The position to display
-     * @param isScrap Array of at least 1 boolean, the first entry will become true if
-     *                the returned view was taken from the "temporary detached" scrap heap, false if
-     *                otherwise.
+     * @param position the position to display
+     * @param outMetadata an array of at least 1 boolean where the first entry
+     *                    will be set {@code true} if the view is currently
+     *                    attached to the window, {@code false} otherwise (e.g.
+     *                    newly-inflated or remained scrap for multiple layout
+     *                    passes)
      *
      * @return A view displaying the data associated with the specified position
      */
-    View obtainView(int position, boolean[] isScrap) {
+    View obtainView(int position, boolean[] outMetadata) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");
 
-        isScrap[0] = false;
+        outMetadata[0] = false;
 
         // Check whether we have a transient state view. Attempt to re-bind the
         // data and discard the view if we fail.
@@ -2350,7 +2352,7 @@
                 }
             }
 
-            isScrap[0] = true;
+            outMetadata[0] = true;
 
             // Finish the temporary detach started in addScrapView().
             transientView.dispatchFinishTemporaryDetach();
@@ -2363,19 +2365,11 @@
             if (child != scrapView) {
                 // Failed to re-bind the data, return scrap to the heap.
                 mRecycler.addScrapView(scrapView, position);
-            } else {
-                if (child.isTemporarilyDetached()) {
-                    isScrap[0] = true;
+            } else if (child.isTemporarilyDetached()) {
+                outMetadata[0] = true;
 
-                    // Finish the temporary detach started in addScrapView().
-                    child.dispatchFinishTemporaryDetach();
-                } else {
-                    // we set isScrap to "true" only if the view is temporarily detached.
-                    // if the view is fully detached, it is as good as a view created by the
-                    // adapter
-                    isScrap[0] = false;
-                }
-
+                // Finish the temporary detach started in addScrapView().
+                child.dispatchFinishTemporaryDetach();
             }
         }
 
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 6d7313d..b95aa52 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1407,72 +1407,73 @@
 
 
     /**
-     * Obtain the view and add it to our list of children. The view can be made
-     * fresh, converted from an unused view, or used as is if it was in the
-     * recycle bin.
+     * Obtains the view and adds it to our list of children. The view can be
+     * made fresh, converted from an unused view, or used as is if it was in
+     * the recycle bin.
      *
-     * @param position Logical position in the list
-     * @param y Top or bottom edge of the view to add
-     * @param flow if true, align top edge to y. If false, align bottom edge to
-     *        y.
-     * @param childrenLeft Left edge where children should be positioned
-     * @param selected Is this position selected?
-     * @param where to add new item in the list
+     * @param position logical position in the list
+     * @param y top or bottom edge of the view to add
+     * @param flow {@code true} to align top edge to y, {@code false} to align
+     *             bottom edge to y
+     * @param childrenLeft left edge where children should be positioned
+     * @param selected {@code true} if the position is selected, {@code false}
+     *                 otherwise
+     * @param where position at which to add new item in the list
      * @return View that was added
      */
     private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
             boolean selected, int where) {
-        View child;
-
         if (!mDataChanged) {
             // Try to use an existing view for this position
-            child = mRecycler.getActiveView(position);
-            if (child != null) {
+            final View activeView = mRecycler.getActiveView(position);
+            if (activeView != null) {
                 // Found it -- we're using an existing child
                 // This just needs to be positioned
-                setupChild(child, position, y, flow, childrenLeft, selected, true, where);
-                return child;
+                setupChild(activeView, position, y, flow, childrenLeft, selected, true, where);
+                return activeView;
             }
         }
 
         // Make a new view for this position, or convert an unused view if
-        // possible
-        child = obtainView(position, mIsScrap);
+        // possible.
+        final View child = obtainView(position, mIsScrap);
 
-        // This needs to be positioned and measured
+        // This needs to be positioned and measured.
         setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0], where);
 
         return child;
     }
 
     /**
-     * Add a view as a child and make sure it is measured (if necessary) and
+     * Adds a view as a child and make sure it is measured (if necessary) and
      * positioned properly.
      *
-     * @param child The view to add
-     * @param position The position of the view
-     * @param y The y position relative to which this view will be positioned
-     * @param flow if true, align top edge to y. If false, align bottom edge
-     *        to y.
-     * @param childrenLeft Left edge where children should be positioned
-     * @param selected Is this position selected?
-     * @param recycled Has this view been pulled from the recycle bin? If so it
-     *        does not need to be remeasured.
-     * @param where Where to add the item in the list
+     * @param child the view to add
+     * @param position the position of this child
+     * @param y the y position relative to which this view will be positioned
+     * @param flowDown {@code true} to align top edge to y, {@code false} to
+     *                 align bottom edge to y
+     * @param childrenLeft left edge where children should be positioned
+     * @param selected {@code true} if the position is selected, {@code false}
+     *                 otherwise
+     * @param isAttachedToWindow {@code true} if the view is already attached
+     *                           to the window, e.g. whether it was reused, or
+     *                           {@code false} otherwise
+     * @param where position at which to add new item in the list
      *
      */
-    private void setupChild(View child, int position, int y, boolean flow, int childrenLeft,
-            boolean selected, boolean recycled, int where) {
+    private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,
+            boolean selected, boolean isAttachedToWindow, int where) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "setupGridItem");
 
         boolean isSelected = selected && shouldShowSelector();
         final boolean updateChildSelected = isSelected != child.isSelected();
         final int mode = mTouchMode;
-        final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&
-                mMotionPosition == position;
+        final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL
+                && mMotionPosition == position;
         final boolean updateChildPressed = isPressed != child.isPressed();
-        
-        boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();
+        final boolean needToMeasure = !isAttachedToWindow || updateChildSelected
+                || child.isLayoutRequested();
 
         // Respect layout params that are already in the view. Otherwise make
         // some up...
@@ -1483,13 +1484,9 @@
         p.viewType = mAdapter.getItemViewType(position);
         p.isEnabled = mAdapter.isEnabled(position);
 
-        if (recycled && !p.forceAdd) {
-            attachViewToParent(child, where, p);
-        } else {
-            p.forceAdd = false;
-            addViewInLayout(child, where, p, true);
-        }
-
+        // Set up view state before attaching the view, since we may need to
+        // rely on the jumpDrawablesToCurrentState() call that occurs as part
+        // of view attachment.
         if (updateChildSelected) {
             child.setSelected(isSelected);
             if (isSelected) {
@@ -1510,6 +1507,21 @@
             }
         }
 
+        if (isAttachedToWindow && !p.forceAdd) {
+            attachViewToParent(child, where, p);
+
+            // If the view isn't attached, or if it's attached but for a different
+            // position, then jump the drawables.
+            if (!isAttachedToWindow
+                    || (((AbsListView.LayoutParams) child.getLayoutParams()).scrappedFromPosition)
+                            != position) {
+                child.jumpDrawablesToCurrentState();
+            }
+        } else {
+            p.forceAdd = false;
+            addViewInLayout(child, where, p, true);
+        }
+
         if (needToMeasure) {
             int childHeightSpec = ViewGroup.getChildMeasureSpec(
                     MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
@@ -1525,7 +1537,7 @@
         final int h = child.getMeasuredHeight();
 
         int childLeft;
-        final int childTop = flow ? y : y - h;
+        final int childTop = flowDown ? y : y - h;
 
         final int layoutDirection = getLayoutDirection();
         final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
@@ -1553,15 +1565,10 @@
             child.offsetTopAndBottom(childTop - child.getTop());
         }
 
-        if (mCachingStarted) {
+        if (mCachingStarted && !child.isDrawingCacheEnabled()) {
             child.setDrawingCacheEnabled(true);
         }
 
-        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
-                != position) {
-            child.jumpDrawablesToCurrentState();
-        }
-
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 0e04e300..b8b7c55 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1939,72 +1939,73 @@
     }
 
     /**
-     * Obtain the view and add it to our list of children. The view can be made
-     * fresh, converted from an unused view, or used as is if it was in the
-     * recycle bin.
+     * Obtains the view and adds it to our list of children. The view can be
+     * made fresh, converted from an unused view, or used as is if it was in
+     * the recycle bin.
      *
-     * @param position Logical position in the list
-     * @param y Top or bottom edge of the view to add
-     * @param flow If flow is true, align top edge to y. If false, align bottom
-     *        edge to y.
-     * @param childrenLeft Left edge where children should be positioned
-     * @param selected Is this position selected?
-     * @return View that was added
+     * @param position logical position in the list
+     * @param y top or bottom edge of the view to add
+     * @param flow {@code true} to align top edge to y, {@code false} to align
+     *             bottom edge to y
+     * @param childrenLeft left edge where children should be positioned
+     * @param selected {@code true} if the position is selected, {@code false}
+     *                 otherwise
+     * @return the view that was added
      */
     private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
             boolean selected) {
-        View child;
-
-
         if (!mDataChanged) {
-            // Try to use an existing view for this position
-            child = mRecycler.getActiveView(position);
-            if (child != null) {
-                // Found it -- we're using an existing child
-                // This just needs to be positioned
-                setupChild(child, position, y, flow, childrenLeft, selected, true);
-
-                return child;
+            // Try to use an existing view for this position.
+            final View activeView = mRecycler.getActiveView(position);
+            if (activeView != null) {
+                // Found it. We're reusing an existing child, so it just needs
+                // to be positioned like a scrap view.
+                setupChild(activeView, position, y, flow, childrenLeft, selected, true);
+                return activeView;
             }
         }
 
-        // Make a new view for this position, or convert an unused view if possible
-        child = obtainView(position, mIsScrap);
+        // Make a new view for this position, or convert an unused view if
+        // possible.
+        final View child = obtainView(position, mIsScrap);
 
-        // This needs to be positioned and measured
+        // This needs to be positioned and measured.
         setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
 
         return child;
     }
 
     /**
-     * Add a view as a child and make sure it is measured (if necessary) and
+     * Adds a view as a child and make sure it is measured (if necessary) and
      * positioned properly.
      *
-     * @param child The view to add
-     * @param position The position of this child
-     * @param y The y position relative to which this view will be positioned
-     * @param flowDown If true, align top edge to y. If false, align bottom
-     *        edge to y.
-     * @param childrenLeft Left edge where children should be positioned
-     * @param selected Is this position selected?
-     * @param recycled Has this view been pulled from the recycle bin? If so it
-     *        does not need to be remeasured.
+     * @param child the view to add
+     * @param position the position of this child
+     * @param y the y position relative to which this view will be positioned
+     * @param flowDown {@code true} to align top edge to y, {@code false} to
+     *                 align bottom edge to y
+     * @param childrenLeft left edge where children should be positioned
+     * @param selected {@code true} if the position is selected, {@code false}
+     *                 otherwise
+     * @param isAttachedToWindow {@code true} if the view is already attached
+     *                           to the window, e.g. whether it was reused, or
+     *                           {@code false} otherwise
      */
     private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,
-            boolean selected, boolean recycled) {
+            boolean selected, boolean isAttachedToWindow) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "setupListItem");
 
         final boolean isSelected = selected && shouldShowSelector();
         final boolean updateChildSelected = isSelected != child.isSelected();
         final int mode = mTouchMode;
-        final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&
-                mMotionPosition == position;
+        final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL
+                && mMotionPosition == position;
         final boolean updateChildPressed = isPressed != child.isPressed();
-        final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();
+        final boolean needToMeasure = !isAttachedToWindow || updateChildSelected
+                || child.isLayoutRequested();
 
-        // Respect layout params that are already in the view. Otherwise make some up...
-        // noinspection unchecked
+        // Respect layout params that are already in the view. Otherwise make
+        // some up...
         AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
         if (p == null) {
             p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
@@ -2012,17 +2013,9 @@
         p.viewType = mAdapter.getItemViewType(position);
         p.isEnabled = mAdapter.isEnabled(position);
 
-        if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter
-                && p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
-            attachViewToParent(child, flowDown ? -1 : 0, p);
-        } else {
-            p.forceAdd = false;
-            if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
-                p.recycledHeaderFooter = true;
-            }
-            addViewInLayout(child, flowDown ? -1 : 0, p, true);
-        }
-
+        // Set up view state before attaching the view, since we may need to
+        // rely on the jumpDrawablesToCurrentState() call that occurs as part
+        // of view attachment.
         if (updateChildSelected) {
             child.setSelected(isSelected);
         }
@@ -2040,6 +2033,25 @@
             }
         }
 
+        if ((isAttachedToWindow && !p.forceAdd) || (p.recycledHeaderFooter
+                && p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
+            attachViewToParent(child, flowDown ? -1 : 0, p);
+
+            // If the view was previously attached for a different position,
+            // then manually jump the drawables.
+            if (isAttachedToWindow
+                    && (((AbsListView.LayoutParams) child.getLayoutParams()).scrappedFromPosition)
+                            != position) {
+                child.jumpDrawablesToCurrentState();
+            }
+        } else {
+            p.forceAdd = false;
+            if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
+                p.recycledHeaderFooter = true;
+            }
+            addViewInLayout(child, flowDown ? -1 : 0, p, true);
+        }
+
         if (needToMeasure) {
             final int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
                     mListPadding.left + mListPadding.right, p.width);
@@ -2073,11 +2085,6 @@
             child.setDrawingCacheEnabled(true);
         }
 
-        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
-                != position) {
-            child.jumpDrawablesToCurrentState();
-        }
-
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index eff116b..0059d4d 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -149,7 +149,7 @@
     private DevicePolicyManager mDevicePolicyManager;
     private ILockSettings mLockSettingsService;
     private UserManager mUserManager;
-    private final Handler mHandler = new Handler();
+    private final Handler mHandler;
 
     /**
      * Use {@link TrustManager#isTrustUsuallyManaged(int)}.
@@ -231,6 +231,9 @@
     public LockPatternUtils(Context context) {
         mContext = context;
         mContentResolver = context.getContentResolver();
+
+        Looper looper = Looper.myLooper();
+        mHandler = looper != null ? new Handler(looper) : null;
     }
 
     private ILockSettings getLockSettings() {
@@ -1506,6 +1509,10 @@
         if (callback == null) {
             return null;
         } else {
+            if (mHandler == null) {
+                throw new IllegalStateException("Must construct LockPatternUtils on a looper thread"
+                        + " to use progress callbacks.");
+            }
             return new ICheckCredentialProgressCallback.Stub() {
 
                 @Override
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7d19537..e87bb81 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2527,4 +2527,9 @@
 
     <!-- Package name for the device provisioning package. -->
     <string name="config_deviceProvisioningPackage"></string>
+
+    <!-- User restrictions set when the first user is created.
+         Note: Also update appropriate overlay files. -->
+    <string-array translatable="false" name="config_defaultFirstUserRestrictions">
+    </string-array>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ff5f7d9..6b064c1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2668,4 +2668,7 @@
   <java-symbol type="integer" name="config_defaultNightDisplayAutoMode" />
   <java-symbol type="integer" name="config_defaultNightDisplayCustomStartTime" />
   <java-symbol type="integer" name="config_defaultNightDisplayCustomEndTime" />
+
+  <!-- Default first user restrictions -->
+  <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
 </resources>
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 11e06f1..d4732f8 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -801,8 +801,8 @@
   to: http://android-developers.blogspot.com/2016/03/first-preview-of-android-n-developer.html
 - from: /reference/org/apache/http/...
   to: /about/versions/marshmallow/android-6.0-changes.html#behavior-apache-http-client
-- from: /shareables/
-  to: https://commondatastorage.googleapis.com/androiddevelopers/shareables/
+- from: /shareables/...
+  to: https://commondatastorage.googleapis.com/androiddevelopers/shareables/...
 - from: /downloads/
   to: https://commondatastorage.googleapis.com/androiddevelopers/
 - from: /training/performance/battery/network/action-any-traffic.html
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 911e256..a9f1985 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -59,7 +59,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on June 6, 2016.
+<p style="clear:both"><em>Data collected during a 7-day period ending on July 11, 2016.
 <br/>Any versions with less than 0.1% distribution are not shown.</em>
 </p>
 
@@ -81,7 +81,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on June 6, 2016.
+<p style="clear:both"><em>Data collected during a 7-day period ending on July 11, 2016.
 
 <br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
 
@@ -101,7 +101,7 @@
 
 
 <img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0%7CGL%203.1&chf=bg%2Cs%2C00000000&chd=t%3A48.6%2C41.8%2C9.6&chco=c4df9b%2C6fad0c&cht=p&chs=400x250">
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0%7CGL%203.1&chf=bg%2Cs%2C00000000&chd=t%3A47.5%2C41.9%2C10.6&chco=c4df9b%2C6fad0c&cht=p&chs=400x250">
 
 <p>To declare which version of OpenGL ES your application requires, you should use the {@code
 android:glEsVersion} attribute of the <a
@@ -119,21 +119,21 @@
 </tr>
 <tr>
 <td>2.0</td>
-<td>48.6%</td>
+<td>47.5%</td>
 </tr>
 <tr>
 <td>3.0</td>
-<td>41.8%</td>
+<td>41.9%</td>
 </tr>
 <tr>
 <td>3.1</td>
-<td>9.6%</td>
+<td>10.6%</td>
 </tr>
 </table>
 
 
 
-<p style="clear:both"><em>Data collected during a 7-day period ending on June 6, 2016</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on July 11, 2016</em></p>
 
 
 
@@ -147,28 +147,28 @@
       "Large": {
         "hdpi": "0.5",
         "ldpi": "0.2",
-        "mdpi": "4.5",
-        "tvdpi": "2.2",
+        "mdpi": "4.4",
+        "tvdpi": "2.1",
         "xhdpi": "0.5"
       },
       "Normal": {
-        "hdpi": "41.1",
-        "mdpi": "4.2",
+        "hdpi": "40.9",
+        "mdpi": "4.1",
         "tvdpi": "0.1",
-        "xhdpi": "25.6",
-        "xxhdpi": "15.0"
+        "xhdpi": "26.3",
+        "xxhdpi": "15.1"
       },
       "Small": {
-        "ldpi": "2.0"
+        "ldpi": "1.9"
       },
       "Xlarge": {
         "hdpi": "0.3",
-        "mdpi": "3.1",
+        "mdpi": "2.9",
         "xhdpi": "0.7"
       }
     },
-    "densitychart": "//chart.googleapis.com/chart?chs=400x250&cht=p&chco=c4df9b%2C6fad0c&chf=bg%2Cs%2C00000000&chd=t%3A2.2%2C11.8%2C2.3%2C41.9%2C26.8%2C15.0&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi",
-    "layoutchart": "//chart.googleapis.com/chart?chs=400x250&cht=p&chco=c4df9b%2C6fad0c&chf=bg%2Cs%2C00000000&chd=t%3A4.1%2C7.9%2C86.0%2C2.0&chl=Xlarge%7CLarge%7CNormal%7CSmall"
+    "densitychart": "//chart.googleapis.com/chart?chco=c4df9b%2C6fad0c&chd=t%3A2.1%2C11.4%2C2.2%2C41.7%2C27.5%2C15.1&chf=bg%2Cs%2C00000000&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chs=400x250&cht=p",
+    "layoutchart": "//chart.googleapis.com/chart?chco=c4df9b%2C6fad0c&chd=t%3A3.9%2C7.7%2C86.5%2C1.9&chf=bg%2Cs%2C00000000&chl=Xlarge%7CLarge%7CNormal%7CSmall&chs=400x250&cht=p"
   }
 ];
 
@@ -176,7 +176,7 @@
 var VERSION_DATA =
 [
   {
-    "chart": "//chart.googleapis.com/chart?chs=500x250&cht=p&chco=c4df9b%2C6fad0c&chf=bg%2Cs%2C00000000&chd=t%3A0.1%2C2.0%2C1.9%2C18.9%2C31.6%2C35.4%2C10.1&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop%7CMarshmallow",
+    "chart": "//chart.googleapis.com/chart?chco=c4df9b%2C6fad0c&chd=t%3A0.1%2C1.9%2C1.7%2C17.8%2C30.2%2C35.1%2C13.3&chf=bg%2Cs%2C00000000&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop%7CMarshmallow&chs=500x250&cht=p",
     "data": [
       {
         "api": 8,
@@ -186,47 +186,47 @@
       {
         "api": 10,
         "name": "Gingerbread",
-        "perc": "2.0"
+        "perc": "1.9"
       },
       {
         "api": 15,
         "name": "Ice Cream Sandwich",
-        "perc": "1.9"
+        "perc": "1.7"
       },
       {
         "api": 16,
         "name": "Jelly Bean",
-        "perc": "6.8"
+        "perc": "6.4"
       },
       {
         "api": 17,
         "name": "Jelly Bean",
-        "perc": "9.4"
+        "perc": "8.8"
       },
       {
         "api": 18,
         "name": "Jelly Bean",
-        "perc": "2.7"
+        "perc": "2.6"
       },
       {
         "api": 19,
         "name": "KitKat",
-        "perc": "31.6"
+        "perc": "30.1"
       },
       {
         "api": 21,
         "name": "Lollipop",
-        "perc": "15.4"
+        "perc": "14.3"
       },
       {
         "api": 22,
         "name": "Lollipop",
-        "perc": "20.0"
+        "perc": "20.8"
       },
       {
         "api": 23,
         "name": "Marshmallow",
-        "perc": "10.1"
+        "perc": "13.3"
       }
     ]
   }
diff --git a/docs/html/distribute/stories/apps.jd b/docs/html/distribute/stories/apps.jd
index 9a642dc..9c2e8e9 100644
--- a/docs/html/distribute/stories/apps.jd
+++ b/docs/html/distribute/stories/apps.jd
@@ -1,5 +1,4 @@
 page.title=Developer Stories: Apps
-meta.tags="apps, developer story"
 page.timestamp=1381449601
 page.image=images/cards/distribute/stories/intuit-mint.png
 page.metaDescription=How app developers are making use of localization, tablet features.
diff --git a/docs/html/distribute/stories/games.jd b/docs/html/distribute/stories/games.jd
index fe059eb..daaac0d 100644
--- a/docs/html/distribute/stories/games.jd
+++ b/docs/html/distribute/stories/games.jd
@@ -1,5 +1,4 @@
 page.title=Developer Stories: Games
-meta.tags="google play, games, global, developer story"
 page.timestamp=1381449601
 page.image=/images/distribute/glu-ew-gpgames.jpg
 page.metaDescription=How game studios are using Google Play game services to deliver new gaming experiences for their users.
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index 3151694..7db0466 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -98,13 +98,13 @@
             throw new InvalidParameterException("Parameter 'clock' must not be null.");
         }
         if (measurements == null || measurements.length == 0) {
-            throw new InvalidParameterException(
-                    "Parameter 'measurements' must not be null or empty.");
+            mReadOnlyMeasurements = Collections.emptyList();
+        } else {
+            Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
+            mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
         }
 
         mClock = clock;
-        Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
-        mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
     }
 
     /**
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 7d5939c..81cc93d 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -264,7 +264,6 @@
      */
     public static final int ENCODING_IEC61937 = 13;
     /** Audio data format: DOLBY TRUEHD compressed
-     * @hide
      **/
     public static final int ENCODING_DOLBY_TRUEHD = 14;
 
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index df0961b..d5296ae 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -15,6 +15,7 @@
  */
 
 package android.media.soundtrigger;
+import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -243,27 +244,29 @@
 
         boolean allowMultipleTriggers =
                 (recognitionFlags & RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS) != 0;
+        int status = STATUS_OK;
         try {
-            mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId),
+            status = mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId),
                     mRecognitionCallback, new RecognitionConfig(captureTriggerAudio,
                         allowMultipleTriggers, null, null));
         } catch (RemoteException e) {
             return false;
         }
-        return true;
+        return status == STATUS_OK;
     }
 
     /**
      * Stops recognition for the associated model.
      */
     public boolean stopRecognition() {
+        int status = STATUS_OK;
         try {
-            mSoundTriggerService.stopRecognition(new ParcelUuid(mSoundModelId),
+            status = mSoundTriggerService.stopRecognition(new ParcelUuid(mSoundModelId),
                     mRecognitionCallback);
         } catch (RemoteException e) {
             return false;
         }
-        return true;
+        return status == STATUS_OK;
     }
 
     /**
diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp
index d07942b..2ab7e39 100644
--- a/media/jni/android_media_MediaDataSource.cpp
+++ b/media/jni/android_media_MediaDataSource.cpp
@@ -26,6 +26,7 @@
 #include "JNIHelp.h"
 
 #include <binder/MemoryDealer.h>
+#include <drm/drm_framework_common.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <nativehelper/ScopedLocalRef.h>
 
@@ -159,4 +160,8 @@
     return String8::format("JMediaDataSource(pid %d, uid %d)", getpid(), getuid());
 }
 
+sp<DecryptHandle> JMediaDataSource::DrmInitialization(const char * /* mime */) {
+    return NULL;
+}
+
 }  // namespace android
diff --git a/media/jni/android_media_MediaDataSource.h b/media/jni/android_media_MediaDataSource.h
index 378baf4..39405d2 100644
--- a/media/jni/android_media_MediaDataSource.h
+++ b/media/jni/android_media_MediaDataSource.h
@@ -47,6 +47,7 @@
     virtual void close();
     virtual uint32_t getFlags();
     virtual String8 toString();
+    virtual sp<DecryptHandle> DrmInitialization(const char *mime);
 
 private:
     // Protect all member variables with mLock because this object will be
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index d830c61..a37590d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.text.Editable;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -91,6 +92,14 @@
                             return false;
                         }
 
+                        // Returning false in this method will bubble the event up to
+                        // {@link BaseActivity#onKeyDown}. In order to prevent backspace popping
+                        // documents once the textView is empty, we are going to trap it here.
+                        if (keyCode == KeyEvent.KEYCODE_DEL
+                                && TextUtils.isEmpty(mDisplayName.getText())) {
+                            return true;
+                        }
+
                         if (keyCode == KeyEvent.KEYCODE_ENTER && mSave.isEnabled()) {
                             performSave();
                             return true;
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 6ea9ae5..cf78a01 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -322,7 +322,7 @@
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Rendszergazda által irányítva"</string>
     <string name="enabled_by_admin" msgid="2386503803463071894">"Engedélyezve a rendszergazda által"</string>
     <string name="disabled_by_admin" msgid="3669999613095206948">"Letiltva a rendszergazda által"</string>
-    <string name="home" msgid="3256884684164448244">"Kezdőlap beállítása"</string>
+    <string name="home" msgid="3256884684164448244">"Beállítások kezdőlapja"</string>
   <string-array name="battery_labels">
     <item msgid="8494684293649631252">"0%"</item>
     <item msgid="8934126114226089439">"50%"</item>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 561691d..492b07d 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -200,7 +200,7 @@
     <string name="select_application" msgid="5156029161289091703">"အပလီကေးရှင်းရွေးချယ်ရန်"</string>
     <string name="no_application" msgid="2813387563129153880">"တခုမှမရှိ"</string>
     <string name="wait_for_debugger" msgid="1202370874528893091">"အပြစ်ရှာဖွေ ဖယ်ရှားချက်ကိုစောင့်ရန်"</string>
-    <string name="wait_for_debugger_summary" msgid="1766918303462746804">"အမှားပြင်ဆင်ရှာဖွေသည့် အပလီကေးရှင်းသည် လုပ်ငန်းမစမှီ တွဲဖက်ရန် အမှားရှာဖွေမည့်သူကို စောင့်နေသည်။"</string>
+    <string name="wait_for_debugger_summary" msgid="1766918303462746804">"အမှားပြင်ဆင်ရှာဖွေသည့် အပလီကေးရှင်းသည် လုပ်ငန်းမစမီ တွဲဖက်ရန် အမှားရှာဖွေမည့်သူကို စောင့်နေသည်။"</string>
     <string name="debug_input_category" msgid="1811069939601180246">"ထည့်သွင်းရန်"</string>
     <string name="debug_drawing_category" msgid="6755716469267367852">"ရေးဆွဲခြင်း"</string>
     <string name="debug_hw_drawing_category" msgid="6220174216912308658">"ဟာ့ဒ်ဝဲ အရှိန်မြှင့် ပုံဖော်ခြင်း"</string>
@@ -213,11 +213,11 @@
     <string name="show_touches" msgid="2642976305235070316">"တို့ခြင်းများကို ပြပါ"</string>
     <string name="show_touches_summary" msgid="6101183132903926324">"တို့ခြင်းများအတွက် အမြင်ဖြင့် တုံ့ပြန်မှုပြပါ"</string>
     <string name="show_screen_updates" msgid="5470814345876056420">"surface အဆင့်မြှင့်မှုများပြပါ"</string>
-    <string name="show_screen_updates_summary" msgid="2569622766672785529">"အဆင့်မြှင့်နေစဉ် ဝင်းဒိုးမျက်နှာတပြင်လုံးကို အချက်ပြရန်"</string>
+    <string name="show_screen_updates_summary" msgid="2569622766672785529">"အပ်ဒိတ်လုပ်စဉ် ဝင်းဒိုးမျက်နှာပြင်တွင် အချက်ပြရန်"</string>
     <string name="show_hw_screen_updates" msgid="5036904558145941590">"GPUမြင်ကွင်းအဆင့်မြှင့်ခြင်းများပြရန်"</string>
     <string name="show_hw_screen_updates_summary" msgid="1115593565980196197">"GPU နှင့်ဆွဲစဉ် ၀င်းဒိုးအတွင်းပိုင်း လျှပ်တပြက်မြင်ကွင်းများ"</string>
     <string name="show_hw_layers_updates" msgid="5645728765605699821">"ဟာ့ဒ်ဝဲအလွှာများအဆင်မြှင့်မှုကိုပြရန်"</string>
-    <string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"အဆင့်မြှင်ချိန် ဟာ့ဒ်ဝဲအလွှာများကို အစိမ်းရောင်ဖြင့်အချက်ပြရန်"</string>
+    <string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"အပ်ဒိတ်လုပ်ချိန် ဟာ့ဒ်ဝဲအလွှာများ အစိမ်းရောင်ပြပါ"</string>
     <string name="debug_hw_overdraw" msgid="2968692419951565417">"GPU ပိုသုံးစွဲမှုအမှားရှာဖွေပြင်ဆင်ရန်"</string>
     <string name="disable_overlays" msgid="2074488440505934665">"HWထပ်ဆင့်အရာများပိတ်ရန်"</string>
     <string name="disable_overlays_summary" msgid="3578941133710758592">"GPU ကိုမျက်နှာပြင်ခင်းကျင်းရာတွင် အမြဲသုံးပါ။"</string>
@@ -283,7 +283,7 @@
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ပြောင်းရန်…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ဖိုင်ကို လုံခြုံအောင်ပြုလုပ်ပြီးပါပြီ"</string>
     <string name="title_convert_fbe" msgid="1263622876196444453">"ဖိုင်အခြေပြုလုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းလဲရန်"</string>
-    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"အချက်အလက်အပိုင်းကို ဖိုင်အခြေပြုလုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းပါ။\n !!သတိ!! ၎င်းသည်သင့် အချက်အလက်များအားလုံးကို ဖျက်ပါမည်။ \n ဤလုပ်ဆောင်ချက်သည် ပထမအဆင့်တွင်သာ ရှိသေးပြီး မှန်ကန်စွာ လုပ်ဆောင်လိမ့်မည် မဟုတ်ပါ။\n ရှေ့ဆက်ရန် \'ရှင်းလင်းပြီး ပြောင်းလဲရန်…\' ကိုနှိပ်ပါ။"</string>
+    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"အချက်အလက်အပိုင်းကို ဖိုင်အခြေပြုလုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းပါ။\n !!သတိ!! ၎င်းသည်သင့် အချက်အလက်များအားလုံးကို ဖျက်ပါမည်။ \n ဤလုပ်ဆောင်ချက်သည် ပထမအဆင့်တွင်သာ ရှိသေးပြီး မှန်ကန်စွာ လုပ်ဆောင်လိမ့်မည် မဟုတ်ပါ။\n ဆက်လုပ်ရန် \'ရှင်းလင်းပြီး ပြောင်းလဲရန်…\' ကိုနှိပ်ပါ။"</string>
     <string name="button_convert_fbe" msgid="5152671181309826405">"ရှင်းလင်းပြီး ပြောင်းလဲရန်…"</string>
     <string name="picture_color_mode" msgid="4560755008730283695">"ဓာတ်ပုံအရောင်မုဒ်"</string>
     <string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGB ကို အသုံးပြုပါ"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 6efd782..b8fc9ff 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -221,7 +221,7 @@
     <string name="debug_hw_overdraw" msgid="2968692419951565417">"Depurar sobreposição GPU"</string>
     <string name="disable_overlays" msgid="2074488440505934665">"Desativ. sobreposições HW"</string>
     <string name="disable_overlays_summary" msgid="3578941133710758592">"Utilizar sempre GPU para a composição do ecrã"</string>
-    <string name="simulate_color_space" msgid="6745847141353345872">"Simular espaço de cor"</string>
+    <string name="simulate_color_space" msgid="6745847141353345872">"Simular espaço da cor"</string>
     <string name="enable_opengl_traces_title" msgid="6790444011053219871">"Ativar vestígios OpenGL"</string>
     <string name="usb_audio_disable_routing" msgid="8114498436003102671">"Desativ. encam. áudio USB"</string>
     <string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Desativar encam. auto. para periféricos áudio USB"</string>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 108814e..978ca94 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -216,7 +216,4 @@
 
     <!-- Default setting for ability to add users from the lock screen -->
     <bool name="def_add_users_from_lockscreen">false</bool>
-
-    <!-- Default setting for disallow oem unlock. -->
-    <bool name="def_oem_unlock_disallow">false</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 950c7d3..28e9a45 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2330,14 +2330,7 @@
                 }
 
                 if (currentVersion == 127) {
-                    // Version 127: Disable OEM unlock setting by default on some devices.
-                    final SettingsState globalSettings = getGlobalSettingsLocked();
-                    String defaultOemUnlockDisabled = (getContext().getResources()
-                            .getBoolean(R.bool.def_oem_unlock_disallow) ? "1" : "0");
-                    globalSettings.insertSettingLocked(
-                            Settings.Global.OEM_UNLOCK_DISALLOWED,
-                            defaultOemUnlockDisabled,
-                            SettingsState.SYSTEM_PACKAGE_NAME);
+                    // version 127 is no longer used.
                     currentVersion = 128;
                 }
 
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 68395cd..4cd635e 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -48,6 +48,7 @@
             android:clickable="true"
             android:soundEffectsEnabled="false"
             android:src="@drawable/ic_volume_collapse_animation"
+            android:background="@drawable/ripple_drawable"
             tools:ignore="RtlHardcoded"
             android:layout_alignParentEnd="true"
             android:layout_alignParentTop="true"
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index a30fc43..95019b8 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -22,7 +22,8 @@
     android:id="@+id/volume_dialog_row"
     android:paddingEnd="@dimen/volume_dialog_padding_end"
     android:orientation="vertical"
-    android:paddingBottom="@dimen/volume_row_padding_bottom" >
+    android:paddingBottom="@dimen/volume_row_padding_bottom"
+    android:animateLayoutChanges="true">
 
     <TextView
         android:id="@+id/volume_row_header"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index dd46b08..67699737 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -28,7 +28,6 @@
 import android.widget.Space;
 
 import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.tuner.TunerService;
 
@@ -71,6 +70,8 @@
     private View mLastRot0;
     private View mLastRot90;
 
+    private boolean mAlternativeOrder;
+
     public NavigationBarInflaterView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mDensity = context.getResources().getConfiguration().densityDpi;
@@ -114,6 +115,7 @@
                 false);
         mRot90.setId(R.id.rot90);
         addView(mRot90);
+        updateAlternativeOrder();
         if (getParent() instanceof NavigationBarView) {
             ((NavigationBarView) getParent()).updateRotatedViews();
         }
@@ -152,6 +154,26 @@
         }
     }
 
+    public void setAlternativeOrder(boolean alternativeOrder) {
+        if (alternativeOrder != mAlternativeOrder) {
+            mAlternativeOrder = alternativeOrder;
+            updateAlternativeOrder();
+        }
+    }
+
+    private void updateAlternativeOrder() {
+        updateAlternativeOrder(mRot0.findViewById(R.id.ends_group));
+        updateAlternativeOrder(mRot0.findViewById(R.id.center_group));
+        updateAlternativeOrder(mRot90.findViewById(R.id.ends_group));
+        updateAlternativeOrder(mRot90.findViewById(R.id.center_group));
+    }
+
+    private void updateAlternativeOrder(View v) {
+        if (v instanceof ReverseLinearLayout) {
+            ((ReverseLinearLayout) v).setAlternativeOrder(mAlternativeOrder);
+        }
+    }
+
     private void initiallyFill(ButtonDispatcher buttonDispatcher) {
         addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.ends_group));
         addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.center_group));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 53fe6ce3..23aeae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -99,6 +99,8 @@
     private final SparseArray<ButtonDispatcher> mButtonDisatchers = new SparseArray<>();
     private Configuration mConfiguration;
 
+    private NavigationBarInflaterView mNavigationInflaterView;
+
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
         private boolean mHomeAppearing;
@@ -472,9 +474,10 @@
 
     @Override
     public void onFinishInflate() {
+        mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
+                R.id.navigation_inflater);
         updateRotatedViews();
-        ((NavigationBarInflaterView) findViewById(R.id.navigation_inflater)).setButtonDispatchers(
-                mButtonDisatchers);
+        mNavigationInflaterView.setButtonDispatchers(mButtonDisatchers);
 
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
 
@@ -530,6 +533,7 @@
         }
         mCurrentView = mRotatedViews[rot];
         mCurrentView.setVisibility(View.VISIBLE);
+        mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
         for (int i = 0; i < mButtonDisatchers.size(); i++) {
             mButtonDisatchers.valueAt(i).setCurrentView(mCurrentView);
         }
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 34aaae4..9bc5426 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -52,6 +52,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioAttributes;
 import android.media.MediaMetadata;
@@ -200,7 +201,7 @@
 
 public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
-        HeadsUpManager.OnHeadsUpChangedListener {
+        HeadsUpManager.OnHeadsUpChangedListener, DisplayManager.DisplayListener {
     static final String TAG = "PhoneStatusBar";
     public static final boolean DEBUG = BaseStatusBar.DEBUG;
     public static final boolean SPEW = false;
@@ -684,6 +685,8 @@
         mUnlockMethodCache.addListener(this);
         startKeyguard();
 
+        mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, null);
+
         mDozeServiceHost = new DozeServiceHost();
         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mDozeServiceHost);
         putComponent(DozeHost.class, mDozeServiceHost);
@@ -3503,6 +3506,21 @@
     }
 
     @Override
+    public void onDisplayAdded(int displayId) {
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            repositionNavigationBar();
+        }
+    }
+
+    @Override
     public void userSwitched(int newUserId) {
         super.userSwitched(newUserId);
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
index 3682aa1..f45967a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
@@ -30,7 +30,11 @@
  */
 public class ReverseLinearLayout extends LinearLayout {
 
-    private boolean mIsLayoutRtl;
+    /** If true, the layout is reversed vs. a regular linear layout */
+    private boolean mIsLayoutReverse;
+
+    /** If true, the layout is opposite to it's natural reversity from the layout direction */
+    private boolean mIsAlternativeOrder;
 
     public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -39,45 +43,50 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mIsLayoutRtl = getResources().getConfiguration()
-                .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        updateOrder();
     }
 
     @Override
     public void addView(View child) {
         reversParams(child.getLayoutParams());
-        if (mIsLayoutRtl) {
-            super.addView(child);
-        } else {
+        if (mIsLayoutReverse) {
             super.addView(child, 0);
+        } else {
+            super.addView(child);
         }
     }
 
     @Override
     public void addView(View child, ViewGroup.LayoutParams params) {
         reversParams(params);
-        if (mIsLayoutRtl) {
-            super.addView(child, params);
-        } else {
+        if (mIsLayoutReverse) {
             super.addView(child, 0, params);
+        } else {
+            super.addView(child, params);
         }
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        updateRTLOrder();
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+        updateOrder();
+    }
+
+    public void setAlternativeOrder(boolean alternative) {
+        mIsAlternativeOrder = alternative;
+        updateOrder();
     }
 
     /**
      * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we
      * have to do it manually
      */
-    private void updateRTLOrder() {
-        boolean isLayoutRtl = getResources().getConfiguration()
-                .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-        if (mIsLayoutRtl != isLayoutRtl) {
-            // RTL changed, swap the order of all views.
+    private void updateOrder() {
+        boolean isLayoutRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        boolean isLayoutReverse = isLayoutRtl ^ mIsAlternativeOrder;
+
+        if (mIsLayoutReverse != isLayoutReverse) {
+            // reversity changed, swap the order of all views.
             int childCount = getChildCount();
             ArrayList<View> childList = new ArrayList<>(childCount);
             for (int i = 0; i < childCount; i++) {
@@ -87,7 +96,7 @@
             for (int i = childCount - 1; i >= 0; i--) {
                 super.addView(childList.get(i));
             }
-            mIsLayoutRtl = isLayoutRtl;
+            mIsLayoutReverse = isLayoutReverse;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 5d734c6..b9c7a4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -89,14 +89,18 @@
 
     @Override
     public void addStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
-        mChangeCallbacks.add(cb);
+        synchronized (mChangeCallbacks) {
+            mChangeCallbacks.add(cb);
+        }
         cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
         cb.onPowerSaveChanged(mPowerSave);
     }
 
     @Override
     public void removeStateChangedCallback(BatteryController.BatteryStateChangeCallback cb) {
-        mChangeCallbacks.remove(cb);
+        synchronized (mChangeCallbacks) {
+            mChangeCallbacks.remove(cb);
+        }
     }
 
     @Override
@@ -171,16 +175,20 @@
     }
 
     protected void fireBatteryLevelChanged() {
-        final int N = mChangeCallbacks.size();
-        for (int i = 0; i < N; i++) {
-            mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+        synchronized (mChangeCallbacks) {
+            final int N = mChangeCallbacks.size();
+            for (int i = 0; i < N; i++) {
+                mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+            }
         }
     }
 
     private void firePowerSaveChanged() {
-        final int N = mChangeCallbacks.size();
-        for (int i = 0; i < N; i++) {
-            mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
+        synchronized (mChangeCallbacks) {
+            final int N = mChangeCallbacks.size();
+            for (int i = 0; i < N; i++) {
+                mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index f01e95f..995ecae 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -124,12 +124,8 @@
                 : null;
         Util.setText(mSummaryLine1, line1);
 
-        final boolean isForever = mConfig != null && mConfig.manualRule != null
-                && mConfig.manualRule.conditionId == null;
-        final CharSequence line2 =
-                isForever ? mContext.getString(com.android.internal.R.string.zen_mode_forever_dnd)
-                : ZenModeConfig.getConditionSummary(mContext, mConfig, mController.getCurrentUser(),
-                        true /*shortVersion*/);
+        final CharSequence line2 = ZenModeConfig.getConditionSummary(mContext, mConfig,
+                                mController.getCurrentUser(), true /*shortVersion*/);
         Util.setText(mSummaryLine2, line2);
     }
 
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 47d1493..97efed0 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -40,22 +40,30 @@
 
 public class WallpaperBackupAgent extends BackupAgent {
     private static final String TAG = "WallpaperBackup";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     // NB: must be kept in sync with WallpaperManagerService but has no
     // compile-time visibility.
 
     // Target filenames within the system's wallpaper directory
     static final String WALLPAPER = "wallpaper_orig";
+    static final String WALLPAPER_LOCK = "wallpaper_lock_orig";
     static final String WALLPAPER_INFO = "wallpaper_info.xml";
 
     // Names of our local-data stage files/links
     static final String IMAGE_STAGE = "wallpaper-stage";
+    static final String LOCK_IMAGE_STAGE = "wallpaper-lock-stage";
     static final String INFO_STAGE = "wallpaper-info-stage";
     static final String EMPTY_SENTINEL = "empty";
+    static final String QUOTA_SENTINEL = "quota";
 
-    private File mWallpaperInfo;    // wallpaper metadata file
-    private File mWallpaperFile;    // primary wallpaper image file
+    private File mWallpaperInfo;        // wallpaper metadata file
+    private File mWallpaperFile;        // primary wallpaper image file
+    private File mLockWallpaperFile;    // lock wallpaper image file
+
+    // If this file exists, it means we exceeded our quota last time
+    private File mQuotaFile;
+    private boolean mQuotaExceeded;
 
     private WallpaperManager mWm;
 
@@ -68,7 +76,14 @@
         File wallpaperDir = Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM);
         mWallpaperInfo = new File(wallpaperDir, WALLPAPER_INFO);
         mWallpaperFile = new File(wallpaperDir, WALLPAPER);
+        mLockWallpaperFile = new File(wallpaperDir, WALLPAPER_LOCK);
         mWm = (WallpaperManager) getSystemService(Context.WALLPAPER_SERVICE);
+
+        mQuotaFile = new File(getFilesDir(), QUOTA_SENTINEL);
+        mQuotaExceeded = mQuotaFile.exists();
+        if (DEBUG) {
+            Slog.v(TAG, "quota file " + mQuotaFile.getPath() + " exists=" + mQuotaExceeded);
+        }
     }
 
     @Override
@@ -77,6 +92,7 @@
         final File filesDir = getFilesDir();
         final File infoStage = new File(filesDir, INFO_STAGE);
         final File imageStage = new File (filesDir, IMAGE_STAGE);
+        final File lockImageStage = new File (filesDir, LOCK_IMAGE_STAGE);
         final File empty = new File (filesDir, EMPTY_SENTINEL);
 
         try {
@@ -96,11 +112,18 @@
                 // In case of prior muddled state
                 infoStage.delete();
                 imageStage.delete();
+                lockImageStage.delete();
 
                 Os.link(mWallpaperInfo.getCanonicalPath(), infoStage.getCanonicalPath());
                 fullBackupFile(infoStage, data);
                 Os.link(mWallpaperFile.getCanonicalPath(), imageStage.getCanonicalPath());
                 fullBackupFile(imageStage, data);
+
+                // Don't try to store the lock image if we overran our quota last time
+                if (!mQuotaExceeded) {
+                    Os.link(mLockWallpaperFile.getCanonicalPath(), lockImageStage.getCanonicalPath());
+                    fullBackupFile(lockImageStage, data);
+                }
             } else {
                 if (DEBUG) {
                     Slog.v(TAG, "Wallpaper not backup-eligible; writing no data");
@@ -114,6 +137,26 @@
             }
             infoStage.delete();
             imageStage.delete();
+            lockImageStage.delete();
+
+            // Even if this time we had to back off on attempting to store the lock image
+            // due to exceeding the data quota, try again next time.  This will alternate
+            // between "try both" and "only store the primary image" until either there
+            // is no lock image to store, or the quota is raised, or both fit under the
+            // quota.
+            mQuotaFile.delete();
+        }
+    }
+
+    @Override
+    public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+        if (DEBUG) {
+            Slog.i(TAG, "Quota exceeded (" + backupDataBytes + " vs " + quotaBytes + ')');
+        }
+        try (FileOutputStream f = new FileOutputStream(mQuotaFile)) {
+            f.write(0);
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to record quota-exceeded: " + e.getMessage());
         }
     }
 
@@ -124,30 +167,17 @@
         if (DEBUG) {
             Slog.v(TAG, "onRestoreFinished()");
         }
-        final File infoStage = new File(getFilesDir(), INFO_STAGE);
-        final File imageStage = new File (getFilesDir(), IMAGE_STAGE);
+        final File filesDir = getFilesDir();
+        final File infoStage = new File(filesDir, INFO_STAGE);
+        final File imageStage = new File (filesDir, IMAGE_STAGE);
+        final File lockImageStage = new File (filesDir, LOCK_IMAGE_STAGE);
 
         try {
             // It is valid for the imagery to be absent; it means that we were not permitted
-            // to back up the original image on the source device.
-            if (imageStage.exists()) {
-                if (DEBUG) {
-                    Slog.v(TAG, "Got restored wallpaper; applying");
-                }
-
-                // Parse the restored info file to find the crop hint.  Note that this currently
-                // relies on a priori knowledge of the wallpaper info file schema.
-                Rect cropHint = parseCropHint(infoStage);
-                if (cropHint != null) {
-                    if (DEBUG) {
-                        Slog.v(TAG, "Restored crop hint " + cropHint + "; now writing data");
-                    }
-                    WallpaperManager wm = getSystemService(WallpaperManager.class);
-                    try (FileInputStream in = new FileInputStream(imageStage)) {
-                        wm.setStream(in, cropHint, true, WallpaperManager.FLAG_SYSTEM);
-                    } finally {} // auto-closes 'in'
-                }
-            }
+            // to back up the original image on the source device, or there was no user-supplied
+            // wallpaper image present.
+            restoreFromStage(imageStage, infoStage, "wp", WallpaperManager.FLAG_SYSTEM);
+            restoreFromStage(lockImageStage, infoStage, "kwp", WallpaperManager.FLAG_LOCK);
         } catch (Exception e) {
             Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
         } finally {
@@ -156,10 +186,31 @@
             }
             infoStage.delete();
             imageStage.delete();
+            lockImageStage.delete();
         }
     }
 
-    private Rect parseCropHint(File wallpaperInfo) {
+    private void restoreFromStage(File stage, File info, String hintTag, int which)
+            throws IOException {
+        if (stage.exists()) {
+            if (DEBUG) {
+                Slog.v(TAG, "Got restored wallpaper; applying which=" + which);
+            }
+            // Parse the restored info file to find the crop hint.  Note that this currently
+            // relies on a priori knowledge of the wallpaper info file schema.
+            Rect cropHint = parseCropHint(info, hintTag);
+            if (cropHint != null) {
+                if (DEBUG) {
+                    Slog.v(TAG, "Restored crop hint " + cropHint + "; now writing data");
+                }
+                try (FileInputStream in = new FileInputStream(stage)) {
+                    mWm.setStream(in, cropHint, true, which);
+                } finally {} // auto-closes 'in'
+            }
+        }
+    }
+
+    private Rect parseCropHint(File wallpaperInfo, String sectionTag) {
         Rect cropHint = new Rect();
         try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
             XmlPullParser parser = Xml.newPullParser();
@@ -170,7 +221,7 @@
                 type = parser.next();
                 if (type == XmlPullParser.START_TAG) {
                     String tag = parser.getName();
-                    if ("wp".equals(tag)) {
+                    if (sectionTag.equals(tag)) {
                         cropHint.left = getAttributeInt(parser, "cropLeft", 0);
                         cropHint.top = getAttributeInt(parser, "cropTop", 0);
                         cropHint.right = getAttributeInt(parser, "cropRight", 0);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 45e3e7e..64b30d9 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2371,6 +2371,10 @@
     // CATEGORY: SETTINGS
     NIGHT_DISPLAY_SETTINGS = 488;
 
+    // ACTION: Settings -> Storage -> Manage storage -> Click Storage Manager
+        //   SUBTYPE: false is off, true is on
+    ACTION_TOGGLE_STORAGE_MANAGER = 489;
+
     // ---- End N-MR1 Constants, all N-MR1 constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6defd0f..9d3889b 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1905,7 +1905,7 @@
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = widget.host;
         args.arg2 = widget.host.callbacks;
-        args.arg3 = updateViews.clone();
+        args.arg3 = (updateViews != null) ? updateViews.clone() : null;
         args.arg4 = requestTime;
         args.argi1 = widget.appWidgetId;
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 0b85198..5ce8c9e 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -797,7 +797,7 @@
                                 queue, oldJournal, null, null, false);
                         Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
                         sendMessage(pbtMessage);
-                    } catch (RemoteException e) {
+                    } catch (Exception e) {
                         // unable to ask the transport its dir name -- transient failure, since
                         // the above check succeeded.  Try again next time.
                         Slog.e(TAG, "Transport became unavailable attempting backup");
@@ -940,7 +940,7 @@
                     }
                     if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
                 } catch (Exception e) {
-                    Slog.e(TAG, "Error from transport getting set list");
+                    Slog.e(TAG, "Error from transport getting set list: " + e.getMessage());
                 } finally {
                     if (params.observer != null) {
                         try {
@@ -948,7 +948,7 @@
                         } catch (RemoteException re) {
                             Slog.e(TAG, "Unable to report listing to observer");
                         } catch (Exception e) {
-                            Slog.e(TAG, "Restore observer threw", e);
+                            Slog.e(TAG, "Restore observer threw: " + e.getMessage());
                         }
                     }
 
@@ -1770,8 +1770,10 @@
                 }
                 return; // done; don't fall through to the error case
             }
-        } catch (RemoteException e) {
+        } catch (Exception e) {
             // transport threw when asked its name; fall through to the lookup-failed case
+            Slog.e(TAG, "Transport " + transportName + " failed to report name: "
+                    + e.getMessage());
         }
 
         // The named transport doesn't exist or threw.  This operation is
@@ -1859,7 +1861,7 @@
                             System.currentTimeMillis() + delay, mRunInitIntent);
                 }
             }
-        } catch (RemoteException e) {
+        } catch (Exception e) {
             // the transport threw when asked its file naming prefs; declare it invalid
             Slog.e(TAG, "Unable to register transport as " + name);
             mTransportNames.remove(component);
@@ -2065,8 +2067,9 @@
                 IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
                 registerTransport(transport.name(), name, transport);
                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to register transport " + component);
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to register transport " + component
+                        + ": " + e.getMessage());
                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
             }
         }
@@ -2529,8 +2532,8 @@
         String dirName;
         try {
             dirName = transport.transportDirName();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Transport became unavailable while attempting backup");
+        } catch (Exception e) {
+            Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage());
             sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
             return BackupManager.ERROR_TRANSPORT_ABORTED;
         }
@@ -2974,9 +2977,10 @@
                 try {
                     mCurrentToken = mTransport.getCurrentRestoreSet();
                     writeRestoreTokens();
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     // nothing for it at this point, unfortunately, but this will be
                     // recorded the next time we fully succeed.
+                    Slog.e(TAG, "Transport threw reporting restore set: " + e.getMessage());
                     addBackupTrace("transport threw returning token");
                 }
             }
@@ -3001,7 +3005,7 @@
                             }
                         }
                     } catch (Exception e) {
-                        Slog.w(TAG, "Failed to query transport name heading for init", e);
+                        Slog.w(TAG, "Failed to query transport name for init: " + e.getMessage());
                         // swallow it and proceed; we don't rely on this
                     }
                     clearMetadata();
@@ -3367,8 +3371,8 @@
                     try {
                         long quota = mTransport.getBackupQuota(mCurrentPackage.packageName, false);
                         mAgentBinder.doQuotaExceeded(size, quota);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to contact backup agent for quota exceeded");
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Unable to notify about quota exceeded: " + e.getMessage());
                     }
                 }
                 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
@@ -3406,7 +3410,7 @@
             try {
                 delay = mTransport.requestBackupTime();
             } catch (Exception e) {
-                Slog.w(TAG, "Unable to contact transport for recommended backoff");
+                Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage());
                 delay = 0;  // use the scheduler's default
             }
             KeyValueBackupJob.schedule(mContext, delay);
@@ -4324,7 +4328,10 @@
                 Slog.e(TAG, "Internal exception during full backup", e);
             } finally {
                 try {
-                    if (out != null) out.close();
+                    if (out != null) {
+                        out.flush();
+                        out.close();
+                    }
                     mOutputFile.close();
                 } catch (IOException e) {
                     /* nothing we can do about this */
@@ -4662,6 +4669,13 @@
                     }
                     cleanUpPipes(transportPipes);
                     cleanUpPipes(enginePipes);
+                    if (currentPackage.applicationInfo != null) {
+                        Slog.i(TAG, "Unbinding agent in " + packageName);
+                        addBackupTrace("unbinding " + packageName);
+                        try {
+                            mActivityManager.unbindBackupAgent(currentPackage.applicationInfo);
+                        } catch (RemoteException e) { /* can't happen; activity manager is local */ }
+                    }
                 }
             } catch (Exception e) {
                 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
@@ -4997,7 +5011,7 @@
                 return false;
             }
         } catch (Exception e) {
-            Slog.w(TAG, "Unable to contact transport");
+            Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
             return false;
         }
 
@@ -8221,9 +8235,9 @@
                 // Success; cache the metadata and continue as expected with the
                 // next state already enqueued
 
-            } catch (RemoteException e) {
+            } catch (Exception e) {
                 // If we lost the transport at any time, halt
-                Slog.e(TAG, "Unable to contact transport for restore");
+                Slog.e(TAG, "Unable to contact transport for restore: " + e.getMessage());
                 mStatus = BackupTransport.TRANSPORT_ERROR;
                 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
                 executeNextState(UnifiedRestoreState.FINAL);
@@ -8320,8 +8334,9 @@
                     nextState = UnifiedRestoreState.RUNNING_QUEUE;
                     return;
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Can't get next target from transport; ending restore");
+            } catch (Exception e) {
+                Slog.e(TAG, "Can't get next restore target from transport; halting: "
+                        + e.getMessage());
                 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
                 nextState = UnifiedRestoreState.FINAL;
                 return;
@@ -8631,11 +8646,11 @@
                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                             mCurrentPackage.packageName, "I/O error on pipes");
                     status = BackupTransport.AGENT_ERROR;
-                } catch (RemoteException e) {
-                    // The transport went away; terminate the whole operation.  Closing
+                } catch (Exception e) {
+                    // The transport threw; terminate the whole operation.  Closing
                     // the sockets will wake up the engine and it will then tidy up the
                     // remote end.
-                    Slog.e(TAG, "Transport failed during restore");
+                    Slog.e(TAG, "Transport failed during restore: " + e.getMessage());
                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
                     status = BackupTransport.TRANSPORT_ERROR;
                 } finally {
@@ -8673,9 +8688,10 @@
                         // level is immaterial; we need to tell the transport to bail
                         try {
                             mTransport.abortFullRestore();
-                        } catch (RemoteException e) {
+                        } catch (Exception e) {
                             // transport itself is dead; make sure we handle this as a
                             // fatal error
+                            Slog.e(TAG, "Transport threw from abortFullRestore: " + e.getMessage());
                             status = BackupTransport.TRANSPORT_ERROR;
                         }
 
@@ -9023,16 +9039,15 @@
                 // Tell the transport to remove all the persistent storage for the app
                 // TODO - need to handle failures
                 mTransport.clearBackupData(mPackage);
-            } catch (RemoteException e) {
-                // can't happen; the transport is local
             } catch (Exception e) {
-                Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
+                Slog.e(TAG, "Transport threw clearing data for " + mPackage + ": " + e.getMessage());
             } finally {
                 try {
                     // TODO - need to handle failures
                     mTransport.finishBackup();
-                } catch (RemoteException e) {
-                    // can't happen; the transport is local
+                } catch (Exception e) {
+                    // Nothing we can do here, alas
+                    Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage());
                 }
 
                 // Last but not least, release the cpu
@@ -9091,8 +9106,6 @@
                                 System.currentTimeMillis() + delay, mRunInitIntent);
                     }
                 }
-            } catch (RemoteException e) {
-                // can't happen; the transports are local
             } catch (Exception e) {
                 Slog.e(TAG, "Unexpected error performing init", e);
             } finally {
@@ -9780,8 +9793,9 @@
                     if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
                             + intent);
                     return intent;
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     /* fall through to return null */
+                    Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
                 }
             }
         }
@@ -9805,8 +9819,9 @@
                     final String text = transport.currentDestinationString();
                     if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
                     return text;
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     /* fall through to return null */
+                    Slog.e(TAG, "Unable to get string from transport: " + e.getMessage());
                 }
             }
         }
@@ -9827,8 +9842,9 @@
                     if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
                             + intent);
                     return intent;
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     /* fall through to return null */
+                    Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
                 }
             }
         }
@@ -9849,8 +9865,9 @@
                     final String text = transport.dataManagementLabel();
                     if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
                     return text;
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     /* fall through to return null */
+                    Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
                 }
             }
         }
@@ -9943,9 +9960,9 @@
                 msg.obj = new RestoreParams(transport, dirName, null,
                         restoreSet, packageName, token);
                 mBackupHandler.sendMessage(msg);
-            } catch (RemoteException e) {
-                // Binding to the transport broke; back off and proceed with the installation.
-                Slog.e(TAG, "Unable to contact transport");
+            } catch (Exception e) {
+                // Calling into the transport broke; back off and proceed with the installation.
+                Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
                 skip = true;
             }
         }
@@ -10066,8 +10083,8 @@
                 try {
                     return transport.isAppEligibleForBackup(packageInfo,
                         appGetsFullBackup(packageInfo));
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to contact transport");
+                } catch (Exception e) {
+                    Slog.e(TAG, "Unable to ask about eligibility: " + e.getMessage());
                 }
             }
             // If transport is not present we couldn't tell that the package is not eligible.
@@ -10169,9 +10186,9 @@
             String dirName;
             try {
                 dirName = mRestoreTransport.transportDirName();
-            } catch (RemoteException e) {
+            } catch (Exception e) {
                 // Transport went AWOL; fail.
-                Slog.e(TAG, "Unable to contact transport for restore");
+                Slog.e(TAG, "Unable to get transport dir for restore: " + e.getMessage());
                 return -1;
             }
 
@@ -10251,9 +10268,9 @@
             String dirName;
             try {
                 dirName = mRestoreTransport.transportDirName();
-            } catch (RemoteException e) {
+            } catch (Exception e) {
                 // Transport went AWOL; fail.
-                Slog.e(TAG, "Unable to contact transport for restore");
+                Slog.e(TAG, "Unable to get transport name for restoreSome: " + e.getMessage());
                 return -1;
             }
 
@@ -10341,9 +10358,9 @@
                 String dirName;
                 try {
                     dirName = mRestoreTransport.transportDirName();
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     // Transport went AWOL; fail.
-                    Slog.e(TAG, "Unable to contact transport for restore");
+                    Slog.e(TAG, "Unable to get transport dir for restorePackage: " + e.getMessage());
                     return -1;
                 }
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 312553a..4dd88b2 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -2547,7 +2547,9 @@
                 } else {
                     // Just in case -- even though no wakeup flag was set, make sure
                     // we have updated the kernel to the next alarm time.
-                    rescheduleKernelAlarmsLocked();
+                    synchronized (mLock) {
+                        rescheduleKernelAlarmsLocked();
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index a8ae914d..f93c716 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -70,6 +70,9 @@
     /** The interval between accelerometer orientation measurements. */
     private static final long ORIENTATION_MEASUREMENT_INTERVAL_MILLIS = 5000;
 
+    /** The maximum duration we will hold a wakelock to determine stationary status. */
+    private static final long WAKELOCK_TIMEOUT_MILLIS = 30000;
+
     /**
      * The duration in milliseconds after which an orientation measurement is considered
      * too stale to be used.
@@ -141,25 +144,30 @@
                 mCurrentGravityVector = null;
                 mPreviousGravityVector = null;
                 mWakeLock.acquire();
+                Message wakelockTimeoutMsg = Message.obtain(mHandler, mWakelockTimeout);
+                mHandler.sendMessageDelayed(wakelockTimeoutMsg, WAKELOCK_TIMEOUT_MILLIS);
                 startOrientationMeasurementLocked();
             }
         }
     }
 
     public void stop() {
-        if (mState == STATE_ACTIVE) {
-            synchronized (mLock) {
+        synchronized (mLock) {
+            if (mState == STATE_ACTIVE) {
                 mState = STATE_INACTIVE;
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
-                if (mMeasurementInProgress) {
-                    mMeasurementInProgress = false;
-                    mSensorManager.unregisterListener(mListener);
-                }
-                mHandler.removeCallbacks(mMeasurementTimeout);
-                mHandler.removeCallbacks(mSensorRestart);
-                mCurrentGravityVector = null;
-                mPreviousGravityVector = null;
+            }
+            if (mMeasurementInProgress) {
+                mMeasurementInProgress = false;
+                mSensorManager.unregisterListener(mListener);
+            }
+            mHandler.removeCallbacks(mMeasurementTimeout);
+            mHandler.removeCallbacks(mSensorRestart);
+            mCurrentGravityVector = null;
+            mPreviousGravityVector = null;
+            if (mWakeLock.isHeld()) {
                 mWakeLock.release();
+                mHandler.removeCallbacks(mWakelockTimeout);
             }
         }
     }
@@ -173,9 +181,8 @@
                 mMeasurementInProgress = true;
                 mRunningStats.reset();
             }
-            Message msg = Message.obtain(mHandler, mMeasurementTimeout);
-            msg.setAsynchronous(true);
-            mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
+            Message measurementTimeoutMsg = Message.obtain(mHandler, mMeasurementTimeout);
+            mHandler.sendMessageDelayed(measurementTimeoutMsg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
         }
     }
 
@@ -186,10 +193,12 @@
         if (mMeasurementInProgress) {
             mSensorManager.unregisterListener(mListener);
             mHandler.removeCallbacks(mMeasurementTimeout);
-            long detectionEndTime = SystemClock.elapsedRealtime();
             mMeasurementInProgress = false;
             mPreviousGravityVector = mCurrentGravityVector;
             mCurrentGravityVector = mRunningStats.getRunningAverage();
+            if (mRunningStats.getSampleCount() == 0) {
+                Slog.w(TAG, "No accelerometer data acquired for orientation measurement.");
+            }
             if (DEBUG) {
                 Slog.d(TAG, "mRunningStats = " + mRunningStats.toString());
                 String currentGravityVectorString = (mCurrentGravityVector == null) ?
@@ -203,7 +212,10 @@
             status = getStationaryStatus();
             if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
             if (status != RESULT_UNKNOWN) {
-                mWakeLock.release();
+                if (mWakeLock.isHeld()) {
+                    mWakeLock.release();
+                    mHandler.removeCallbacks(mWakelockTimeout);
+                }
                 if (DEBUG) {
                     Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " + status);
                 }
@@ -217,7 +229,6 @@
                         " scheduled in " + ORIENTATION_MEASUREMENT_INTERVAL_MILLIS +
                         " milliseconds.");
                 Message msg = Message.obtain(mHandler, mSensorRestart);
-                msg.setAsynchronous(true);
                 mHandler.sendMessageDelayed(msg, ORIENTATION_MEASUREMENT_INTERVAL_MILLIS);
             }
         }
@@ -271,6 +282,7 @@
                 }
             }
             if (status != RESULT_UNKNOWN) {
+                mHandler.removeCallbacks(mWakelockTimeout);
                 mCallback.onAnyMotionResult(status);
             }
         }
@@ -290,20 +302,30 @@
     };
 
     private final Runnable mMeasurementTimeout = new Runnable() {
-      @Override
-      public void run() {
-          int status = RESULT_UNKNOWN;
-          synchronized (mLock) {
-              if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
+        @Override
+        public void run() {
+            int status = RESULT_UNKNOWN;
+            synchronized (mLock) {
+                if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
                       "data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
                       "orientation measurement.");
-              status = stopOrientationMeasurementLocked();
-          }
-          if (status != RESULT_UNKNOWN) {
-              mCallback.onAnyMotionResult(status);
-          }
-      }
-  };
+                status = stopOrientationMeasurementLocked();
+            }
+            if (status != RESULT_UNKNOWN) {
+                mHandler.removeCallbacks(mWakelockTimeout);
+                mCallback.onAnyMotionResult(status);
+            }
+        }
+    };
+
+    private final Runnable mWakelockTimeout = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                stop();
+            }
+        }
+    };
 
     /**
      * A timestamped three dimensional vector and some vector operations.
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index afed5ef..488f0e7 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -973,13 +973,12 @@
                 cancelSensingTimeoutAlarmLocked();
             }
         }
-        if (result == AnyMotionDetector.RESULT_MOVED) {
-            if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
+        if ((result == AnyMotionDetector.RESULT_MOVED) ||
+            (result == AnyMotionDetector.RESULT_UNKNOWN)) {
             synchronized (this) {
-                handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "sense_motion");
+                handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "non_stationary");
             }
         } else if (result == AnyMotionDetector.RESULT_STATIONARY) {
-            if (DEBUG) Slog.d(TAG, "RESULT_STATIONARY received.");
             if (mState == STATE_SENSING) {
                 // If we are currently sensing, it is time to move to locating.
                 synchronized (this) {
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index e233b1c..080b46c 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -157,11 +157,10 @@
         }
     }
 
-    private void enforceFactoryResetAllowed() {
-        final boolean isOemUnlockRestricted = UserManager.get(mContext)
-                .hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
-        if (isOemUnlockRestricted) {
-            throw new SecurityException("OEM unlock is disallowed by DISALLOW_FACTORY_RESET");
+    private void enforceUserRestriction(String userRestriction) {
+        if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
+            throw new SecurityException(
+                    "OEM unlock is disallowed by user restriction: " + userRestriction);
         }
     }
 
@@ -467,13 +466,9 @@
             enforceIsAdmin();
 
             if (enabled) {
-                // Do not allow oem unlock to be enabled if it has been disallowed.
-                if (Settings.Global.getInt(getContext().getContentResolver(),
-                        Settings.Global.OEM_UNLOCK_DISALLOWED, 0) == 1) {
-                    throw new SecurityException(
-                            "OEM unlock has been disallowed by OEM_UNLOCK_DISALLOWED.");
-                }
-                enforceFactoryResetAllowed();
+                // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
+                enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
+                enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
             }
             synchronized (mLock) {
                 doSetOemUnlockEnabledLocked(enabled);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5bfb90f..4762a67 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18669,7 +18669,7 @@
 
         synchronized(this) {
             final long origId = Binder.clearCallingIdentity();
-            updateConfigurationLocked(values, null, false, true, userId);
+            updateConfigurationLocked(values, null, false, true, userId, false /* deferResume */);
             Binder.restoreCallingIdentity(origId);
         }
     }
@@ -18737,11 +18737,16 @@
         updateConfigurationLocked(configuration, null, false);
     }
 
-    boolean updateConfigurationLocked(Configuration values,
-            ActivityRecord starting, boolean initLocale) {
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale) {
+        return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
+    }
+
+    boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean deferResume) {
         // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
-        return updateConfigurationLocked(values, starting, initLocale, false,
-                UserHandle.USER_NULL);
+        return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
+                UserHandle.USER_NULL, deferResume);
     }
 
     // To cache the list of supported system locales
@@ -18757,8 +18762,8 @@
      * @param userId is only used when persistent parameter is set to true to persist configuration
      *               for that particular user
      */
-    private boolean updateConfigurationLocked(Configuration values,
-            ActivityRecord starting, boolean initLocale, boolean persistent, int userId) {
+    private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+            boolean initLocale, boolean persistent, int userId, boolean deferResume) {
         int changes = 0;
 
         if (mWindowManager != null) {
@@ -18886,7 +18891,7 @@
                     for (int stackId : resizedStacks) {
                         final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
                         mStackSupervisor.resizeStackLocked(
-                                stackId, newBounds, null, null, false, false, !DEFER_RESUME);
+                                stackId, newBounds, null, null, false, false, deferResume);
                     }
                 }
             }
@@ -21783,16 +21788,16 @@
             Preconditions.checkNotNull(values, "Configuration must not be null");
             Preconditions.checkArgumentNonnegative(userId, "userId " + userId + " not supported");
             synchronized (ActivityManagerService.this) {
-                updateConfigurationLocked(values, null, false, true, userId);
+                updateConfigurationLocked(values, null, false, true, userId,
+                        false /* deferResume */);
             }
         }
 
         @Override
-        public IIntentSender getActivityIntentSenderAsPackage(
-                String packageName, int userId, int requestCode, Intent intent,
-                int flags, Bundle bOptions) {
-            String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
-                    mContext.getContentResolver()) : null;
+        public int startActivityAsPackage(String packageName, int userId, Intent intent,
+                Bundle bOptions) {
+            Preconditions.checkNotNull(intent, "intent");
+            final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
 
             // UID of the package on user userId.
             // "= 0" is needed because otherwise catch(RemoteException) would make it look like
@@ -21806,11 +21811,9 @@
             }
 
             synchronized (ActivityManagerService.this) {
-                return getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, packageName, packageUid,
-                        UserHandle.getUserId(packageUid), /*token*/ null, /*resultWho*/ null,
-                        requestCode, new Intent[] {intent}, new String[]{resolvedType},
-                        flags, bOptions);
+                return startActivityInPackage(packageUid, packageName, intent, resolvedType,
+                        /*resultTo*/ null, /*resultWho*/ null, /*requestCode*/ 0, /*startFlags*/ 0,
+                        bOptions, userId, /*container*/ null, /*inTask*/ null);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a102664..eb02dc3 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2051,6 +2051,14 @@
         // We don't want to clear starting window for activities that aren't behind fullscreen
         // activities as we need to display their starting window until they are done initializing.
         boolean behindFullscreenActivity = false;
+
+        if (getStackVisibilityLocked(null) == STACK_INVISIBLE) {
+            // The stack is not visible, so no activity in it should be displaying a starting
+            // window. Mark all activities below top and behind fullscreen.
+            aboveTop = false;
+            behindFullscreenActivity = true;
+        }
+
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 82668e4..a4fc251 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1189,7 +1189,10 @@
             Configuration config = mWindowManager.updateOrientationFromAppTokens(
                     mService.mConfiguration,
                     r.mayFreezeScreenLocked(app) ? r.appToken : null);
-            mService.updateConfigurationLocked(config, r, false);
+            // Deferring resume here because we're going to launch new activity shortly.
+            // We don't want to perform a redundant launch of the same record while ensuring
+            // configurations and trying to resume top activity of focused stack.
+            mService.updateConfigurationLocked(config, r, false, true /* deferResume */);
         }
 
         r.app = app;
@@ -2004,7 +2007,7 @@
             boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
         if (stackId == DOCKED_STACK_ID) {
             resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
-                    preserveWindows);
+                    preserveWindows, deferResume);
             return;
         }
         final ActivityStack stack = getStack(stackId);
@@ -2154,8 +2157,16 @@
     }
 
     void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
-            Rect tempDockedTaskInsetBounds,
-            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows) {
+            Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
+            boolean preserveWindows) {
+        resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
+                tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
+                false /* deferResume */);
+    }
+
+    void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
+            boolean preserveWindows, boolean deferResume) {
 
         if (!mAllowDockedStackResize) {
             // Docked stack resize currently disabled.
@@ -2198,11 +2209,13 @@
                     if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
                         resizeStackLocked(i, tempRect, tempOtherTaskBounds,
                                 tempOtherTaskInsetBounds, preserveWindows,
-                                true /* allowResizeInDockedMode */, !DEFER_RESUME);
+                                true /* allowResizeInDockedMode */, deferResume);
                     }
                 }
             }
-            stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
+            if (!deferResume) {
+                stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
+            }
         } finally {
             mAllowDockedStackResize = true;
             mWindowManager.continueSurfaceLayout();
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 49106f4..03843d1 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -58,6 +58,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Set;
 import java.util.concurrent.Semaphore;
 
 import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
@@ -386,8 +387,8 @@
                     } catch (IllegalArgumentException e) {
                         // Hmm, that didn't work, app might have crashed before creating a
                         // recents entry. Let's see if we have a safe-to-restart intent.
-                        if (task.intent.getCategories().contains(
-                                Intent.CATEGORY_LAUNCHER)) {
+                        final Set<String> cats = task.intent.getCategories();
+                        if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
                             mService.startActivityInPackage(task.mCallingUid,
                                     task.mCallingPackage, task.intent,
                                     null, null, null, 0, 0,
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 6f67b6f..92b55db 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -69,6 +69,7 @@
 import com.android.internal.util.StateMachine;
 import com.android.server.IoThread;
 import com.android.server.connectivity.tethering.IControlsTethering;
+import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -1143,7 +1144,8 @@
         // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList
         // so that the garbage collector does not clean up the state machine before it has a chance
         // to tear itself down.
-        private ArrayList<TetherInterfaceStateMachine> mNotifyList;
+        private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+        private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
 
         private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
         private NetworkCallback mMobileUpstreamCallback;
@@ -1171,6 +1173,7 @@
             addState(mSetDnsForwardersErrorState);
 
             mNotifyList = new ArrayList<>();
+            mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList);
             setInitialState(mInitialState);
         }
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
new file mode 100644
index 0000000..8254397
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.net.ConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.util.Log;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+
+
+/**
+ * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
+ * This coordinator is responsible for evaluating the dedicated prefixes
+ * assigned to the device and deciding how to divvy them up among downstream
+ * interfaces.
+ *
+ * @hide
+ */
+public class IPv6TetheringCoordinator {
+    private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
+    private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+    private NetworkState mUpstreamNetworkState;
+
+    public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) {
+        mNotifyList = notifyList;
+    }
+
+    public void updateUpstreamNetworkState(NetworkState ns) {
+        if (VDBG) {
+            Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
+        }
+        if (ns == null || ns.network == null) {
+            stopIPv6TetheringOnAllInterfaces();
+            setUpstreamNetworkState(null);
+            return;
+        }
+
+        if (mUpstreamNetworkState != null &&
+            !ns.network.equals(mUpstreamNetworkState.network)) {
+            stopIPv6TetheringOnAllInterfaces();
+        }
+        setUpstreamNetworkState(ns);
+        maybeUpdateIPv6TetheringInterfaces();
+    }
+
+    private void stopIPv6TetheringOnAllInterfaces() {
+        for (TetherInterfaceStateMachine sm : mNotifyList) {
+            sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE,
+                    0, 0, null);
+        }
+    }
+
+    private void setUpstreamNetworkState(NetworkState ns) {
+        if (!canTetherIPv6(ns)) {
+            mUpstreamNetworkState = null;
+        } else {
+            mUpstreamNetworkState = new NetworkState(
+                    null,
+                    new LinkProperties(ns.linkProperties),
+                    new NetworkCapabilities(ns.networkCapabilities),
+                    new Network(ns.network),
+                    null,
+                    null);
+        }
+
+        if (DBG) {
+            Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
+        }
+    }
+
+    private void maybeUpdateIPv6TetheringInterfaces() {
+        if (mUpstreamNetworkState == null) return;
+
+        for (TetherInterfaceStateMachine sm : mNotifyList) {
+            final LinkProperties lp = getInterfaceIPv6LinkProperties(sm.interfaceType());
+            if (lp != null) {
+                sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+            }
+            break;
+        }
+    }
+
+    private LinkProperties getInterfaceIPv6LinkProperties(int interfaceType) {
+        // NOTE: Here, in future, we would have policies to decide how to divvy
+        // up the available dedicated prefixes among downstream interfaces.
+        // At this time we have no such mechanism--we only support tethering
+        // IPv6 toward Wi-Fi interfaces.
+
+        switch (interfaceType) {
+            case ConnectivityManager.TETHERING_WIFI:
+                final LinkProperties lp = getIPv6OnlyLinkProperties(
+                        mUpstreamNetworkState.linkProperties);
+                if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
+                    return lp;
+                }
+                break;
+        }
+
+        return null;
+    }
+
+    private static boolean canTetherIPv6(NetworkState ns) {
+        // Broadly speaking:
+        //
+        //     [1] does the upstream have an IPv6 default route?
+        //
+        // and
+        //
+        //     [2] does the upstream have one or more global IPv6 /64s
+        //         dedicated to this device?
+        //
+        // In lieu of Prefix Delegation and other evaluation of whether a
+        // prefix may or may not be dedicated to this device, for now just
+        // check whether the upstream is TRANSPORT_CELLULAR. This works
+        // because "[t]he 3GPP network allocates each default bearer a unique
+        // /64 prefix", per RFC 6459, Section 5.2.
+
+        final boolean canTether =
+                (ns != null) && (ns.network != null) &&
+                (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
+                // At least one upstream DNS server:
+                ns.linkProperties.isProvisioned() &&
+                // Minimal amount of IPv6 provisioning:
+                ns.linkProperties.hasIPv6DefaultRoute() &&
+                ns.linkProperties.hasGlobalIPv6Address() &&
+                // Temporary approximation of "dedicated prefix":
+                ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+        // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
+        // tethering with 464xlat involved). TODO: Rectify this shortcoming,
+        // likely by calling NetworkManagementService#startInterfaceForwarding()
+        // for all upstream interfaces.
+        RouteInfo v4default = null;
+        RouteInfo v6default = null;
+        if (canTether) {
+            for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
+                if (r.isIPv4Default()) {
+                    v4default = r;
+                } else if (r.isIPv6Default()) {
+                    v6default = r;
+                }
+
+                if (v4default != null && v6default != null) {
+                    break;
+                }
+            }
+        }
+
+        final boolean supportedConfiguration =
+                (v4default != null) && (v6default != null) &&
+                (v4default.getInterface() != null) &&
+                v4default.getInterface().equals(v6default.getInterface());
+
+        final boolean outcome = canTether && supportedConfiguration;
+
+        if (VDBG) {
+            if (ns == null) {
+                Log.d(TAG, "No available upstream.");
+            } else {
+                Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s",
+                        (outcome ? "available" : "not available"), toDebugString(ns)));
+            }
+        }
+
+        return outcome;
+    }
+
+    private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
+        final LinkProperties v6only = new LinkProperties();
+        if (lp == null) {
+            return v6only;
+        }
+
+        // NOTE: At this time we don't copy over any information about any
+        // stacked links. No current stacked link configuration has IPv6.
+
+        v6only.setInterfaceName(lp.getInterfaceName());
+
+        v6only.setMtu(lp.getMtu());
+
+        for (LinkAddress linkAddr : lp.getLinkAddresses()) {
+            if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
+                v6only.addLinkAddress(linkAddr);
+            }
+        }
+
+        for (RouteInfo routeInfo : lp.getRoutes()) {
+            final IpPrefix destination = routeInfo.getDestination();
+            if ((destination.getAddress() instanceof Inet6Address) &&
+                (destination.getPrefixLength() <= 64)) {
+                v6only.addRoute(routeInfo);
+            }
+        }
+
+        for (InetAddress dnsServer : lp.getDnsServers()) {
+            if (isIPv6GlobalAddress(dnsServer)) {
+                // For now we include ULAs.
+                v6only.addDnsServer(dnsServer);
+            }
+        }
+
+        v6only.setDomains(lp.getDomains());
+
+        return v6only;
+    }
+
+    // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
+    // announce our own IPv6 address as DNS server.
+    private static boolean isIPv6GlobalAddress(InetAddress ip) {
+        return (ip instanceof Inet6Address) &&
+               !ip.isAnyLocalAddress() &&
+               !ip.isLoopbackAddress() &&
+               !ip.isLinkLocalAddress() &&
+               !ip.isSiteLocalAddress() &&
+               !ip.isMulticastAddress();
+    }
+
+    private static String toDebugString(NetworkState ns) {
+        if (ns == null) {
+            return "NetworkState{null}";
+        }
+        return String.format("NetworkState{%s, %s, %s}",
+                ns.network,
+                ns.networkCapabilities,
+                ns.linkProperties);
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
new file mode 100644
index 0000000..b742838
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.ip.RouterAdvertisementDaemon.RaParams;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.HashSet;
+
+
+/**
+ * @hide
+ */
+class IPv6TetheringInterfaceServices {
+    private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
+
+    private final String mIfName;
+    private final INetworkManagementService mNMService;
+
+    private NetworkInterface mNetworkInterface;
+    private byte[] mHwAddr;
+    private ArrayList<RouteInfo> mLastLocalRoutes;
+    private RouterAdvertisementDaemon mRaDaemon;
+    private RaParams mLastRaParams;
+
+    IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
+        mIfName = ifname;
+        mNMService = nms;
+    }
+
+    public boolean start() {
+        try {
+            mNetworkInterface = NetworkInterface.getByName(mIfName);
+        } catch (SocketException e) {
+            Log.e(TAG, "Failed to find NetworkInterface for " + mIfName, e);
+            stop();
+            return false;
+        }
+
+        try {
+            mHwAddr = mNetworkInterface.getHardwareAddress();
+        } catch (SocketException e) {
+            Log.e(TAG, "Failed to find hardware address for " + mIfName, e);
+            stop();
+            return false;
+        }
+
+        final int ifindex = mNetworkInterface.getIndex();
+        mRaDaemon = new RouterAdvertisementDaemon(mIfName, ifindex, mHwAddr);
+        if (!mRaDaemon.start()) {
+            stop();
+            return false;
+        }
+
+        return true;
+    }
+
+    public void stop() {
+        mNetworkInterface = null;
+        mHwAddr = null;
+        updateLocalRoutes(null);
+        updateRaParams(null);
+
+        if (mRaDaemon != null) {
+            mRaDaemon.stop();
+            mRaDaemon = null;
+        }
+    }
+
+    // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
+    // LinkProperties. These have extraneous data filtered out and only the
+    // necessary prefixes included (per its prefix distribution policy).
+    //
+    // TODO: Evaluate using a data structure than is more directly suited to
+    // communicating only the relevant information.
+    public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
+        if (mRaDaemon == null) return;
+
+        if (v6only == null) {
+            updateLocalRoutes(null);
+            updateRaParams(null);
+            return;
+        }
+
+        RaParams params = new RaParams();
+        params.mtu = v6only.getMtu();
+        params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
+
+        ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
+        for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
+            final IpPrefix prefix = new IpPrefix(linkAddr.getAddress(),
+                                                 linkAddr.getPrefixLength());
+
+            // Accumulate routes representing "prefixes to be assigned to the
+            // local interface", for subsequent addition to the local network
+            // in the routing rules.
+            localRoutes.add(new RouteInfo(prefix, null, mIfName));
+
+            params.prefixes.add(prefix);
+        }
+
+        // We need to be able to send unicast RAs, and clients might like to
+        // ping the default router's link-local address, so add that as well.
+        localRoutes.add(new RouteInfo(new IpPrefix("fe80::/64"), null, mIfName));
+
+        // TODO: Add a local interface address, update dnsmasq to listen on the
+        // new address, and use only that address as a DNS server.
+        for (InetAddress dnsServer : v6only.getDnsServers()) {
+            if (dnsServer instanceof Inet6Address) {
+                params.dnses.add((Inet6Address) dnsServer);
+            }
+        }
+
+        updateLocalRoutes(localRoutes);
+        updateRaParams(params);
+    }
+
+    private void updateLocalRoutes(ArrayList<RouteInfo> localRoutes) {
+        if (localRoutes != null) {
+            // TODO: Compare with mLastLocalRoutes and take appropriate
+            // appropriate action on the difference between the two.
+
+            if (!localRoutes.isEmpty()) {
+                try {
+                    mNMService.addInterfaceToLocalNetwork(mIfName, localRoutes);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
+                }
+            }
+        } else {
+            if (mLastLocalRoutes != null && !mLastLocalRoutes.isEmpty()) {
+                // TODO: Remove only locally added network routes.
+                // mNMSwervice.removeInterfaceFromLocalNetwork(mIfName);
+            }
+        }
+
+        mLastLocalRoutes = localRoutes;
+    }
+
+    private void updateRaParams(RaParams params) {
+        if (mRaDaemon != null) {
+            // Currently, we send spurious RAs (5) whenever there's any update.
+            // TODO: Compare params with mLastParams to avoid spurious updates.
+            mRaDaemon.buildNewRa(params);
+        }
+
+        mLastRaParams = params;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index aebeb69..9e7cb93 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -20,6 +20,7 @@
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.os.INetworkManagementService;
 import android.os.Looper;
@@ -73,6 +74,8 @@
     public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
     // the upstream connection has changed
     public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
+    // new IPv6 tethering parameters need to be processed
+    public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
 
     private final State mInitialState;
     private final State mTetheredState;
@@ -84,6 +87,7 @@
 
     private final String mIfaceName;
     private final int mInterfaceType;
+    private final IPv6TetheringInterfaceServices mIPv6TetherSvc;
 
     private int mLastError;
     private String mMyUpstreamIfaceName;  // may change over time
@@ -97,6 +101,7 @@
         mTetherController = tetherController;
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
+        mIPv6TetherSvc = new IPv6TetheringInterfaceServices(mIfaceName, mNMService);
         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
 
         mInitialState = new InitialState();
@@ -109,6 +114,10 @@
         setInitialState(mInitialState);
     }
 
+    public int interfaceType() {
+        return mInterfaceType;
+    }
+
     // configured when we start tethering and unconfig'd on error or conclusion
     private boolean configureIfaceIp(boolean enabled) {
         if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
@@ -175,6 +184,10 @@
                 case CMD_INTERFACE_DOWN:
                     transitionTo(mUnavailableState);
                     break;
+                case CMD_IPV6_TETHER_UPDATE:
+                    mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
+                            (LinkProperties) message.obj);
+                    break;
                 default:
                     retValue = false;
                     break;
@@ -200,6 +213,11 @@
                 transitionTo(mInitialState);
                 return;
             }
+
+            if (!mIPv6TetherSvc.start()) {
+                Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
+            }
+
             if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
             mTetherController.notifyInterfaceStateChange(
                     mIfaceName, TetherInterfaceStateMachine.this,
@@ -211,6 +229,7 @@
             // Note that at this point, we're leaving the tethered state.  We can fail any
             // of these operations, but it doesn't really change that we have to try them
             // all in sequence.
+            mIPv6TetherSvc.stop();
             cleanupUpstream();
 
             try {
@@ -287,6 +306,10 @@
                     }
                     mMyUpstreamIfaceName = newUpstreamIfaceName;
                     break;
+                case CMD_IPV6_TETHER_UPDATE:
+                    mIPv6TetherSvc.updateUpstreamIPv6LinkProperties(
+                            (LinkProperties) message.obj);
+                    break;
                 case CMD_IP_FORWARDING_ENABLE_ERROR:
                 case CMD_IP_FORWARDING_DISABLE_ERROR:
                 case CMD_START_TETHERING_ERROR:
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 29c54e9..88d6c14 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -454,7 +454,7 @@
 
     @Override
     public String toString() {
-        return mPackageName + "/" + mTag;
+        return mPackageName + "/" + mTag + " (uid=" + mUserId + ")";
     }
 
     private void postAdjustLocalVolume(final int stream, final int direction, final int flags,
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a4d2cd2..a3f09c0 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -47,10 +47,12 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.speech.RecognizerIntent;
 import android.text.TextUtils;
@@ -67,6 +69,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -97,7 +100,9 @@
     private ContentResolver mContentResolver;
     private SettingsObserver mSettingsObserver;
 
-    private int mCurrentUserId = -1;
+    // List of user IDs running in the foreground.
+    // Multiple users can be in the foreground if the work profile is on.
+    private final List<Integer> mCurrentUserIdList = new ArrayList<>();
 
     // Used to notify system UI when remote volume was changed. TODO find a
     // better way to handle this.
@@ -181,22 +186,26 @@
     }
 
     @Override
-    public void onStartUser(int userHandle) {
+    public void onStartUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStartUser: " + userId);
         updateUser();
     }
 
     @Override
-    public void onSwitchUser(int userHandle) {
+    public void onSwitchUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onSwitchUser: " + userId);
         updateUser();
     }
 
     @Override
-    public void onStopUser(int userHandle) {
+    public void onStopUser(int userId) {
+        if (DEBUG) Log.d(TAG, "onStopUser: " + userId);
         synchronized (mLock) {
-            UserRecord user = mUserRecords.get(userHandle);
+            UserRecord user = mUserRecords.get(userId);
             if (user != null) {
                 destroyUserLocked(user);
             }
+            updateUser();
         }
     }
 
@@ -228,18 +237,23 @@
 
     private void updateUser() {
         synchronized (mLock) {
-            int userId = ActivityManager.getCurrentUser();
-            if (mCurrentUserId != userId) {
-                final int oldUserId = mCurrentUserId;
-                mCurrentUserId = userId; // do this first
-
-                UserRecord oldUser = mUserRecords.get(oldUserId);
-                if (oldUser != null) {
-                    oldUser.stopLocked();
+            UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+            int currentUser = ActivityManager.getCurrentUser();
+            int[] userIds = manager.getEnabledProfileIds(currentUser);
+            mCurrentUserIdList.clear();
+            if (userIds != null && userIds.length > 0) {
+                for (int userId : userIds) {
+                    mCurrentUserIdList.add(userId);
                 }
-
-                UserRecord newUser = getOrCreateUser(userId);
-                newUser.startLocked();
+            } else {
+                // This shouldn't happen.
+                Log.w(TAG, "Failed to get enabled profiles.");
+                mCurrentUserIdList.add(currentUser);
+            }
+            for (int userId : mCurrentUserIdList) {
+                if (mUserRecords.get(userId) == null) {
+                    mUserRecords.put(userId, new UserRecord(getContext(), userId));
+                }
             }
         }
     }
@@ -272,7 +286,6 @@
      * @param user The user to dispose of
      */
     private void destroyUserLocked(UserRecord user) {
-        user.stopLocked();
         user.destroyLocked();
         mUserRecords.remove(user.mUserId);
     }
@@ -436,9 +449,9 @@
         }
 
         mAllSessions.add(session);
-        mPriorityStack.addSession(session);
+        mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId));
 
-        UserRecord user = getOrCreateUser(userId);
+        UserRecord user = mUserRecords.get(userId);
         user.addSessionLocked(session);
 
         mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
@@ -449,15 +462,6 @@
         return session;
     }
 
-    private UserRecord getOrCreateUser(int userId) {
-        UserRecord user = mUserRecords.get(userId);
-        if (user == null) {
-            user = new UserRecord(getContext(), userId);
-            mUserRecords.put(userId, user);
-        }
-        return user;
-    }
-
     private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
         for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
             if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
@@ -536,13 +540,6 @@
             restoreMediaButtonReceiver();
         }
 
-        public void startLocked() {
-        }
-
-        public void stopLocked() {
-            // nothing for now
-        }
-
         public void destroyLocked() {
             for (int i = mSessions.size() - 1; i >= 0; i--) {
                 MediaSessionRecord session = mSessions.get(i);
@@ -578,7 +575,7 @@
 
         private void restoreMediaButtonReceiver() {
             String receiverName = Settings.Secure.getStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
+                    Settings.System.MEDIA_BUTTON_RECEIVER, mUserId);
             if (!TextUtils.isEmpty(receiverName)) {
                 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
                 if (eventReceiver == null) {
@@ -767,12 +764,22 @@
                 synchronized (mLock) {
                     // If we don't have a media button receiver to fall back on
                     // include non-playing sessions for dispatching
-                    UserRecord ur = mUserRecords.get(mCurrentUserId);
-                    boolean useNotPlayingSessions = (ur == null) ||
-                            (ur.mLastMediaButtonReceiver == null
-                                && ur.mRestoredMediaButtonReceiver == null);
-                    MediaSessionRecord session = mPriorityStack
-                            .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
+                    boolean useNotPlayingSessions = true;
+                    for (int userId : mCurrentUserIdList) {
+                        UserRecord ur = mUserRecords.get(userId);
+                        if (ur.mLastMediaButtonReceiver != null
+                                || ur.mRestoredMediaButtonReceiver != null) {
+                            useNotPlayingSessions = false;
+                            break;
+                        }
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "dispatchMediaKeyEvent, useNotPlayingSessions="
+                                + useNotPlayingSessions);
+                    }
+                    MediaSessionRecord session = mPriorityStack.getDefaultMediaButtonSession(
+                            mCurrentUserIdList, useNotPlayingSessions);
                     if (isVoiceKey(keyEvent.getKeyCode())) {
                         handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
                     } else {
@@ -786,13 +793,11 @@
 
         @Override
         public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
-            final int pid = Binder.getCallingPid();
-            final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     MediaSessionRecord session = mPriorityStack
-                            .getDefaultVolumeSession(mCurrentUserId);
+                            .getDefaultVolumeSession(mCurrentUserIdList);
                     dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
                 }
             } finally {
@@ -899,7 +904,7 @@
                 }
             } else {
                 session.adjustVolume(direction, flags, getContext().getPackageName(),
-                        UserHandle.myUserId(), true);
+                        Process.SYSTEM_UID, true);
             }
         }
 
@@ -946,13 +951,16 @@
                 // won't release it later
                 session.sendMediaButton(keyEvent,
                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                        mKeyEventReceiver, getContext().getApplicationInfo().uid,
+                        mKeyEventReceiver, Process.SYSTEM_UID,
                         getContext().getPackageName());
             } else {
                 // Launch the last PendingIntent we had with priority
-                UserRecord user = mUserRecords.get(mCurrentUserId);
-                if (user != null && (user.mLastMediaButtonReceiver != null
-                        || user.mRestoredMediaButtonReceiver != null)) {
+                for (int userId : mCurrentUserIdList) {
+                    UserRecord user = mUserRecords.get(userId);
+                    if (user.mLastMediaButtonReceiver == null
+                            && user.mRestoredMediaButtonReceiver == null) {
+                        continue;
+                    }
                     if (DEBUG) {
                         Log.d(TAG, "Sending media key to last known PendingIntent "
                                 + user.mLastMediaButtonReceiver + " or restored Intent "
@@ -972,30 +980,30 @@
                         } else {
                             mediaButtonIntent.setComponent(user.mRestoredMediaButtonReceiver);
                             getContext().sendBroadcastAsUser(mediaButtonIntent,
-                                    new UserHandle(mCurrentUserId));
+                                    UserHandle.of(userId));
                         }
                     } catch (CanceledException e) {
                         Log.i(TAG, "Error sending key event to media button receiver "
                                 + user.mLastMediaButtonReceiver, e);
                     }
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, "Sending media key ordered broadcast");
-                    }
-                    if (needWakeLock) {
-                        mMediaEventWakeLock.acquire();
-                    }
-                    // Fallback to legacy behavior
-                    Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
-                    keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                    keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-                    if (needWakeLock) {
-                        keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
-                                WAKELOCK_RELEASE_ON_FINISHED);
-                    }
-                    getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
-                            null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
+                    return;
                 }
+                if (DEBUG) {
+                    Log.d(TAG, "Sending media key ordered broadcast");
+                }
+                if (needWakeLock) {
+                    mMediaEventWakeLock.acquire();
+                }
+                // Fallback to legacy behavior
+                Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+                if (needWakeLock) {
+                    keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+                }
+                // Send broadcast only to the full user.
+                getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.CURRENT,
+                        null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
             }
         }
 
@@ -1025,6 +1033,7 @@
                 if (voiceIntent != null) {
                     voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                             | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    if (DEBUG) Log.d(TAG, "voiceIntent: " + voiceIntent);
                     getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
                 }
             } catch (ActivityNotFoundException e) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index cc007ef..3327b36 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -32,7 +32,7 @@
  * Keeps track of media sessions and their priority for notifications, media
  * button dispatch, etc.
  */
-public class MediaSessionStack {
+class MediaSessionStack {
     /**
      * These are states that usually indicate the user took an action and should
      * bump priority regardless of the old state.
@@ -68,13 +68,10 @@
      * Checks if a media session is created from the most recent app.
      *
      * @param record A media session record to be examined.
-     * @return true if the media session's package name equals to the most recent app, false
-     * otherwise.
+     * @return {@code true} if the media session's package name equals to the most recent app, false
+     *            otherwise.
      */
     private static boolean isFromMostRecentApp(MediaSessionRecord record) {
-        if (ActivityManager.getCurrentUser() != record.getUserId()) {
-            return false;
-        }
         try {
             List<ActivityManager.RecentTaskInfo> tasks =
                     ActivityManagerNative.getDefault().getRecentTasks(1,
@@ -84,9 +81,10 @@
                             ActivityManager.RECENT_WITH_EXCLUDED, record.getUserId()).getList();
             if (tasks != null && !tasks.isEmpty()) {
                 ActivityManager.RecentTaskInfo recentTask = tasks.get(0);
-                if (recentTask.baseIntent != null)
+                if (recentTask.userId == record.getUserId() && recentTask.baseIntent != null) {
                     return recentTask.baseIntent.getComponent().getPackageName()
                             .equals(record.getPackageName());
+                }
             }
         } catch (RemoteException e) {
             return false;
@@ -98,11 +96,12 @@
      * Add a record to the priority tracker.
      *
      * @param record The record to add.
+     * @param fromForegroundUser {@code true} if the session is created by the foreground user.
      */
-    public void addSession(MediaSessionRecord record) {
+    public void addSession(MediaSessionRecord record, boolean fromForegroundUser) {
         mSessions.add(record);
         clearCache();
-        if (isFromMostRecentApp(record)) {
+        if (fromForegroundUser && isFromMostRecentApp(record)) {
             mLastInterestingRecord = record;
         }
     }
@@ -210,12 +209,13 @@
     /**
      * Get the highest priority session that can handle media buttons.
      *
-     * @param userId The user to check.
+     * @param userIdList The user lists to check.
      * @param includeNotPlaying Return a non-playing session if nothing else is
      *            available
      * @return The default media button session or null.
      */
-    public MediaSessionRecord getDefaultMediaButtonSession(int userId, boolean includeNotPlaying) {
+    public MediaSessionRecord getDefaultMediaButtonSession(
+            List<Integer> userIdList, boolean includeNotPlaying) {
         if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
             return mGlobalPrioritySession;
         }
@@ -223,7 +223,7 @@
             return mCachedButtonReceiver;
         }
         ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
-                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId);
+                MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userIdList);
         if (records.size() > 0) {
             MediaSessionRecord record = records.get(0);
             if (record.isPlaybackActive(false)) {
@@ -248,14 +248,14 @@
         return mCachedButtonReceiver;
     }
 
-    public MediaSessionRecord getDefaultVolumeSession(int userId) {
+    public MediaSessionRecord getDefaultVolumeSession(List<Integer> userIdList) {
         if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
             return mGlobalPrioritySession;
         }
         if (mCachedVolumeDefault != null) {
             return mCachedVolumeDefault;
         }
-        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
+        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userIdList);
         int size = records.size();
         for (int i = 0; i < size; i++) {
             MediaSessionRecord record = records.get(i);
@@ -298,6 +298,13 @@
         }
     }
 
+    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
+            int userId) {
+        List<Integer> userIdList = new ArrayList<>();
+        userIdList.add(userId);
+        return getPriorityListLocked(activeOnly, withFlags, userIdList);
+    }
+
     /**
      * Get a priority sorted list of sessions. Can filter to only return active
      * sessions or sessions with specific flags.
@@ -306,22 +313,23 @@
      *            all sessions.
      * @param withFlags Only return sessions with all the specified flags set. 0
      *            returns all sessions.
-     * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
+     * @param userIdList The user to get sessions for. {@link UserHandle#USER_ALL}
      *            will return sessions for all users.
      * @return The priority sorted list of sessions.
      */
     private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
-            int userId) {
+            List<Integer> userIdList) {
         ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
         int lastLocalIndex = 0;
         int lastActiveIndex = 0;
         int lastPublishedIndex = 0;
 
+        boolean filterUser = !userIdList.contains(UserHandle.USER_ALL);
         int size = mSessions.size();
         for (int i = 0; i < size; i++) {
             final MediaSessionRecord session = mSessions.get(i);
 
-            if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
+            if (filterUser && !userIdList.contains(session.getUserId())) {
                 // Filter out sessions for the wrong user
                 continue;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ec77daf..8d19a24 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1851,7 +1851,7 @@
             enforceSystemOrSystemUIOrVolume("INotificationManager.setZenMode");
             final long identity = Binder.clearCallingIdentity();
             try {
-                mZenModeHelper.setManualZenMode(mode, conditionId, reason);
+                mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -1928,7 +1928,7 @@
             if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
             final long identity = Binder.clearCallingIdentity();
             try {
-                mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter");
+                mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 6864ed8..8c9dc3b 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -54,7 +54,6 @@
 import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenModeConfig.ZenRule;
-import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 import android.util.SparseArray;
@@ -229,7 +228,7 @@
     public void requestFromListener(ComponentName name, int filter) {
         final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
         if (newZen != -1) {
-            setManualZenMode(newZen, null,
+            setManualZenMode(newZen, null, name != null ? name.getPackageName() : null,
                     "listener:" + (name != null ? name.flattenToShortString() : null));
         }
     }
@@ -452,11 +451,11 @@
                 rule.creationTime);
     }
 
-    public void setManualZenMode(int zenMode, Uri conditionId, String reason) {
-        setManualZenMode(zenMode, conditionId, reason, true /*setRingerMode*/);
+    public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
+        setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/);
     }
 
-    private void setManualZenMode(int zenMode, Uri conditionId, String reason,
+    private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
             boolean setRingerMode) {
         ZenModeConfig newConfig;
         synchronized (mConfig) {
@@ -478,6 +477,7 @@
                 newRule.enabled = true;
                 newRule.zenMode = zenMode;
                 newRule.conditionId = conditionId;
+                newRule.enabler = caller;
                 newConfig.manualRule = newRule;
             }
             setConfigLocked(newConfig, reason, setRingerMode);
@@ -950,7 +950,8 @@
                     break;
             }
             if (newZen != -1) {
-                setManualZenMode(newZen, null, "ringerModeInternal", false /*setRingerMode*/);
+                setManualZenMode(newZen, null, "ringerModeInternal", null,
+                        false /*setRingerMode*/);
             }
 
             if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
@@ -988,7 +989,8 @@
                     break;
             }
             if (newZen != -1) {
-                setManualZenMode(newZen, null, "ringerModeExternal", false /*setRingerMode*/);
+                setManualZenMode(newZen, null, "ringerModeExternal", caller,
+                        false /*setRingerMode*/);
             }
 
             ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller,
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 098b39e..f7638a1 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -619,6 +619,7 @@
             grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
             grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
             grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
+            grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, userId);
         }
     }
 
@@ -656,6 +657,7 @@
             grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
             grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, false, true, userId);
             grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 03d5645f..cff2da9 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -19,19 +19,18 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
-import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ILauncherApps;
 import android.content.pm.IOnAppsChangedListener;
 import android.content.pm.IPackageManager;
+import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -439,7 +438,7 @@
         }
 
         @Override
-        public void startShortcut(String callingPackage, String packageName, String shortcutId,
+        public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
                 Rect sourceBounds, Bundle startActivityOptions, int userId) {
             verifyCallingPackage(callingPackage);
             ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
@@ -458,39 +457,36 @@
             final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
                     callingPackage, packageName, shortcutId, userId);
             if (intent == null) {
-                return;
+                return false;
             }
             // Note the target activity doesn't have to be exported.
 
             prepareIntentForLaunch(intent, sourceBounds);
 
-            startShortcutIntentAsPublisher(
+            return startShortcutIntentAsPublisher(
                     intent, packageName, startActivityOptions, userId);
         }
 
-        @VisibleForTesting
-        protected void startShortcutIntentAsPublisher(@NonNull Intent intent,
+        private boolean startShortcutIntentAsPublisher(@NonNull Intent intent,
                 @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
-
+            final int code;
+            final long ident = injectClearCallingIdentity();
             try {
-                final IIntentSender intentSender;
-
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    intentSender = mActivityManagerInternal.getActivityIntentSenderAsPackage(
-                            publisherPackage, userId, /* requestCode= */ 0,
-                            intent, PendingIntent.FLAG_ONE_SHOT,
-                            /* options= */ startActivityOptions);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
+                code = mActivityManagerInternal.startActivityAsPackage(publisherPackage,
+                        userId, intent, startActivityOptions);
+                if (code >= ActivityManager.START_SUCCESS) {
+                    return true; // Success
+                } else {
+                    Log.e(TAG, "Couldn't start activity, code=" + code);
                 }
-
-                // Negative result means a failure.
-                ActivityManagerNative.getDefault().sendIntentSender(
-                        intentSender, 0, null, null, null, null, null);
-
-            } catch (RemoteException e) {
-                return;
+                return code >= ActivityManager.START_SUCCESS;
+            } catch (SecurityException e) {
+                if (DEBUG) {
+                    Slog.d(TAG, "SecurityException while launching intent", e);
+                }
+                return false;
+            } finally {
+                injectRestoreCallingIdentity(ident);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 83af017..a3cf9c8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -666,23 +666,26 @@
                         "Too many historical sessions for UID " + callingUid);
             }
 
-            final long createdMillis = System.currentTimeMillis();
             sessionId = allocateSessionIdLocked();
+        }
 
-            // We're staging to exactly one location
-            File stageDir = null;
-            String stageCid = null;
-            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
-                final boolean isEphemeral =
-                        (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
-                stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
-            } else {
-                stageCid = buildExternalStageCid(sessionId);
-            }
+        final long createdMillis = System.currentTimeMillis();
+        // We're staging to exactly one location
+        File stageDir = null;
+        String stageCid = null;
+        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+            final boolean isEphemeral =
+                    (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
+            stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
+        } else {
+            stageCid = buildExternalStageCid(sessionId);
+        }
 
-            session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
-                    mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
-                    params, createdMillis, stageDir, stageCid, false, false);
+        session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
+                mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
+                params, createdMillis, stageDir, stageCid, false, false);
+
+        synchronized (mSessions) {
             mSessions.put(sessionId, session);
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 455e1fa..5980715 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -159,7 +159,6 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
@@ -11439,9 +11438,6 @@
                     } else {
                         resolvedUserIds = userIds;
                     }
-                    final ShortcutServiceInternal shortcutService =
-                            LocalServices.getService(ShortcutServiceInternal.class);
-
                     for (int id : resolvedUserIds) {
                         final Intent intent = new Intent(action,
                                 pkg != null ? Uri.fromParts("package", pkg, null) : null);
@@ -11466,10 +11462,6 @@
                                     + intent.toShortString(false, true, false, false)
                                     + " " + intent.getExtras(), here);
                         }
-                        // TODO b/29385425 Consider making lifecycle callbacks for this.
-                        if (shortcutService != null) {
-                            shortcutService.onPackageBroadcast(intent);
-                        }
                         am.broadcastIntent(null, intent, null, finishedReceiver,
                                 0, null, null, null, android.app.AppOpsManager.OP_NONE,
                                 null, finishedReceiver != null, false, id);
@@ -20443,12 +20435,7 @@
         }
     }
 
-    void onBeforeUserStartUninitialized(final int userId) {
-        synchronized (mPackages) {
-            if (mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
-                return;
-            }
-        }
+    void onNewUserCreated(final int userId) {
         mDefaultPermissionPolicy.grantDefaultPermissions(userId);
         // If permission review for legacy apps is required, we represent
         // dagerous permissions for such apps as always granted runtime
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1a4e4e0..b94d0f0 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1185,8 +1185,8 @@
     private static void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup)
             throws IOException, XmlPullParserException {
         if (forBackup) {
-            if (!si.isPinned()) {
-                return; // Backup only pinned icons.
+            if (!(si.isPinned() && si.isEnabled())) {
+                return; // We only backup pinned shortcuts that are enabled.
             }
         }
         out.startTag(null, TAG_SHORTCUT);
diff --git a/services/core/java/com/android/server/pm/ShortcutPendingTasks.java b/services/core/java/com/android/server/pm/ShortcutPendingTasks.java
deleted file mode 100644
index a5ace56..0000000
--- a/services/core/java/com/android/server/pm/ShortcutPendingTasks.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.pm;
-
-import android.annotation.NonNull;
-import android.util.Slog;
-
-import java.io.PrintWriter;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.BooleanSupplier;
-import java.util.function.Consumer;
-import java.util.logging.Handler;
-
-/**
- * Used by {@link ShortcutService} to register tasks to be executed on Handler and also wait for
- * all pending tasks.
- *
- * Tasks can be registered with {@link #addTask(Runnable)}.  Call {@link #waitOnAllTasks()} to wait
- * on all tasks that have been registered.
- *
- * In order to avoid deadlocks, {@link #waitOnAllTasks} MUST NOT be called with any lock held, nor
- * on the handler thread.  These conditions are checked by {@link #mWaitThreadChecker} and wtf'ed.
- *
- * During unit tests, we can't run tasks asynchronously, so we just run Runnables synchronously,
- * which also means the "is lock held" check doesn't work properly during unit tests (e.g. normally
- * when a Runnable is executed on a Handler, the thread doesn't hold any lock, but during the tests
- * we just run a Runnable on the thread that registers it, so the thread may or may not hold locks.)
- * So unfortunately we have to disable {@link #mWaitThreadChecker} during unit tests.
- *
- * Because of the complications like those, this class should be used only for specific purposes:
- * - {@link #addTask(Runnable)} should only be used to register tasks on callbacks from lower level
- * services like the package manager or the activity manager.
- *
- * - {@link #waitOnAllTasks} should only be called at the entry point of RPC calls (or the test only
- * accessors}.
- */
-public class ShortcutPendingTasks {
-    private static final String TAG = "ShortcutPendingTasks";
-
-    private static final boolean DEBUG = false || ShortcutService.DEBUG; // DO NOT SUBMIT WITH TRUE.
-
-    private final Consumer<Runnable> mRunner;
-
-    private final BooleanSupplier mWaitThreadChecker;
-
-    private final Consumer<Throwable> mExceptionHandler;
-
-    /** # of tasks in the queue, including the running one. */
-    private final AtomicInteger mRunningTaskCount = new AtomicInteger();
-
-    /** For dumpsys */
-    private final AtomicLong mLastTaskStartTime = new AtomicLong();
-
-    /**
-     * Constructor.  In order to allow injection during unit tests, it doesn't take a
-     * {@link Handler} directly, and instead takes {@code runner} which will post an argument
-     * to a handler.
-     */
-    public ShortcutPendingTasks(Consumer<Runnable> runner, BooleanSupplier waitThreadChecker,
-            Consumer<Throwable> exceptionHandler) {
-        mRunner = runner;
-        mWaitThreadChecker = waitThreadChecker;
-        mExceptionHandler = exceptionHandler;
-    }
-
-    private static void dlog(String message) {
-        if (DEBUG) {
-            Slog.d(TAG, message);
-        }
-    }
-
-    /**
-     * Block until all tasks that are already queued finish.  DO NOT call it while holding any lock
-     * or on the handler thread.
-     */
-    public boolean waitOnAllTasks() {
-        dlog("waitOnAllTasks: enter");
-        try {
-            // Make sure it's not holding the lock.
-            if (!mWaitThreadChecker.getAsBoolean()) {
-                return false;
-            }
-
-            // Optimize for the no-task case.
-            if (mRunningTaskCount.get() == 0) {
-                return true;
-            }
-
-            final CountDownLatch latch = new CountDownLatch(1);
-
-            addTask(latch::countDown);
-
-            for (; ; ) {
-                try {
-                    if (latch.await(1, TimeUnit.SECONDS)) {
-                        return true;
-                    }
-                    dlog("waitOnAllTasks: Task(s) still running...");
-                } catch (InterruptedException ignore) {
-                }
-            }
-        } finally {
-            dlog("waitOnAllTasks: exit");
-        }
-    }
-
-    /**
-     * Add a new task.  This operation is lock-free.
-     */
-    public void addTask(Runnable task) {
-        mRunningTaskCount.incrementAndGet();
-        mLastTaskStartTime.set(System.currentTimeMillis());
-
-        dlog("Task registered");
-
-        mRunner.accept(() -> {
-            try {
-                dlog("Task started");
-
-                task.run();
-            } catch (Throwable th) {
-                mExceptionHandler.accept(th);
-            } finally {
-                dlog("Task finished");
-                mRunningTaskCount.decrementAndGet();
-            }
-        });
-    }
-
-    public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
-        pw.print(prefix);
-        pw.print("Pending tasks:  # running tasks: ");
-        pw.println(mRunningTaskCount.get());
-
-        pw.print(prefix);
-        pw.print("  Last task started time: ");
-        final long lastStarted = mLastTaskStartTime.get();
-        pw.print(" [");
-        pw.print(lastStarted);
-        pw.print("] ");
-        pw.println(ShortcutService.formatTime(lastStarted));
-    }
-}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index dda9a5d..5f8cbbf 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -49,7 +49,6 @@
 import android.graphics.Canvas;
 import android.graphics.RectF;
 import android.graphics.drawable.Icon;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -82,6 +81,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
@@ -122,6 +122,9 @@
 
 /**
  * TODO:
+ * - Deal with the async nature of PACKAGE_ADD.  Basically when a publisher does anything after
+ *   it's upgraded, the manager should make sure the upgrade process has been executed.
+ *
  * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
  *   -> But TypedValue.applyDimension() doesn't differentiate x and y..?
  *
@@ -301,8 +304,6 @@
 
     private final AtomicBoolean mBootCompleted = new AtomicBoolean();
 
-    private final ShortcutPendingTasks mPendingTasks;
-
     private static final int PACKAGE_MATCH_FLAGS =
             PackageManager.MATCH_DIRECT_BOOT_AWARE
                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -376,41 +377,16 @@
         mUsageStatsManagerInternal = Preconditions.checkNotNull(
                 LocalServices.getService(UsageStatsManagerInternal.class));
 
-        mPendingTasks = new ShortcutPendingTasks(
-                this::injectPostToHandler,
-                this::injectCheckPendingTaskWaitThread,
-                throwable -> wtf(throwable.getMessage(), throwable));
-
         if (onlyForPackageManagerApis) {
             return; // Don't do anything further.  For unit tests only.
         }
 
+        mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
+
         injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
                 | ActivityManager.UID_OBSERVER_GONE);
     }
 
-    /**
-     * Check whether {@link ShortcutPendingTasks#waitOnAllTasks()} can be called on the current
-     * thread.
-     *
-     * During unit tests, all tasks are executed synchronously which makes the lock held check would
-     * misfire, so we override this method to always return true.
-     */
-    @VisibleForTesting
-    boolean injectCheckPendingTaskWaitThread() {
-        // We shouldn't wait while holding mLock.  We should never do this so wtf().
-        if (Thread.holdsLock(mLock)) {
-            wtf("waitOnAllTasks() called while holding the lock");
-            return false;
-        }
-        // This shouldn't be called on the handler thread either.
-        if (Thread.currentThread() == mHandler.getLooper().getThread()) {
-            wtf("waitOnAllTasks() called on handler thread");
-            return false;
-        }
-        return true;
-    }
-
     void logDurationStat(int statId, long start) {
         synchronized (mStatLock) {
             mCountStats[statId]++;
@@ -1516,8 +1492,6 @@
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
-        mPendingTasks.waitOnAllTasks();
-
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
         final int size = newShortcuts.size();
 
@@ -1567,8 +1541,6 @@
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
-        mPendingTasks.waitOnAllTasks();
-
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
         final int size = newShortcuts.size();
 
@@ -1647,8 +1619,6 @@
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
-        mPendingTasks.waitOnAllTasks();
-
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
         final int size = newShortcuts.size();
 
@@ -1699,8 +1669,6 @@
         verifyCaller(packageName, userId);
         Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
 
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
@@ -1728,8 +1696,6 @@
         verifyCaller(packageName, userId);
         Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
 
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
@@ -1750,8 +1716,6 @@
         verifyCaller(packageName, userId);
         Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
 
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
 
@@ -1774,8 +1738,6 @@
     public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
         }
@@ -1788,9 +1750,6 @@
     public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             return getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
@@ -1802,9 +1761,6 @@
     public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             return getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
@@ -1816,9 +1772,6 @@
     public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             return getShortcutsWithQueryLocked(
                     packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
@@ -1848,8 +1801,6 @@
     public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             return mMaxUpdatesPerInterval
                     - getPackageShortcutsLocked(packageName, userId).getApiCallCount();
@@ -1860,8 +1811,6 @@
     public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
-        mPendingTasks.waitOnAllTasks();
-
         synchronized (mLock) {
             return getNextResetTimeLocked();
         }
@@ -1880,8 +1829,6 @@
     public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
         verifyCaller(packageName, userId);
 
-        mPendingTasks.waitOnAllTasks();
-
         Preconditions.checkNotNull(shortcutId);
 
         if (DEBUG) {
@@ -1914,8 +1861,6 @@
     public void resetThrottling() {
         enforceSystemOrShell();
 
-        mPendingTasks.waitOnAllTasks();
-
         resetThrottlingInner(getCallingUserId());
     }
 
@@ -1948,9 +1893,6 @@
         if (DEBUG) {
             Slog.d(TAG, "onApplicationActive: package=" + packageName + "  userid=" + userId);
         }
-
-        mPendingTasks.waitOnAllTasks();
-
         enforceResetThrottlingPermission();
         resetPackageThrottling(packageName, userId);
     }
@@ -2113,14 +2055,6 @@
                 @Nullable String packageName, @Nullable List<String> shortcutIds,
                 @Nullable ComponentName componentName,
                 int queryFlags, int userId) {
-
-            // When this method is called from onShortcutChangedInner() in LauncherApps,
-            // we're on the handler thread.  Do not try to wait on tasks.  Not waiting for pending
-            // tasks on this specific case should be fine.
-            if (Thread.currentThread() != mHandler.getLooper().getThread()) {
-                mPendingTasks.waitOnAllTasks();
-            }
-
             final ArrayList<ShortcutInfo> ret = new ArrayList<>();
             final boolean cloneKeyFieldOnly =
                     ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
@@ -2199,8 +2133,6 @@
             Preconditions.checkStringNotEmpty(packageName, "packageName");
             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
 
-            mPendingTasks.waitOnAllTasks();
-
             synchronized (mLock) {
                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
                         .attemptToRestoreIfNeededAndSave();
@@ -2238,8 +2170,6 @@
             Preconditions.checkStringNotEmpty(packageName, "packageName");
             Preconditions.checkNotNull(shortcutIds, "shortcutIds");
 
-            mPendingTasks.waitOnAllTasks();
-
             synchronized (mLock) {
                 final ShortcutLauncher launcher =
                         getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
@@ -2260,8 +2190,6 @@
             Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
             Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
 
-            mPendingTasks.waitOnAllTasks();
-
             synchronized (mLock) {
                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
                         .attemptToRestoreIfNeededAndSave();
@@ -2271,6 +2199,7 @@
                         launcherUserId, callingPackage, packageName, shortcutId, userId);
                 // "si == null" should suffice here, but check the flags too just to make sure.
                 if (si == null || !si.isEnabled() || !si.isAlive()) {
+                    Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
                     return null;
                 }
                 return si.getIntent();
@@ -2291,8 +2220,6 @@
             Preconditions.checkNotNull(packageName, "packageName");
             Preconditions.checkNotNull(shortcutId, "shortcutId");
 
-            mPendingTasks.waitOnAllTasks();
-
             synchronized (mLock) {
                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
                         .attemptToRestoreIfNeededAndSave();
@@ -2317,8 +2244,6 @@
             Preconditions.checkNotNull(packageName, "packageName");
             Preconditions.checkNotNull(shortcutId, "shortcutId");
 
-            mPendingTasks.waitOnAllTasks();
-
             synchronized (mLock) {
                 getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
                         .attemptToRestoreIfNeededAndSave();
@@ -2379,18 +2304,9 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get());
                 }
-                mPendingTasks.addTask(() -> handleLocaleChanged());
+                injectPostToHandler(() -> handleLocaleChanged());
             }
         }
-
-        @Override
-        public void onPackageBroadcast(Intent intent) {
-            if (DEBUG) {
-                Slog.d(TAG, "onPackageBroadcast");
-            }
-            mPendingTasks.addTask(() -> ShortcutService.this.onPackageBroadcast(
-                    new Intent(intent)));
-        }
     }
 
     void handleLocaleChanged() {
@@ -2407,49 +2323,58 @@
         }
     }
 
-    private void onPackageBroadcast(Intent intent) {
-        final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-        if (userId == UserHandle.USER_NULL) {
-            Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
-            return;
+    /**
+     * Package event callbacks.
+     */
+    @VisibleForTesting
+    final PackageMonitor mPackageMonitor = new PackageMonitor() {
+
+        private boolean isUserUnlocked() {
+            return mUserManager.isUserUnlocked(getChangingUserId());
         }
 
-        final String action = intent.getAction();
-
-        if (!mUserManager.isUserUnlocked(userId)) {
-            if (DEBUG) {
-                Slog.d(TAG, "Ignoring package broadcast " + action + " for locked/stopped user "
-                        + userId);
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // clearCallingIdentity is not needed normally, but need to do it for the unit test.
+            final long token = injectClearCallingIdentity();
+            try {
+                super.onReceive(context, intent);
+            } finally {
+                injectRestoreCallingIdentity(token);
             }
-            return;
         }
 
-        final Uri intentUri = intent.getData();
-        final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart() : null;
-        if (packageName == null) {
-            Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
-            return;
+        @Override
+        public void onPackageAdded(String packageName, int uid) {
+            if (!isUserUnlocked()) return;
+            handlePackageAdded(packageName, getChangingUserId());
         }
 
-        final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-
-        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-            if (replacing) {
-                handlePackageUpdateFinished(packageName, userId);
-            } else {
-                handlePackageAdded(packageName, userId);
-            }
-        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-            if (!replacing) {
-                handlePackageRemoved(packageName, userId);
-            }
-        } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-            handlePackageChanged(packageName, userId);
-
-        } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
-            handlePackageDataCleared(packageName, userId);
+        @Override
+        public void onPackageUpdateFinished(String packageName, int uid) {
+            if (!isUserUnlocked()) return;
+            handlePackageUpdateFinished(packageName, getChangingUserId());
         }
-    }
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            if (!isUserUnlocked()) return;
+            handlePackageRemoved(packageName, getChangingUserId());
+        }
+
+        @Override
+        public void onPackageDataCleared(String packageName, int uid) {
+            if (!isUserUnlocked()) return;
+            handlePackageDataCleared(packageName, getChangingUserId());
+        }
+
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            if (!isUserUnlocked()) return false;
+            handlePackageChanged(packageName, getChangingUserId());
+            return false; // We don't need to receive onSomePackagesChanged(), so just false.
+        }
+    };
 
     /**
      * Called when a user is unlocked.
@@ -3096,9 +3021,6 @@
                 pw.println(Log.getStackTraceString(mLastWtfStacktrace));
             }
 
-            pw.println();
-            mPendingTasks.dump(pw, "  ");
-
             for (int i = 0; i < mUsers.size(); i++) {
                 pw.println();
                 mUsers.valueAt(i).dump(pw, "  ");
@@ -3147,8 +3069,6 @@
 
         enforceShell();
 
-        mPendingTasks.waitOnAllTasks();
-
         final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
 
         resultReceiver.send(status, null);
@@ -3175,6 +3095,10 @@
                     case "--user":
                         if (takeUser) {
                             mUserId = UserHandle.parseUserArg(getNextArgRequired());
+                            if (!mUserManager.isUserUnlocked(mUserId)) {
+                                throw new CommandException(
+                                        "User " + mUserId + " is not running or locked");
+                            }
                             break;
                         }
                         // fallthrough
@@ -3500,7 +3424,6 @@
 
     @VisibleForTesting
     ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
-        mPendingTasks.waitOnAllTasks();
         synchronized (mLock) {
             final ShortcutUser user = mUsers.get(userId);
             if (user == null) return null;
@@ -3511,12 +3434,8 @@
 
     @VisibleForTesting
     ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
-        mPendingTasks.waitOnAllTasks();
         synchronized (mLock) {
-            final ShortcutUser user = mUsers.get(userId);
-            if (user == null) return null;
-
-            final ShortcutPackage pkg = user.getAllPackagesForTest().get(packageName);
+            final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
             if (pkg == null) return null;
 
             return pkg.findShortcutById(shortcutId);
@@ -3551,12 +3470,4 @@
             forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
         }
     }
-
-    ShortcutPendingTasks getPendingTasksForTest() {
-        return mPendingTasks;
-    }
-
-    Object getLockForTest() {
-        return mLock;
-    }
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d750cbf..68ccbdf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1799,6 +1799,18 @@
         mUserVersion = USER_VERSION;
 
         Bundle restrictions = new Bundle();
+        try {
+            final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
+                    com.android.internal.R.array.config_defaultFirstUserRestrictions);
+            for (String userRestriction : defaultFirstUserRestrictions) {
+                if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
+                    restrictions.putBoolean(userRestriction, true);
+                }
+            }
+        } catch (Resources.NotFoundException e) {
+            Log.e(LOG_TAG, "Couldn't find resource: config_defaultFirstUserRestrictions", e);
+        }
+
         synchronized (mRestrictionsLock) {
             mBaseUserRestrictions.append(UserHandle.USER_SYSTEM, restrictions);
         }
@@ -2304,6 +2316,7 @@
             synchronized (mRestrictionsLock) {
                 mBaseUserRestrictions.append(userId, restrictions);
             }
+            mPm.onNewUserCreated(userId);
             Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
             addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
@@ -2874,10 +2887,6 @@
             synchronized (mRestrictionsLock) {
                 applyUserRestrictionsLR(userId);
             }
-            UserInfo userInfo = getUserInfoNoChecks(userId);
-            if (userInfo != null && !userInfo.isInitialized()) {
-                mPm.onBeforeUserStartUninitialized(userId);
-            }
         }
 
         maybeInitializeDemoMode(userId);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c082143..0499757 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -104,7 +104,8 @@
             UserManager.DISALLOW_RUN_IN_BACKGROUND,
             UserManager.DISALLOW_DATA_ROAMING,
             UserManager.DISALLOW_SET_USER_ICON,
-            UserManager.DISALLOW_SET_WALLPAPER
+            UserManager.DISALLOW_SET_WALLPAPER,
+            UserManager.DISALLOW_OEM_UNLOCK
     });
 
     /**
@@ -138,7 +139,8 @@
      */
     private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
             UserManager.DISALLOW_RECORD_AUDIO,
-            UserManager.DISALLOW_WALLPAPER
+            UserManager.DISALLOW_WALLPAPER,
+            UserManager.DISALLOW_OEM_UNLOCK
     );
 
     /**
@@ -426,6 +428,7 @@
                             newValue ? 1 : 0);
                     break;
                 case UserManager.DISALLOW_FACTORY_RESET:
+                case UserManager.DISALLOW_OEM_UNLOCK:
                     if (newValue) {
                         PersistentDataBlockManager manager = (PersistentDataBlockManager) context
                                 .getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 481edba..fbc727d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -216,6 +216,8 @@
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
     static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
 
+    static final int PENDING_KEY_NULL = -1;
+
     // Controls navigation bar opacity depending on which workspace stacks are currently
     // visible.
     // Nav bar is always opaque when either the freeform stack or docked stack is visible.
@@ -410,6 +412,10 @@
     volatile boolean mRecentsVisible;
     volatile boolean mTvPictureInPictureVisible;
 
+    // Used to hold the last user key used to wake the device.  This helps us prevent up events
+    // from being passed to the foregrounded app without a corresponding down event
+    volatile int mPendingWakeKey = PENDING_KEY_NULL;
+
     int mRecentAppsHeldModifiers;
     boolean mLanguageSwitchKeyPressed;
 
@@ -5561,12 +5567,24 @@
             // key to the application.
             result = ACTION_PASS_TO_USER;
             isWakeKey = false;
-        } else if (!interactive && shouldDispatchInputWhenNonInteractive()) {
+
+            if (interactive) {
+                // If the screen is awake, but the button pressed was the one that woke the device
+                // then don't pass it to the application
+                if (keyCode == mPendingWakeKey && !down) {
+                    result = 0;
+                }
+                // Reset the pending key
+                mPendingWakeKey = PENDING_KEY_NULL;
+            }
+        } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) {
             // If we're currently dozing with the screen on and the keyguard showing, pass the key
             // to the application but preserve its wake key status to make sure we still move
             // from dozing to fully interactive if we would normally go from off to fully
             // interactive.
             result = ACTION_PASS_TO_USER;
+            // Since we're dispatching the input, reset the pending key
+            mPendingWakeKey = PENDING_KEY_NULL;
         } else {
             // When the screen is off and the key is not injected, determine whether
             // to wake the device but don't pass the key to the application.
@@ -5574,6 +5592,10 @@
             if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
                 isWakeKey = false;
             }
+            // Cache the wake key on down event so we can also avoid sending the up event to the app
+            if (isWakeKey && down) {
+                mPendingWakeKey = keyCode;
+            }
         }
 
         // If the key would be handled globally, just return the result, don't worry about special
@@ -5950,7 +5972,7 @@
             }
         }
 
-        if (shouldDispatchInputWhenNonInteractive()) {
+        if (shouldDispatchInputWhenNonInteractive(null)) {
             return ACTION_PASS_TO_USER;
         }
 
@@ -5965,7 +5987,7 @@
         return 0;
     }
 
-    private boolean shouldDispatchInputWhenNonInteractive() {
+    private boolean shouldDispatchInputWhenNonInteractive(KeyEvent event) {
         final boolean displayOff = (mDisplay == null || mDisplay.getState() == Display.STATE_OFF);
 
         if (displayOff && !mHasFeatureWatch) {
@@ -5977,6 +5999,14 @@
             return true;
         }
 
+        // Watches handle BACK specially
+        if (mHasFeatureWatch
+                && event != null
+                && (event.getKeyCode() == KeyEvent.KEYCODE_BACK
+                        || event.getKeyCode() == KeyEvent.KEYCODE_STEM_PRIMARY)) {
+            return false;
+        }
+
         // Send events to a dozing dream even if the screen is off since the dream
         // is in control of the state of the screen.
         IDreamManager dreamManager = getDreamManager();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 06e5e73..4b58a3b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -85,6 +85,7 @@
 
 import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.server.EventLogTags;
@@ -166,7 +167,7 @@
      * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
      * that the wallpaper has changed. The CREATE is triggered when there is no
      * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
-     * everytime the wallpaper is changed.
+     * every time the wallpaper is changed.
      */
     private class WallpaperObserver extends FileObserver {
 
@@ -175,7 +176,6 @@
         final File mWallpaperDir;
         final File mWallpaperFile;
         final File mWallpaperLockFile;
-        final File mWallpaperInfoFile;
 
         public WallpaperObserver(WallpaperData wallpaper) {
             super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
@@ -185,7 +185,6 @@
             mWallpaper = wallpaper;
             mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
             mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
-            mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
         }
 
         private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
@@ -943,10 +942,28 @@
         mLockWallpaperMap.remove(userId);
     }
 
-    void onUnlockUser(int userId) {
+    void onUnlockUser(final int userId) {
         synchronized (mLock) {
             if (mCurrentUserId == userId && mWaitingForUnlock) {
                 switchUser(userId, null);
+
+                // Make sure that the SELinux labeling of all the relevant files is correct.
+                // This corrects for mislabeling bugs that might have arisen from move-to
+                // operations involving the wallpaper files.  This isn't timing-critical,
+                // so we do it in the background to avoid holding up the user unlock operation.
+                Runnable relabeler = new Runnable() {
+                    @Override
+                    public void run() {
+                        final File wallpaperDir = getWallpaperDir(userId);
+                        for (String filename : sPerUserFiles) {
+                            File f = new File(wallpaperDir, filename);
+                            if (f.exists()) {
+                                SELinux.restorecon(f);
+                            }
+                        }
+                    }
+                };
+                BackgroundThread.getHandler().post(relabeler);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4c79149..84788cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -545,7 +545,7 @@
     SparseArray<DisplayContent> mDisplayContents = new SparseArray<>(2);
 
     int mRotation = 0;
-    int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    int mLastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     boolean mAltOrientation = false;
 
     private boolean mKeyguardWaitingForActivityDrawn;
@@ -3521,13 +3521,16 @@
                 // can re-appear and inflict its own orientation on us.  Keep the
                 // orientation stable until this all settles down.
                 return mLastWindowForcedOrientation;
-            } else if (mPolicy.isKeyguardLocked()
-                    && mLastKeyguardForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
-                // Use the last orientation the keyguard forced while the display is frozen with the
-                // keyguard locked.
+            } else if (mPolicy.isKeyguardLocked()) {
+                // Use the last orientation the while the display is frozen with the
+                // keyguard locked. This could be the keyguard forced orientation or
+                // from a SHOW_WHEN_LOCKED window. We don't want to check the show when
+                // locked window directly though as things aren't stable while
+                // the display is frozen, for example the window could be momentarily unavailable
+                // due to activity relaunch.
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
-                        + "return " + mLastKeyguardForcedOrientation);
-                return mLastKeyguardForcedOrientation;
+                        + "return " + mLastOrientation);
+                return mLastOrientation;
             }
         } else {
             // TODO(multidisplay): Change to the correct display.
@@ -3657,12 +3660,12 @@
             }
         }
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                "No app is requesting an orientation, return " + mForcedAppOrientation);
+                "No app is requesting an orientation, return " + mLastOrientation);
         // The next app has not been requested to be visible, so we keep the current orientation
         // to prevent freezing/unfreezing the display too early unless we are in multi-window, in
         // which we don't let the app customize the orientation unless it was the home task that
         // is handled above.
-        return inMultiWindow ? SCREEN_ORIENTATION_UNSPECIFIED : mForcedAppOrientation;
+        return inMultiWindow ? SCREEN_ORIENTATION_UNSPECIFIED : mLastOrientation;
     }
 
     @Override
@@ -3745,8 +3748,8 @@
         long ident = Binder.clearCallingIdentity();
         try {
             int req = getOrientationLocked();
-            if (req != mForcedAppOrientation) {
-                mForcedAppOrientation = req;
+            if (req != mLastOrientation) {
+                mLastOrientation = req;
                 //send a message to Policy indicating orientation change to take
                 //action like disabling/enabling sensors etc.,
                 mPolicy.setCurrentOrientationLw(req);
@@ -6159,6 +6162,21 @@
         }
     }
 
+    @Override
+    public Bitmap screenshotWallpaper() {
+        if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
+                "screenshotWallpaper()")) {
+            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+        }
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
+            return screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1, true, 1f,
+                    Bitmap.Config.ARGB_8888, true);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+
     /**
      * Takes a snapshot of the screen.  In landscape mode this grabs the whole screen.
      * In portrait mode, it grabs the upper region of the screen based on the vertical dimension
@@ -6175,7 +6193,7 @@
             @Override
             public void run() {
                 Bitmap bm = screenshotApplicationsInner(null, Display.DEFAULT_DISPLAY, -1, -1,
-                        true, 1f, Bitmap.Config.ARGB_8888);
+                        true, 1f, Bitmap.Config.ARGB_8888, false);
                 try {
                     receiver.send(bm);
                 } catch (RemoteException e) {
@@ -6205,14 +6223,27 @@
         try {
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotApplications");
             return screenshotApplicationsInner(appToken, displayId, width, height, false,
-                    frameScale, Bitmap.Config.RGB_565);
+                    frameScale, Bitmap.Config.RGB_565, false);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
+    /**
+     * Takes a snapshot of the screen.  In landscape mode this grabs the whole screen.
+     * In portrait mode, it grabs the full screenshot.
+     *
+     * @param displayId the Display to take a screenshot of.
+     * @param width the width of the target bitmap
+     * @param height the height of the target bitmap
+     * @param includeFullDisplay true if the screen should not be cropped before capture
+     * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
+     * @param config of the output bitmap
+     * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
+     */
     Bitmap screenshotApplicationsInner(IBinder appToken, int displayId, int width, int height,
-            boolean includeFullDisplay, float frameScale, Bitmap.Config config) {
+            boolean includeFullDisplay, float frameScale, Bitmap.Config config,
+            boolean wallpaperOnly) {
         final DisplayContent displayContent;
         synchronized(mWindowMap) {
             displayContent = getDisplayContentLocked(displayId);
@@ -6239,7 +6270,7 @@
 
         boolean screenshotReady;
         int minLayer;
-        if (appToken == null) {
+        if (appToken == null && !wallpaperOnly) {
             screenshotReady = true;
             minLayer = 0;
         } else {
@@ -6279,11 +6310,20 @@
                 if (ws.mLayer >= aboveAppLayer) {
                     continue;
                 }
+                if (wallpaperOnly && !ws.mIsWallpaper) {
+                    continue;
+                }
                 if (ws.mIsImWindow) {
                     if (!includeImeInScreenshot) {
                         continue;
                     }
                 } else if (ws.mIsWallpaper) {
+                    // If this is the wallpaper layer and we're only looking for the wallpaper layer
+                    // then the target window state is this one.
+                    if (wallpaperOnly) {
+                        appWin = ws;
+                    }
+
                     if (appWin == null) {
                         // We have not ran across the target window yet, so it is probably
                         // behind the wallpaper. This can happen when the keyguard is up and
@@ -6331,8 +6371,10 @@
                     }
                 }
 
-                if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
-                        ws.isDisplayedLw() && winAnim.getShown()) {
+                final boolean foundTargetWs =
+                        (ws.mAppToken != null && ws.mAppToken.token == appToken)
+                        || (appWin != null && wallpaperOnly);
+                if (foundTargetWs && ws.isDisplayedLw() && winAnim.getShown()) {
                     screenshotReady = true;
                 }
 
@@ -6622,13 +6664,13 @@
         //       an orientation that has different metrics than it expected.
         //       eg. Portrait instead of Landscape.
 
-        int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation);
+        int rotation = mPolicy.rotationForOrientationLw(mLastOrientation, mRotation);
         boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
-                mForcedAppOrientation, rotation);
+                mLastOrientation, rotation);
 
         if (DEBUG_ORIENTATION) {
-            Slog.v(TAG_WM, "Application requested orientation "
-                    + mForcedAppOrientation + ", got rotation " + rotation
+            Slog.v(TAG_WM, "Selected orientation "
+                    + mLastOrientation + ", got rotation " + rotation
                     + " which has " + (altOrientation ? "incompatible" : "compatible")
                     + " metrics");
         }
@@ -6642,7 +6684,7 @@
             Slog.v(TAG_WM,
                 "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "")
                 + " from " + mRotation + (mAltOrientation ? " (alt)" : "")
-                + ", forceApp=" + mForcedAppOrientation);
+                + ", lastOrientation=" + mLastOrientation);
         }
 
         int oldRotation = mRotation;
@@ -10487,7 +10529,7 @@
             pw.print("  mRotation="); pw.print(mRotation);
                     pw.print(" mAltOrientation="); pw.println(mAltOrientation);
             pw.print("  mLastWindowForcedOrientation="); pw.print(mLastWindowForcedOrientation);
-                    pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
+                    pw.print(" mLastOrientation="); pw.println(mLastOrientation);
             pw.print("  mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
             pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 31b756e..2120be1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -218,6 +218,8 @@
     private static final String ATTR_SETUP_COMPLETE = "setup-complete";
     private static final String ATTR_PROVISIONING_STATE = "provisioning-state";
     private static final String ATTR_PERMISSION_POLICY = "permission-policy";
+    private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
+            "device-provisioning-config-applied";
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
     private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
@@ -417,6 +419,8 @@
         int mUserProvisioningState;
         int mPermissionPolicy;
 
+        boolean mDeviceProvisioningConfigApplied = false;
+
         final ArrayMap<ComponentName, ActiveAdmin> mAdminMap = new ArrayMap<>();
         final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
         final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
@@ -2173,6 +2177,10 @@
                 out.attribute(null, ATTR_SETUP_COMPLETE,
                         Boolean.toString(true));
             }
+            if (policy.mDeviceProvisioningConfigApplied) {
+                out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED,
+                        Boolean.toString(true));
+            }
             if (policy.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
                 out.attribute(null, ATTR_PROVISIONING_STATE,
                         Integer.toString(policy.mUserProvisioningState));
@@ -2333,6 +2341,12 @@
             if (userSetupComplete != null && Boolean.toString(true).equals(userSetupComplete)) {
                 policy.mUserSetupComplete = true;
             }
+            String deviceProvisioningConfigApplied = parser.getAttributeValue(null,
+                    ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED);
+            if (deviceProvisioningConfigApplied != null
+                    && Boolean.toString(true).equals(deviceProvisioningConfigApplied)) {
+                policy.mDeviceProvisioningConfigApplied = true;
+            }
             String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
             if (!TextUtils.isEmpty(provisioningState)) {
                 policy.mUserProvisioningState = Integer.parseInt(provisioningState);
@@ -9046,4 +9060,23 @@
         // restrictions.
         pushUserRestrictions(userHandle);
     }
+
+    @Override
+    public void setDeviceProvisioningConfigApplied() {
+        enforceManageUsers();
+        synchronized (this) {
+            DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+            policy.mDeviceProvisioningConfigApplied = true;
+            saveSettingsLocked(UserHandle.USER_SYSTEM);
+        }
+    }
+
+    @Override
+    public boolean isDeviceProvisioningConfigApplied() {
+        enforceManageUsers();
+        synchronized (this) {
+            final DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+            return policy.mDeviceProvisioningConfigApplied;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index d853f91..1be57bc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -38,6 +38,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.IUidObserver;
 import android.app.usage.UsageStatsManagerInternal;
@@ -403,11 +404,6 @@
             // During tests, WTF is fatal.
             fail(message + "  exception: " + th + "\n" + Log.getStackTraceString(th));
         }
-
-        @Override
-        boolean injectCheckPendingTaskWaitThread() {
-            return true;
-        }
     }
 
     /** ShortcutManager with injection override methods. */
@@ -475,13 +471,6 @@
         void injectRestoreCallingIdentity(long token) {
             mInjectedCallingUid = (int) token;
         }
-
-        @Override
-        protected void startShortcutIntentAsPublisher(@NonNull Intent intent,
-                @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
-            // Just forward to startActivityAsUser() during unit tests.
-            mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
-        }
     }
 
     protected class LauncherAppsTestable extends LauncherApps {
@@ -709,6 +698,17 @@
         mUnlockedUsers.put(USER_11, true);
         mUnlockedUsers.put(USER_P0, true);
 
+        // Set up mMockActivityManagerInternal.
+        // By default, startActivityAsPackage() will simply forward to startActivityAsUser().
+        doAnswer(new AnswerWithSystemCheck<>(inv -> {
+            mServiceContext.startActivityAsUser(
+                    (Intent) inv.getArguments()[2],
+                    (Bundle) inv.getArguments()[3],
+                    UserHandle.of((Integer) inv.getArguments()[1]));
+            return ActivityManager.START_SUCCESS;
+        })).when(mMockActivityManagerInternal).startActivityAsPackage(anyString(), anyInt(),
+                any(Intent.class), any(Bundle.class));
+
         // Set up resources
         setUpAppResources();
 
@@ -848,8 +848,6 @@
 
     protected void shutdownServices() {
         if (mService != null) {
-            mService.getPendingTasksForTest().waitOnAllTasks();
-
             // Flush all the unsaved data from the previous instance.
             mService.saveDirtyInfo();
 
@@ -1414,20 +1412,22 @@
     }
 
     protected void assertShortcutNotLaunchable(@NonNull String packageName,
-            @NonNull String shortcutId, int userId) {
+            @NonNull String shortcutId, int userId, Class<?> expectedException) {
         reset(mServiceContext);
+        Exception thrown = null;
         try {
             mLauncherApps.startShortcut(packageName, shortcutId, null, null,
                     UserHandle.of(userId));
-        } catch (SecurityException expected) {
-            // security exception is okay.
-            return;
+        } catch (Exception e) {
+            thrown = e;
         }
         // This shouldn't have been called.
         verify(mServiceContext, times(0)).startActivityAsUser(
                 any(Intent.class),
                 any(Bundle.class),
                 any(UserHandle.class));
+        assertNotNull("Exception was not thrown", thrown);
+        assertEquals("Exception type different", expectedException, thrown.getClass());
     }
 
     protected void assertBitmapDirectories(int userId, String... expectedDirectories) {
@@ -1473,12 +1473,30 @@
         return new File(si.getBitmapPath()).getName();
     }
 
+    /**
+     * @return all shortcuts stored internally for the caller.  This reflects the *internal* view
+     * of shortcuts, which may be different from what {@link #getCallerVisibleShortcuts} would
+     * return, because getCallerVisibleShortcuts() will get shortcuts from the proper "front door"
+     * which performs some extra checks, like {@link ShortcutPackage#onRestored}.
+     */
     protected List<ShortcutInfo> getCallerShortcuts() {
         final ShortcutPackage p = mService.getPackageShortcutForTest(
                 getCallingPackage(), getCallingUserId());
         return p == null ? null : p.getAllShortcutsForTest();
     }
 
+    /**
+     * @return all shortcuts owned by caller that are actually visible via ShortcutManager.
+     * See also {@link #getCallerShortcuts}.
+     */
+    protected List<ShortcutInfo> getCallerVisibleShortcuts() {
+        final ArrayList<ShortcutInfo> ret = new ArrayList<>();
+        ret.addAll(mManager.getDynamicShortcuts());
+        ret.addAll(mManager.getPinnedShortcuts());
+        ret.addAll(mManager.getManifestShortcuts());
+        return ret;
+    }
+
     protected ShortcutInfo getCallerShortcut(String shortcutId) {
         return getPackageShortcut(getCallingPackage(), shortcutId, getCallingUserId());
     }
@@ -1689,6 +1707,8 @@
 
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s5"), HANDLE_USER_P0);
         });
+
+        // Note LAUNCHER_3 has allowBackup=false.
         runWithCaller(LAUNCHER_3, USER_0, () -> {
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
             mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4"), HANDLE_USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index f5ae706..0936e46 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -54,13 +54,18 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.waitOnMainThread;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.Manifest.permission;
+import android.app.ActivityManager;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -72,6 +77,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -1291,7 +1297,8 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_3);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -1309,7 +1316,8 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -1666,18 +1674,32 @@
 
         // Get pinned shortcuts from launcher
         runWithCaller(LAUNCHER_1, USER_0, () -> {
-            // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
-            assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllDisabled(
-                    mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
-                    /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
-                    "s2");
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+            // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists, and disabled.
+            assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+                    /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))
+                    .haveIds("s2")
+                    .areAllPinned()
+                    .areAllNotWithKeyFieldsOnly()
+                    .areAllDisabled();
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+                    ActivityNotFoundException.class);
 
-            assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
-                    mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
-                    /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
-                    "s3", "s4");
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+            // Here, s4 is still enabled and launchable, but s3 is disabled.
+            assertWith(mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+                    /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))
+                    .haveIds("s3", "s4")
+                    .areAllPinned()
+                    .areAllNotWithKeyFieldsOnly()
+
+                    .selectByIds("s3")
+                    .areAllDisabled()
+
+                    .revertToOriginalList()
+                    .selectByIds("s4")
+                    .areAllEnabled();
+
+            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0,
+                    ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s4", USER_0);
 
             assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
@@ -2108,12 +2130,18 @@
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_1, USER_P0, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2150,12 +2178,18 @@
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_P0, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2192,12 +2226,18 @@
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_10, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2257,19 +2297,27 @@
                     "s1", "s2", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+                    ActivityNotFoundException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+                    ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_1, USER_P0, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2300,18 +2348,25 @@
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+                    ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_P0, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2341,19 +2396,27 @@
                     "s1", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+                    ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0,
+                    ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_10, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2369,20 +2432,29 @@
                     /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
                     "s1", "s2", "s3");
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+                    SecurityException.class);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s1", USER_0,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0,
+                    SecurityException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    ActivityNotFoundException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    ActivityNotFoundException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    ActivityNotFoundException.class);
         });
 
         // Save & load and make sure we still have the same information.
@@ -2418,19 +2490,27 @@
                     "s1", "s2", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+                    ActivityNotFoundException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+                    ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_1, USER_P0, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2461,18 +2541,25 @@
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+                    ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_P0, () -> {
             assertShortcutIds(assertAllPinned(
@@ -2502,80 +2589,143 @@
                     "s1", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+                    ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0,
+                    ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+                    SecurityException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+                    SecurityException.class);
         });
     }
 
     public void testStartShortcut() {
         // Create some shortcuts.
-        setCaller(CALLING_PACKAGE_1);
-        final ShortcutInfo s1_1 = makeShortcut(
-                "s1",
-                "Title 1",
-                makeComponent(ShortcutActivity.class),
-                /* icon =*/ null,
-                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
-                        "key1", "val1", "nest", makeBundle("key", 123)),
-                /* weight */ 10);
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            final ShortcutInfo s1_1 = makeShortcut(
+                    "s1",
+                    "Title 1",
+                    makeComponent(ShortcutActivity.class),
+            /* icon =*/ null,
+                    makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+                            "key1", "val1", "nest", makeBundle("key", 123)),
+            /* rank */ 10);
 
-        final ShortcutInfo s1_2 = makeShortcut(
-                "s2",
-                "Title 2",
-                /* activity */ null,
-                /* icon =*/ null,
-                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
-                /* weight */ 12);
+            final ShortcutInfo s1_2 = makeShortcut(
+                    "s2",
+                    "Title 2",
+            /* activity */ null,
+            /* icon =*/ null,
+                    makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+            /* rank */ 12);
 
-        assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
+            final ShortcutInfo s1_3 = makeShortcut("s3");
 
-        setCaller(CALLING_PACKAGE_2);
-        final ShortcutInfo s2_1 = makeShortcut(
-                "s1",
-                "ABC",
-                makeComponent(ShortcutActivity.class),
-                /* icon =*/ null,
-                makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
-                        "key1", "val1", "nest", makeBundle("key", 123)),
-                /* weight */ 10);
-        assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
+            assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2, s1_3)));
+        });
 
-        // Pin all.
-        setCaller(LAUNCHER_1);
-        mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
-                list("s1", "s2"), getCallingUser());
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            final ShortcutInfo s2_1 = makeShortcut(
+                    "s1",
+                    "ABC",
+                    makeComponent(ShortcutActivity.class),
+                    /* icon =*/ null,
+                    makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
+                            "key1", "val1", "nest", makeBundle("key", 123)),
+                    /* weight */ 10);
+            assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
+        });
 
-        mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
-                list("s1"), getCallingUser());
+        // Pin some.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+                    list("s1", "s2"), getCallingUser());
+
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+                    list("s1"), getCallingUser());
+        });
 
         // Just to make it complicated, delete some.
-        setCaller(CALLING_PACKAGE_1);
-        mManager.removeDynamicShortcuts(list("s2"));
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            mManager.removeDynamicShortcuts(list("s2"));
+        });
 
-        // intent and check.
-        setCaller(LAUNCHER_1);
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertEquals(
+                    ShortcutActivity2.class.getName(),
+                    launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s1", USER_0)
+                            .getComponent().getClassName());
+            assertEquals(
+                    ShortcutActivity3.class.getName(),
+                    launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s2", USER_0)
+                            .getComponent().getClassName());
+            assertEquals(
+                    ShortcutActivity.class.getName(),
+                    launchShortcutAndGetIntent(CALLING_PACKAGE_2, "s1", USER_0)
+                            .getComponent().getClassName());
 
-        Intent intent;
-        intent = launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s1", USER_0);
-        assertEquals(ShortcutActivity2.class.getName(), intent.getComponent().getClassName());
+            assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
 
+            assertShortcutNotLaunchable("no-such-package", "s2", USER_0,
+                    ActivityNotFoundException.class);
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "xxxx", USER_0,
+                    ActivityNotFoundException.class);
+        });
 
-        intent = launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s2", USER_0);
-        assertEquals(ShortcutActivity3.class.getName(), intent.getComponent().getClassName());
+        // LAUNCHER_1 is no longer the default launcher
+        setDefaultLauncherChecker((pkg, userId) -> false);
 
-        intent = launchShortcutAndGetIntent(CALLING_PACKAGE_2, "s1", USER_0);
-        assertEquals(ShortcutActivity.class.getName(), intent.getComponent().getClassName());
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Not the default launcher, but pinned shortcuts are still lauchable.
+            assertEquals(
+                    ShortcutActivity2.class.getName(),
+                    launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s1", USER_0)
+                            .getComponent().getClassName());
+            assertEquals(
+                    ShortcutActivity3.class.getName(),
+                    launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s2", USER_0)
+                            .getComponent().getClassName());
+            assertEquals(
+                    ShortcutActivity.class.getName(),
+                    launchShortcutAndGetIntent(CALLING_PACKAGE_2, "s1", USER_0)
+                            .getComponent().getClassName());
+
+            // Not pinned, so not lauchable.
+        });
+
+        // Test inner errors.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Not launchable.
+            doAnswer(new AnswerWithSystemCheck<>(inv -> {
+                return ActivityManager.START_CLASS_NOT_FOUND;
+            })).when(mMockActivityManagerInternal).startActivityAsPackage(anyString(), anyInt(),
+                    any(Intent.class), any(Bundle.class));
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0,
+                    ActivityNotFoundException.class);
+
+            // Still not launchable.
+            doAnswer(new AnswerWithSystemCheck<>(inv -> {
+                return ActivityManager.START_PERMISSION_DENIED;
+            })).when(mMockActivityManagerInternal).startActivityAsPackage(anyString(), anyInt(),
+                    any(Intent.class), any(Bundle.class));
+            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0,
+                    ActivityNotFoundException.class);
+        });
+
 
         // TODO Check extra, etc
     }
@@ -2665,7 +2815,7 @@
                     new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                     R.xml.shortcut_2);
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+            mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
         }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
                 .areAllManifest()
@@ -2702,7 +2852,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_0);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         assertForLauncherCallback(mLauncherApps, () -> {
@@ -3322,7 +3472,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -3339,7 +3489,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -3701,7 +3851,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertWith(getCallerShortcuts())
@@ -3741,7 +3891,7 @@
         assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
 
         uninstallPackage(USER_0, CALLING_PACKAGE_1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
 
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
@@ -3761,7 +3911,7 @@
         mRunningUsers.put(USER_10, true);
 
         uninstallPackage(USER_10, CALLING_PACKAGE_2);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
 
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
@@ -3852,7 +4002,7 @@
         assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
         assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
 
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDataClear(CALLING_PACKAGE_1, USER_0));
 
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
@@ -3871,7 +4021,7 @@
 
         mRunningUsers.put(USER_10, true);
 
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDataClear(CALLING_PACKAGE_2, USER_10));
 
         assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
@@ -3898,7 +4048,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -3919,7 +4069,7 @@
         });
 
         // Clear data
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageDataClear(CALLING_PACKAGE_1, USER_10));
 
         // Only manifest shortcuts will remain, and are no longer pinned.
@@ -3984,9 +4134,9 @@
         reset(c0);
         reset(c10);
 
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageUpdateIntent(CALLING_PACKAGE_1, USER_10));
 
         waitOnMainThread();
@@ -4007,7 +4157,7 @@
         updatePackageVersion(CALLING_PACKAGE_1, 1);
 
         // Then send the broadcast, to only user-0.
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
 
         waitOnMainThread();
@@ -4072,7 +4222,7 @@
         updatePackageVersion(CALLING_PACKAGE_2, 10);
 
         // Then send the broadcast, to only user-0.
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageUpdateIntent(CALLING_PACKAGE_2, USER_0));
         mService.handleUnlockUser(USER_10);
 
@@ -4096,7 +4246,7 @@
         updatePackageVersion(CALLING_PACKAGE_3, 100);
 
         // Then send the broadcast, to only user-0.
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageUpdateIntent(CALLING_PACKAGE_3, USER_0));
         mService.handleUnlockUser(USER_10);
 
@@ -4178,7 +4328,7 @@
 
         // Update the package.
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -4207,7 +4357,7 @@
         mRunningUsers.put(USER_10, true);
 
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4239,7 +4389,7 @@
         });
 
         // First, no changes.
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4262,7 +4412,7 @@
 
         // Disable activity 1
         mEnabledActivityChecker = (activity, userId) -> !ACTIVITY1.equals(activity);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4282,7 +4432,7 @@
         // Re-enable activity 1.
         // Manifest shortcuts will be re-published, but dynamic ones are not.
         mEnabledActivityChecker = (activity, userId) -> true;
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4306,7 +4456,7 @@
         // Disable activity 2
         // Because "ms1-alt" and "s2" are both pinned, they will remain, but disabled.
         mEnabledActivityChecker = (activity, userId) -> !ACTIVITY2.equals(activity);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
 
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
@@ -4369,7 +4519,7 @@
         setCaller(LAUNCHER_1, USER_0);
         assertForLauncherCallback(mLauncherApps, () -> {
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+                    mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
         }).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
                 // Make sure the launcher gets callbacks.
@@ -4486,8 +4636,10 @@
         final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
         assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_1));
         assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_2));
-        assertExistsAndShadow(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_1)));
-        assertExistsAndShadow(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_2)));
+        assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+                PackageWithUser.of(USER_0, LAUNCHER_1)));
+        assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+                PackageWithUser.of(USER_0, LAUNCHER_2)));
 
         assertNull(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
         assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
@@ -4495,90 +4647,98 @@
 
         installPackage(USER_0, CALLING_PACKAGE_1);
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertEquals(0, mManager.getDynamicShortcuts().size());
-            assertShortcutIds(assertAllPinned(
-                    mManager.getPinnedShortcuts()),
-                    "s1", "s2");
+            assertWith(getCallerVisibleShortcuts())
+                    .selectDynamic()
+                    .isEmpty()
+
+                    .revertToOriginalList()
+                    .selectPinned()
+                    .haveIds("s1", "s2");
         });
 
         installPackage(USER_0, LAUNCHER_1);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
-                    "s1");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
-                    /* empty, not restored */ );
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty, not restored */ );
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s1");
 
-            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+                    .isEmpty();
         });
 
         installPackage(USER_0, CALLING_PACKAGE_2);
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
-            assertEquals(0, mManager.getDynamicShortcuts().size());
-            assertShortcutIds(assertAllPinned(
-                    mManager.getPinnedShortcuts()),
-                    "s1", "s2", "s3");
+            assertWith(getCallerVisibleShortcuts())
+                    .selectDynamic()
+                    .isEmpty()
+
+                    .revertToOriginalList()
+                    .selectPinned()
+                    .haveIds("s1", "s2", "s3");
         });
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
-                    "s1");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s1", "s2");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* empty, not restored */ );
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s1");
 
-            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s1", "s2");
+
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+                    .isEmpty();
         });
 
         // 3 shouldn't be backed up, so no pinned shortcuts.
         installPackage(USER_0, CALLING_PACKAGE_3);
         runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
-            assertEquals(0, mManager.getDynamicShortcuts().size());
-            assertEquals(0, mManager.getPinnedShortcuts().size());
+            assertWith(getCallerVisibleShortcuts())
+                    .isEmpty();
         });
 
         // Launcher on a different profile shouldn't be restored.
         runWithCaller(LAUNCHER_1, USER_P0, () -> {
-            assertEquals(0,
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)
-                            .size());
-            assertEquals(0,
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)
-                            .size());
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* wasn't restored, so still empty */ );
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .isEmpty();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .isEmpty();
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
         });
 
         // Package on a different profile, no restore.
         installPackage(USER_P0, CALLING_PACKAGE_1);
         runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
-            assertEquals(0, mManager.getDynamicShortcuts().size());
-            assertEquals(0, mManager.getPinnedShortcuts().size());
+            assertWith(getCallerVisibleShortcuts())
+                    .isEmpty();
         });
 
         // Restore launcher 2 on user 0.
         installPackage(USER_0, LAUNCHER_2);
         runWithCaller(LAUNCHER_2, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
-                    "s2");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s2", "s3");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* wasn't restored, so still empty */ );
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s2");
 
-            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s2", "s3");
+
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+                    .isEmpty();
         });
 
 
@@ -4586,33 +4746,33 @@
         // make sure they still have the same result.
         installPackage(USER_0, CALLING_PACKAGE_1);
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertEquals(0, mManager.getDynamicShortcuts().size());
-            assertShortcutIds(assertAllPinned(
-                    mManager.getPinnedShortcuts()),
-                    "s1", "s2");
+            assertWith(getCallerVisibleShortcuts())
+                    .areAllPinned()
+                    .haveIds("s1", "s2");
         });
 
         installPackage(USER_0, LAUNCHER_1);
         runWithCaller(LAUNCHER_1, USER_0, () -> {
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
-                    "s1");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
-                    "s1", "s2");
-            assertShortcutIds(assertAllPinned(
-                    mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
-                    /* wasn't restored, so still empty */ );
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s1");
 
-            assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .areAllPinned()
+                    .haveIds("s1", "s2");
+
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+                    .isEmpty();
         });
 
         installPackage(USER_0, CALLING_PACKAGE_2);
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
-            assertEquals(0, mManager.getDynamicShortcuts().size());
-            assertShortcutIds(assertAllPinned(
-                    mManager.getPinnedShortcuts()),
-                    "s1", "s2", "s3");
+            assertWith(getCallerVisibleShortcuts())
+                    .areAllPinned()
+                    .haveIds("s1", "s2", "s3");
         });
     }
 
@@ -4933,6 +5093,112 @@
         });
     }
 
+    public void testBackupAndRestore_disabled() {
+        prepareCrossProfileDataSet();
+
+        // Before doing backup & restore, disable s1.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            mManager.disableShortcuts(list("s1"));
+        });
+
+        backupAndRestore();
+
+        // Below is copied from checkBackupAndRestore_success.
+
+        // Make sure non-system user is not restored.
+        final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0);
+        assertEquals(0, userP0.getAllPackagesForTest().size());
+        assertEquals(0, userP0.getAllLaunchersForTest().size());
+
+        // Make sure only "allowBackup" apps are restored, and are shadow.
+        final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
+        assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_1));
+        assertExistsAndShadow(user0.getAllPackagesForTest().get(CALLING_PACKAGE_2));
+        assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+                PackageWithUser.of(USER_0, LAUNCHER_1)));
+        assertExistsAndShadow(user0.getAllLaunchersForTest().get(
+                PackageWithUser.of(USER_0, LAUNCHER_2)));
+
+        assertNull(user0.getAllPackagesForTest().get(CALLING_PACKAGE_3));
+        assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
+        assertNull(user0.getAllLaunchersForTest().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
+
+        installPackage(USER_0, CALLING_PACKAGE_1);
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertWith(getCallerVisibleShortcuts())
+                    .areAllEnabled() // disabled shortcuts shouldn't be restored.
+
+                    .selectDynamic()
+                    .isEmpty()
+
+                    .revertToOriginalList()
+                    .selectPinned()
+                    // s1 is not restored.
+                    .haveIds("s2");
+        });
+
+        installPackage(USER_0, LAUNCHER_1);
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Note, s1 was pinned by launcher 1, but was disabled, so isn't restored.
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+                    .isEmpty();
+
+            assertWith(mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0))
+                    .isEmpty();
+        });
+    }
+
+
+    public void testBackupAndRestore_manifestNotRestored() {
+        // Publish two manifest shortcuts.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_2);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mInternal.onPackageBroadcast(
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        // Pin from launcher 1.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1", "ms2"), HANDLE_USER_0);
+        });
+
+        // Update and now ms2 is gone -> disabled.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_1);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mInternal.onPackageBroadcast(
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        // Make sure the manifest shortcuts have been published.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertWith(getCallerShortcuts())
+                    .areAllPinned()
+                    .haveIds("ms1", "ms2")
+
+                    .selectByIds("ms1")
+                    .areAllManifest()
+                    .areAllEnabled()
+
+                    .revertToOriginalList()
+                    .selectByIds("ms2")
+                    .areAllNotManifest()
+                    .areAllDisabled();
+        });
+
+        // Now do the regular backup & restore test.
+        // The existence of the manifest shortcuts shouldn't affect the result.
+        prepareCrossProfileDataSet();
+        backupAndRestore();
+    }
+
     public void testSaveAndLoad_crossProfile() {
         prepareCrossProfileDataSet();
 
@@ -5369,7 +5635,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -5390,7 +5656,7 @@
                 new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
                 R.xml.shortcut_5);
         updatePackageVersion(CALLING_PACKAGE_2, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -5427,7 +5693,7 @@
                 new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -5464,7 +5730,7 @@
         mRunningUsers.put(USER_10, false);
         mUnlockedUsers.put(USER_10, false);
 
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
         runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
             assertEmpty(mManager.getManifestShortcuts());
@@ -5474,7 +5740,7 @@
         // Try again, but the user is locked, so still ignored.
         mRunningUsers.put(USER_10, true);
 
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
         runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
             assertEmpty(mManager.getManifestShortcuts());
@@ -5485,7 +5751,7 @@
         mUnlockedUsers.put(USER_10, true);
 
         // Send PACKAGE_ADD broadcast to have Package 2 on user-10 publish manifest shortcuts.
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
 
         runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
@@ -5526,7 +5792,7 @@
                 R.xml.shortcut_5_reverse);
 
         updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
 
         runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
@@ -5554,7 +5820,7 @@
                 new ComponentName(CALLING_PACKAGE_2, ShortcutActivity2.class.getName()),
                 R.xml.shortcut_0);
         updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
 
         // No manifest shortcuts, and pinned ones are disabled.
@@ -5585,7 +5851,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_error_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Only the valid one is published.
@@ -5600,7 +5866,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_error_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Only the valid one is published.
@@ -5615,7 +5881,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_error_3);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Only the valid one is published.
@@ -5631,7 +5897,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_error_4);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -5659,7 +5925,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_5);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -5697,7 +5963,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_error_4);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Make sure 3, 4 and 5 still exist but disabled.
@@ -5745,7 +6011,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_5);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Only the valid one is published.
@@ -5850,7 +6116,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -5947,7 +6213,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Only the valid one is published.
@@ -5966,7 +6232,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1_disable);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Because shortcut 1 wasn't pinned, it'll just go away.
@@ -5987,7 +6253,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Only the valid one is published.
@@ -6010,7 +6276,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1_disable);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         // Because shortcut 1 was pinned, it'll still exist as pinned, but disabled.
@@ -6043,7 +6309,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2_duplicate);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -6073,7 +6339,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
                 R.xml.shortcut_5);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -6145,7 +6411,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_5);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -6195,7 +6461,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(LAUNCHER_1, USER_0, () -> {
@@ -6206,7 +6472,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -6288,7 +6554,7 @@
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_5);
         updatePackageVersion(CALLING_PACKAGE_1, 1);
-        mInternal.onPackageBroadcast(
+                mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
@@ -6358,7 +6624,7 @@
                     new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                     R.xml.shortcut_2);
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+                    mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
             assertEquals(2, mManager.getManifestShortcuts().size());
 
@@ -6484,7 +6750,7 @@
                     new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                     R.xml.shortcut_2);
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+                    mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
 
             assertEquals(2, mManager.getManifestShortcuts().size());
@@ -6633,7 +6899,7 @@
                     new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                     R.xml.shortcut_1);
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+                    mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
             assertEquals(1, mManager.getManifestShortcuts().size());
 
@@ -6653,7 +6919,7 @@
                     new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
                     R.xml.shortcut_1_alt);
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+                    mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
             assertEquals(3, mManager.getManifestShortcuts().size());
 
@@ -6673,7 +6939,7 @@
                     new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
                     R.xml.shortcut_5_alt); // manifest has 5, but max is 3, so a2 will have 3.
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+                    mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
             assertEquals(5, mManager.getManifestShortcuts().size());
 
@@ -6692,7 +6958,7 @@
                     new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
                     R.xml.shortcut_0);
             updatePackageVersion(CALLING_PACKAGE_1, 1);
-            mInternal.onPackageBroadcast(
+                    mService.mPackageMonitor.onReceive(getTestContext(),
                     genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
             assertEquals(0, mManager.getManifestShortcuts().size());
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
index fcf7ea2..eb4db7a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java
@@ -66,7 +66,7 @@
     private void publishManifestShortcuts(ComponentName activity, int resId) {
         addManifestShortcutResource(activity, resId);
         updatePackageVersion(CALLING_PACKAGE, 1);
-        mInternal.onPackageBroadcast(
+        mService.mPackageMonitor.onReceive(getTestContext(),
                 genPackageAddIntent(CALLING_PACKAGE, USER_0));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutPendingTasksTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutPendingTasksTest.java
deleted file mode 100644
index bf1ed98..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutPendingTasksTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.pm;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Run with:
- adb install \
-   -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
- adb shell am instrument -e class com.android.server.pm.ShortcutPendingTasksTest \
-   -w com.android.frameworks.servicestests
- */
-@LargeTest
-public class ShortcutPendingTasksTest extends BaseShortcutManagerTest {
-    public void testAll() {
-        final AtomicReference<Throwable> thrown = new AtomicReference<>();
-
-        final AtomicBoolean threadCheckerResult = new AtomicBoolean(true);
-
-        final Handler handler = new Handler(Looper.getMainLooper());
-
-        final ShortcutPendingTasks tasks = new ShortcutPendingTasks(
-                handler::post,
-                threadCheckerResult::get,
-                thrown::set);
-
-        // No pending tasks, shouldn't block.
-        assertTrue(tasks.waitOnAllTasks());
-
-        final AtomicInteger counter = new AtomicInteger();
-
-        // Run one task.
-        tasks.addTask(() -> {
-            try {
-                Thread.sleep(1000);
-            } catch (InterruptedException ignore) {
-            }
-            counter.incrementAndGet();
-        });
-
-        assertTrue(tasks.waitOnAllTasks());
-        assertNull(thrown.get());
-
-        assertEquals(1, counter.get());
-
-        // Run 3 tasks.
-
-        // We use this ID to make sure only one task can run at the same time.
-        final AtomicInteger currentTaskId = new AtomicInteger();
-
-        tasks.addTask(() -> {
-            currentTaskId.set(1);
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException ignore) {
-            }
-            counter.incrementAndGet();
-            assertEquals(1, currentTaskId.get());
-        });
-        tasks.addTask(() -> {
-            currentTaskId.set(2);
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException ignore) {
-            }
-            counter.incrementAndGet();
-            assertEquals(2, currentTaskId.get());
-        });
-        tasks.addTask(() -> {
-            currentTaskId.set(3);
-            try {
-                Thread.sleep(500);
-            } catch (InterruptedException ignore) {
-            }
-            counter.incrementAndGet();
-            assertEquals(3, currentTaskId.get());
-        });
-
-        assertTrue(tasks.waitOnAllTasks());
-        assertNull(thrown.get());
-        assertEquals(4, counter.get());
-
-        // No tasks running, shouldn't block.
-        assertTrue(tasks.waitOnAllTasks());
-        assertNull(thrown.get());
-        assertEquals(4, counter.get());
-
-        // Now the thread checker returns false, so waitOnAllTasks() returns false.
-        threadCheckerResult.set(false);
-        assertFalse(tasks.waitOnAllTasks());
-
-        threadCheckerResult.set(true);
-
-        // Make sure the exception handler is called.
-        tasks.addTask(() -> {
-            throw new RuntimeException("XXX");
-        });
-        assertTrue(tasks.waitOnAllTasks());
-        assertNotNull(thrown.get());
-        MoreAsserts.assertContainsRegex("XXX", thrown.get().getMessage());
-    }
-}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index dbc2b0c..473e394 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -220,6 +220,7 @@
     private final Icon mIcon;
     private final Bundle mExtras;
     private boolean mIsEnabled;
+    private String mGroupId;
 
     /**
      * Helper class for creating a {@link PhoneAccount}.
@@ -236,6 +237,7 @@
         private Icon mIcon;
         private Bundle mExtras;
         private boolean mIsEnabled = false;
+        private String mGroupId = "";
 
         /**
          * Creates a builder with the specified {@link PhoneAccountHandle} and label.
@@ -263,6 +265,7 @@
             mIcon = phoneAccount.getIcon();
             mIsEnabled = phoneAccount.isEnabled();
             mExtras = phoneAccount.getExtras();
+            mGroupId = phoneAccount.getGroupId();
         }
 
         /**
@@ -387,6 +390,27 @@
         }
 
         /**
+         * Sets the group Id of the {@link PhoneAccount}. When a new {@link PhoneAccount} is
+         * registered to Telecom, it will replace another {@link PhoneAccount} that is already
+         * registered in Telecom and take on the current user defaults and enabled status. There can
+         * only be one {@link PhoneAccount} with a non-empty group number registered to Telecom at a
+         * time. By default, there is no group Id for a {@link PhoneAccount} (an empty String). Only
+         * grouped {@link PhoneAccount}s with the same {@link ConnectionService} can be replaced.
+         * @param groupId The group Id of the {@link PhoneAccount} that will replace any other
+         * registered {@link PhoneAccount} in Telecom with the same Group Id.
+         * @return The builder
+         * @hide
+         */
+        public Builder setGroupId(String groupId) {
+            if (groupId != null) {
+                mGroupId = groupId;
+            } else {
+                mGroupId = "";
+            }
+            return this;
+        }
+
+        /**
          * Creates an instance of a {@link PhoneAccount} based on the current builder settings.
          *
          * @return The {@link PhoneAccount}.
@@ -408,7 +432,8 @@
                     mShortDescription,
                     mSupportedUriSchemes,
                     mExtras,
-                    mIsEnabled);
+                    mIsEnabled,
+                    mGroupId);
         }
     }
 
@@ -423,7 +448,8 @@
             CharSequence shortDescription,
             List<String> supportedUriSchemes,
             Bundle extras,
-            boolean isEnabled) {
+            boolean isEnabled,
+            String groupId) {
         mAccountHandle = account;
         mAddress = address;
         mSubscriptionAddress = subscriptionAddress;
@@ -435,6 +461,7 @@
         mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
         mExtras = extras;
         mIsEnabled = isEnabled;
+        mGroupId = groupId;
     }
 
     public static Builder builder(
@@ -564,6 +591,21 @@
     }
 
     /**
+     * A non-empty {@link String} representing the group that A {@link PhoneAccount} is in or an
+     * empty {@link String} if the {@link PhoneAccount} is not in a group. If this
+     * {@link PhoneAccount} is in a group, this new {@link PhoneAccount} will replace a registered
+     * {@link PhoneAccount} that is in the same group. When the {@link PhoneAccount} is replaced,
+     * its user defined defaults and enabled status will also pass to this new {@link PhoneAccount}.
+     * Only {@link PhoneAccount}s that share the same {@link ConnectionService} can be replaced.
+     *
+     * @return A non-empty String Id if this {@link PhoneAccount} belongs to a group.
+     * @hide
+     */
+    public String getGroupId() {
+        return mGroupId;
+    }
+
+    /**
      * Determines if the {@link PhoneAccount} supports calls to/from addresses with a specified URI
      * scheme.
      *
@@ -644,6 +686,7 @@
         }
         out.writeByte((byte) (mIsEnabled ? 1 : 0));
         out.writeBundle(mExtras);
+        out.writeString(mGroupId);
     }
 
     public static final Creator<PhoneAccount> CREATOR
@@ -687,6 +730,7 @@
         }
         mIsEnabled = in.readByte() == 1;
         mExtras = in.readBundle();
+        mGroupId = in.readString();
     }
 
     @Override
@@ -704,6 +748,8 @@
         }
         sb.append(" Extras: ");
         sb.append(mExtras);
+        sb.append(" GroupId: ");
+        sb.append(Log.pii(mGroupId));
         sb.append("]");
         return sb.toString();
     }
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 0ff95c4..a2385d6 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -645,11 +645,11 @@
         }
 
         public void onAvailabilityChanged(int status) {
-            postMessage(mModelInfo.name + "Availability changed to: " + status);
+            postMessage(mModelInfo.name + " availability changed to: " + status);
         }
 
         public void onDetected(SoundTriggerDetector.EventPayload event) {
-            postMessage(mModelInfo.name + "onDetected(): " + eventPayloadToString(event));
+            postMessage(mModelInfo.name + " onDetected(): " + eventPayloadToString(event));
             synchronized (SoundTriggerTestService.this) {
                 if (mUserActivity != null) {
                     mUserActivity.handleDetection(mModelInfo.modelUuid);
@@ -661,7 +661,7 @@
         }
 
         public void onError() {
-            postMessage(mModelInfo.name + "onError()");
+            postMessage(mModelInfo.name + " onError()");
             setModelState(mModelInfo, "Error");
         }
 
@@ -671,7 +671,7 @@
         }
 
         public void onRecognitionResumed() {
-            postMessage(mModelInfo.name + "onRecognitionResumed()");
+            postMessage(mModelInfo.name + " onRecognitionResumed()");
             setModelState(mModelInfo, "Resumed");
         }
     }
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 4e4da8b..49ab9f9 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -590,4 +590,9 @@
 
     @Override
     public void removeWallpaperInputConsumer() throws RemoteException {}
+
+    @Override
+    public Bitmap screenshotWallpaper() throws RemoteException {
+        return null;
+    }
 }