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 0e04e30..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 53fe6ce..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;
+ }
}