Merge "Update message on DISALLOW_REMOVE_USERS constant." into lmp-dev
diff --git a/Android.mk b/Android.mk
index 9cda341..7a8907e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -241,6 +241,7 @@
core/java/android/view/IWindowManager.aidl \
core/java/android/view/IWindowSession.aidl \
core/java/android/view/IWindowSessionCallback.aidl \
+ core/java/android/webkit/IWebViewUpdateService.aidl \
core/java/android/speech/IRecognitionListener.aidl \
core/java/android/speech/IRecognitionService.aidl \
core/java/android/speech/tts/ITextToSpeechCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index abf7ea2..d35610b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27253,6 +27253,7 @@
method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
+ method public int setAudioAttributes(android.media.AudioAttributes);
method public deprecated int setEngineByPackageName(java.lang.String);
method public int setLanguage(java.util.Locale);
method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8caea25..afac239 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -131,6 +131,12 @@
public static final int PACKAGE_INFO_GID = 1032;
/**
+ * Defines the UID/GID for the shared RELRO file updater process.
+ * @hide
+ */
+ public static final int SHARED_RELRO_UID = 1037;
+
+ /**
* Defines the start of a range of UIDs (and GIDs), going from this
* number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
* to applications.
diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
index c13b47f..b4ac429 100644
--- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
@@ -54,7 +54,11 @@
final UtteranceProgressDispatcher dispatcher = getDispatcher();
dispatcher.dispatchOnStart();
- mPlayer = MediaPlayer.create(mContext, mUri);
+
+ int sessionId = mAudioParams.mSessionId;
+ mPlayer = MediaPlayer.create(
+ mContext, mUri, null, mAudioParams.mAudioAttributes,
+ sessionId > 0 ? sessionId : AudioSystem.AUDIO_SESSION_ALLOCATE);
if (mPlayer == null) {
dispatcher.dispatchOnError(TextToSpeech.ERROR_OUTPUT);
return;
@@ -76,11 +80,8 @@
mDone.open();
}
});
- mPlayer.setAudioStreamType(mAudioParams.mStreamType);
+
setupVolume(mPlayer, mAudioParams.mVolume, mAudioParams.mPan);
- if (mAudioParams.mSessionId != AudioSystem.AUDIO_SESSION_ALLOCATE) {
- mPlayer.setAudioSessionId(mAudioParams.mSessionId);
- }
mPlayer.start();
mDone.block();
finish();
diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java
index b405de0..dc4e9d3 100644
--- a/core/java/android/speech/tts/BlockingAudioTrack.java
+++ b/core/java/android/speech/tts/BlockingAudioTrack.java
@@ -2,6 +2,7 @@
package android.speech.tts;
+import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.speech.tts.TextToSpeechService.AudioOutputParams;
@@ -214,9 +215,14 @@
= AudioTrack.getMinBufferSize(mSampleRateInHz, channelConfig, mAudioFormat);
int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
- AudioTrack audioTrack = new AudioTrack(mAudioParams.mStreamType, mSampleRateInHz,
- channelConfig, mAudioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
+ AudioFormat audioFormat = (new AudioFormat.Builder())
+ .setChannelMask(channelConfig)
+ .setEncoding(mAudioFormat)
+ .setSampleRate(mSampleRateInHz).build();
+ AudioTrack audioTrack = new AudioTrack(mAudioParams.mAudioAttributes,
+ audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM,
mAudioParams.mSessionId);
+
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
Log.w(TAG, "Unable to create audio track.");
audioTrack.release();
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 46077ed..d8b9b5f 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
import android.os.AsyncTask;
@@ -524,6 +525,17 @@
public static final String KEY_PARAM_STREAM = "streamType";
/**
+ * Parameter key to specify the audio attributes to be used when
+ * speaking text or playing back a file. The value should be set
+ * using {@link TextToSpeech#setAudioAttributes(AudioAttributes)}.
+ *
+ * @see TextToSpeech#speak(String, int, HashMap)
+ * @see TextToSpeech#playEarcon(String, int, HashMap)
+ * @hide
+ */
+ public static final String KEY_PARAM_AUDIO_ATTRIBUTES = "audioAttributes";
+
+ /**
* Parameter key to identify an utterance in the
* {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
* spoken, a file has been played back or a silence duration has elapsed.
@@ -1357,6 +1369,25 @@
}
/**
+ * Sets the audio attributes to be used when speaking text or playing
+ * back a file.
+ *
+ * @param audioAttributes Valid AudioAttributes instance.
+ *
+ * @return {@link #ERROR} or {@link #SUCCESS}.
+ */
+ public int setAudioAttributes(AudioAttributes audioAttributes) {
+ if (audioAttributes != null) {
+ synchronized (mStartLock) {
+ mParams.putParcelable(Engine.KEY_PARAM_AUDIO_ATTRIBUTES,
+ audioAttributes);
+ }
+ return SUCCESS;
+ }
+ return ERROR;
+ }
+
+ /**
* @return the engine currently in use by this TextToSpeech instance.
* @hide
*/
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a0743f7..4fea109 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -17,7 +17,7 @@
import android.app.Service;
import android.content.Intent;
-import android.media.AudioManager;
+import android.media.AudioAttributes;
import android.media.AudioSystem;
import android.net.Uri;
import android.os.Binder;
@@ -608,12 +608,6 @@
public final int mSessionId;
/**
- * Audio stream type. Must be one of the STREAM_ contants defined in
- * {@link android.media.AudioManager}.
- */
- public final int mStreamType;
-
- /**
* Volume, in the range [0.0f, 1.0f]. The default value is
* {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
*/
@@ -625,42 +619,62 @@
*/
public final float mPan;
+
+ /**
+ * Audio attributes, set by {@link TextToSpeech#setAudioAttributes}
+ * or created from the value of {@link TextToSpeech.Engine#KEY_PARAM_STREAM}.
+ */
+ public final AudioAttributes mAudioAttributes;
+
/** Create AudioOutputParams with default values */
AudioOutputParams() {
mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
- mStreamType = Engine.DEFAULT_STREAM;
mVolume = Engine.DEFAULT_VOLUME;
mPan = Engine.DEFAULT_PAN;
+ mAudioAttributes = null;
}
- AudioOutputParams(int sessionId, int streamType, float volume, float pan) {
+ AudioOutputParams(int sessionId, float volume, float pan,
+ AudioAttributes audioAttributes) {
mSessionId = sessionId;
- mStreamType = streamType;
mVolume = volume;
mPan = pan;
+ mAudioAttributes = audioAttributes;
}
/** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */
- static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle) {
+ static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle,
+ boolean isSpeech) {
if (paramsBundle == null) {
return new AudioOutputParams();
}
+ AudioAttributes audioAttributes =
+ (AudioAttributes) paramsBundle.getParcelable(
+ Engine.KEY_PARAM_AUDIO_ATTRIBUTES);
+ if (audioAttributes == null) {
+ int streamType = paramsBundle.getInt(
+ Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
+ audioAttributes = (new AudioAttributes.Builder())
+ .setLegacyStreamType(streamType)
+ .setContentType((isSpeech ?
+ AudioAttributes.CONTENT_TYPE_SPEECH :
+ AudioAttributes.CONTENT_TYPE_SONIFICATION))
+ .build();
+ }
+
return new AudioOutputParams(
paramsBundle.getInt(
Engine.KEY_PARAM_SESSION_ID,
AudioSystem.AUDIO_SESSION_ALLOCATE),
- paramsBundle.getInt(
- Engine.KEY_PARAM_STREAM,
- Engine.DEFAULT_STREAM),
paramsBundle.getFloat(
Engine.KEY_PARAM_VOLUME,
Engine.DEFAULT_VOLUME),
paramsBundle.getFloat(
Engine.KEY_PARAM_PAN,
- Engine.DEFAULT_PAN));
+ Engine.DEFAULT_PAN),
+ audioAttributes);
}
-
}
@@ -832,7 +846,7 @@
}
AudioOutputParams getAudioParams() {
- return AudioOutputParams.createFromV1ParamsBundle(mParams);
+ return AudioOutputParams.createFromV1ParamsBundle(mParams, true);
}
}
@@ -1005,6 +1019,11 @@
public String getUtteranceId() {
return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null);
}
+
+ @Override
+ AudioOutputParams getAudioParams() {
+ return AudioOutputParams.createFromV1ParamsBundle(mParams, false);
+ }
}
private class SilenceSpeechItem extends UtteranceSpeechItem {
diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
new file mode 100644
index 0000000..a77459b
--- /dev/null
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.webkit;
+
+/**
+ * Private service to wait for the updatable WebView to be ready for use.
+ * @hide
+ */
+interface IWebViewUpdateService {
+
+ /**
+ * Used by the relro file creator to notify the service that it's done.
+ */
+ void notifyRelroCreationCompleted(boolean is64Bit, boolean success);
+
+ /**
+ * Used by WebViewFactory to block loading of WebView code until
+ * preparations are complete.
+ */
+ void waitForRelroCreationCompleted(boolean is64Bit);
+
+}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index aaf0a75..13be453 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,9 +16,18 @@
package android.webkit;
+import android.os.Build;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.StrictMode;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+
+import com.android.internal.os.Zygote;
/**
* Top level factory, used creating all the main WebView implementation classes.
@@ -33,25 +42,26 @@
private static final String NULL_WEBVIEW_FACTORY =
"com.android.webview.nullwebview.NullWebViewFactoryProvider";
+ // TODO(torne): we need to use a system property instead of hardcoding the library paths to
+ // enable it to be changed when a webview update apk is installed.
+ private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_32 =
+ "/system/lib/libwebviewchromium.so";
+ private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_64 =
+ "/system/lib64/libwebviewchromium.so";
+ private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
+ "/data/misc/shared_relro/libwebviewchromium32.relro";
+ private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
+ "/data/misc/shared_relro/libwebviewchromium64.relro";
+
private static final String LOGTAG = "WebViewFactory";
private static final boolean DEBUG = false;
- private static class Preloader {
- static WebViewFactoryProvider sPreloadedProvider;
- static {
- try {
- sPreloadedProvider = getFactoryClass().newInstance();
- } catch (Exception e) {
- Log.w(LOGTAG, "error preloading provider", e);
- }
- }
- }
-
// Cache the factory both for efficiency, and ensure any one process gets all webviews from the
// same provider.
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
+ private static boolean sAddressSpaceReserved = false;
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
@@ -59,6 +69,8 @@
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
+ loadNativeLibrary();
+
Class<WebViewFactoryProvider> providerClass;
try {
providerClass = getFactoryClass();
@@ -67,15 +79,6 @@
throw new AndroidRuntimeException(e);
}
- // This implicitly loads Preloader even if it wasn't preloaded at boot.
- if (Preloader.sPreloadedProvider != null &&
- Preloader.sPreloadedProvider.getClass() == providerClass) {
- sProviderInstance = Preloader.sPreloadedProvider;
- if (DEBUG) Log.v(LOGTAG, "Using preloaded provider: " + sProviderInstance);
- return sProviderInstance;
- }
-
- // The preloaded provider isn't the one we wanted; construct our own.
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
sProviderInstance = providerClass.newInstance();
@@ -98,4 +101,121 @@
return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
}
}
+
+ /**
+ * Perform any WebView loading preparations that must happen in the zygote.
+ * Currently, this means allocating address space to load the real JNI library later.
+ */
+ public static void prepareWebViewInZygote() {
+ try {
+ System.loadLibrary("webviewchromium_loader");
+ sAddressSpaceReserved = nativeReserveAddressSpace(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
+ CHROMIUM_WEBVIEW_NATIVE_LIB_64);
+ if (sAddressSpaceReserved) {
+ if (DEBUG) Log.v(LOGTAG, "address space reserved");
+ } else {
+ Log.e(LOGTAG, "reserving address space failed");
+ }
+ } catch (Throwable e) {
+ // Log and discard errors at this stage as we must not crash the zygote.
+ Log.e(LOGTAG, "error preparing native loader", e);
+ }
+ }
+
+ /**
+ * Perform any WebView loading preparations that must happen at boot from the system server,
+ * after the package manager has started.
+ * This must be called in the system server.
+ * Currently, this means spawning the child processes which will create the relro files.
+ */
+ public static void prepareWebViewInSystemServer() {
+ if (DEBUG) Log.v(LOGTAG, "creating relro files");
+ if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_64).exists()) {
+ createRelroFile(Build.SUPPORTED_64_BIT_ABIS[0]);
+ }
+ if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) {
+ createRelroFile(Build.SUPPORTED_32_BIT_ABIS[0]);
+ }
+ }
+
+ private static void createRelroFile(String abi) {
+ try {
+ Process.start("android.webkit.WebViewFactory$RelroFileCreator",
+ "WebViewLoader-" + abi,
+ Process.SHARED_RELRO_UID,
+ Process.SHARED_RELRO_UID,
+ null,
+ 0, // TODO(torne): do we need to set debug flags?
+ Zygote.MOUNT_EXTERNAL_NONE,
+ Build.VERSION.SDK_INT,
+ null,
+ abi,
+ null);
+ } catch (Throwable e) {
+ // Log and discard errors as we must not crash the system server.
+ Log.e(LOGTAG, "error starting relro file creator for abi " + abi, e);
+ }
+ }
+
+ private static class RelroFileCreator {
+ // Called in an unprivileged child process to create the relro file.
+ public static void main(String[] args) {
+ if (!sAddressSpaceReserved) {
+ Log.e(LOGTAG, "can't create relro file; address space not reserved");
+ return;
+ }
+ boolean result = nativeCreateRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
+ CHROMIUM_WEBVIEW_NATIVE_LIB_64,
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+ if (!result) {
+ Log.e(LOGTAG, "failed to create relro file");
+ } else if (DEBUG) {
+ Log.v(LOGTAG, "created relro file");
+ }
+ try {
+ getUpdateService().notifyRelroCreationCompleted(VMRuntime.getRuntime().is64Bit(),
+ result);
+ } catch (RemoteException e) {
+ Log.e(LOGTAG, "error notifying update service", e);
+ }
+
+ // Must explicitly exit or else this process will just sit around after we return.
+ System.exit(0);
+ }
+ }
+
+ private static void loadNativeLibrary() {
+ if (!sAddressSpaceReserved) {
+ Log.e(LOGTAG, "can't load with relro file; address space not reserved");
+ return;
+ }
+
+ try {
+ getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit());
+ } catch (RemoteException e) {
+ Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e);
+ return;
+ }
+
+ boolean result = nativeLoadWithRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
+ CHROMIUM_WEBVIEW_NATIVE_LIB_64,
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+ if (!result) {
+ Log.w(LOGTAG, "failed to load with relro file, proceeding without");
+ } else if (DEBUG) {
+ Log.v(LOGTAG, "loaded with relro file");
+ }
+ }
+
+ private static IWebViewUpdateService getUpdateService() {
+ return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
+ }
+
+ private static native boolean nativeReserveAddressSpace(String lib32, String lib64);
+ private static native boolean nativeCreateRelroFile(String lib32, String lib64,
+ String relro32, String relro64);
+ private static native boolean nativeLoadWithRelroFile(String lib32, String lib64,
+ String relro32, String relro64);
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5ce658b..eea4201 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -34,6 +34,7 @@
import android.system.OsConstants;
import android.util.EventLog;
import android.util.Log;
+import android.webkit.WebViewFactory;
import dalvik.system.VMRuntime;
@@ -250,6 +251,9 @@
preloadClasses();
preloadResources();
preloadOpenGL();
+ // Ask the WebViewFactory to do any initialization that must run in the zygote process,
+ // for memory sharing purposes.
+ WebViewFactory.prepareWebViewInZygote();
Log.d(TAG, "end preload");
}
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 11f50ee..8b1c2b7 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -78,4 +78,11 @@
android:orientation="vertical"
android:paddingTop="3dp" />
+ <TextView
+ android:id="@+id/zen_alarm_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/qs_panel_padding"
+ android:gravity="center"
+ android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
</com.android.systemui.volume.ZenModePanel>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a7f894c..a1064fd 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -38,6 +38,7 @@
<color name="system_secondary_color">#ff384248</color>
<color name="system_accent_color">#ff80CBC4</color><!-- deep teal 200 -->
<color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
+ <color name="qs_text">#FFFFFFFF</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
<color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 28b0853..c8c8e9a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -617,8 +617,8 @@
<!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
<string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
- <!-- Zen mode: No interruptions title, with a warning about alarms and timers. [CHAR LIMIT=60] -->
- <string name="zen_no_interruptions_with_warning">No interruptions, including alarms and timers</string>
+ <!-- Zen mode: No interruptions title, with a warning about alarms. [CHAR LIMIT=60] -->
+ <string name="zen_no_interruptions_with_warning">No interruptions, including alarms</string>
<!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
<string name="zen_no_interruptions">No interruptions</string>
@@ -626,6 +626,15 @@
<!-- Zen mode: Only important interruptions. [CHAR LIMIT=40] -->
<string name="zen_important_interruptions">Priority interruptions only</string>
+ <!-- Zen mode: Next alarm information - just a time. [CHAR LIMIT=40] -->
+ <string name="zen_alarm_information_time">Your next alarm is at <xliff:g id="alarm_time" example="5:00 PM">%s</xliff:g></string>
+
+ <!-- Zen mode: Next alarm information - day and time. [CHAR LIMIT=40] -->
+ <string name="zen_alarm_information_day_time">Your next alarm is <xliff:g id="alarm_day_and_time" example="Fri 5:00 PM">%s</xliff:g></string>
+
+ <!-- Zen mode: Next alarm warning. [CHAR LIMIT=40] -->
+ <string name="zen_alarm_warning">You won\'t hear your alarm at <xliff:g id="alarm_time" example="5:00 PM">%s</xliff:g></string>
+
<!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
<string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 708d3e8..5cc987a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -154,7 +154,7 @@
<style name="TextAppearance.QS">
<item name="android:textStyle">normal</item>
- <item name="android:textColor">#ffffff</item>
+ <item name="android:textColor">@color/qs_text</item>
<item name="android:fontFamily">sans-serif</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 8564409..72c12d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -549,6 +549,8 @@
mDozeServiceHost = new DozeServiceHost();
putComponent(DozeService.Host.class, mDozeServiceHost);
+
+ setControllerUsers();
}
// ================================================================================
@@ -2797,6 +2799,13 @@
animateCollapsePanels();
updateNotifications();
resetUserSetupObserver();
+ setControllerUsers();
+ }
+
+ private void setControllerUsers() {
+ if (mZenModeController != null) {
+ mZenModeController.setUserId(mCurrentUserId);
+ }
}
private void resetUserSetupObserver() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index dc06ec2..574d536 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -285,9 +285,6 @@
mDateExpanded.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
mSettingsButton.setVisibility(mExpanded && !mOverscrolled ? View.VISIBLE : View.GONE);
mQsDetailHeader.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
- if (mStatusIcons != null) {
- mStatusIcons.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
- }
if (mSignalCluster != null) {
mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
}
@@ -440,12 +437,13 @@
public void attachSystemIcons(LinearLayout systemIcons) {
mSystemIconsContainer.addView(systemIcons);
mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
+ mStatusIcons.addOnLayoutChangeListener(mStatusIconsChanged);
mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
}
public void onSystemIconsDetached() {
if (mStatusIcons != null) {
- mStatusIcons.setVisibility(View.VISIBLE);
+ mStatusIcons.removeOnLayoutChangeListener(mStatusIconsChanged);
}
if (mSignalCluster != null) {
mSignalCluster.setVisibility(View.VISIBLE);
@@ -530,6 +528,23 @@
// here.
}
+ private final OnLayoutChangeListener mStatusIconsChanged = new OnLayoutChangeListener() {
+ private final Rect mClipBounds = new Rect();
+
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ // Hide the statusIcons in the header by clipping them. Can't touch visibility since
+ // they are mirrored to the real status bar.
+ final Rect r = mSystemIconsContainer.getClipBounds();
+ if (r == null || r.left != right) {
+ mClipBounds.set(right, 0, mSystemIconsContainer.getWidth(),
+ mSystemIconsContainer.getHeight());
+ mSystemIconsContainer.setClipBounds(mClipBounds);
+ }
+ }
+ };
+
private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
@Override
public void onToggleStateChanged(final boolean state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 61902a2..2e97d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -27,11 +27,13 @@
void requestConditions(boolean request);
void setExitConditionId(Uri exitConditionId);
Uri getExitConditionId();
- boolean hasNextAlarm();
+ long getNextAlarm();
+ void setUserId(int userId);
public static class Callback {
public void onZenChanged(int zen) {}
public void onExitConditionChanged(Uri exitConditionId) {}
public void onConditionsChanged(Condition[] conditions) {}
+ public void onNextAlarmChanged() {}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 7703966..ae037f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,19 +16,25 @@
package com.android.systemui.statusbar.policy;
+import android.app.AlarmClockInfo;
+import android.app.AlarmManager;
import android.app.INotificationManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.IConditionListener;
import android.service.notification.ZenModeConfig;
+import android.util.Log;
import android.util.Slog;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.qs.GlobalSetting;
import java.util.ArrayList;
@@ -36,8 +42,8 @@
/** Platform implementation of the zen mode controller. **/
public class ZenModeControllerImpl implements ZenModeController {
- private static final String TAG = "ZenModeControllerImpl";
- private static final boolean DEBUG = false;
+ private static final String TAG = "ZenModeController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final Context mContext;
@@ -45,13 +51,13 @@
private final GlobalSetting mConfigSetting;
private final INotificationManager mNoMan;
private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
- private final LockPatternUtils mUtils;
+ private final AlarmManager mAlarmManager;
+ private int mUserId;
private boolean mRequesting;
public ZenModeControllerImpl(Context context, Handler handler) {
mContext = context;
- mUtils = new LockPatternUtils(mContext);
mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
@Override
protected void handleValueChanged(int value) {
@@ -68,6 +74,7 @@
mConfigSetting.setListening(true);
mNoMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
@Override
@@ -126,8 +133,22 @@
}
@Override
- public boolean hasNextAlarm() {
- return mUtils.getNextAlarm() != null;
+ public long getNextAlarm() {
+ final AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId);
+ return info != null ? info.getTriggerTime() : 0;
+ }
+
+ @Override
+ public void setUserId(int userId) {
+ mUserId = userId;
+ mContext.registerReceiverAsUser(mReceiver, new UserHandle(mUserId),
+ new IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED), null, null);
+ }
+
+ private void fireNextAlarmChanged() {
+ for (Callback cb : mCallbacks) {
+ cb.onNextAlarmChanged();
+ }
}
private void fireZenChanged(int zen) {
@@ -169,4 +190,13 @@
updateConditions(conditions);
}
};
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(intent.getAction())) {
+ fireNextAlarmChanged();
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index cf04219..33cf3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -30,6 +30,8 @@
import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
+import android.text.format.DateFormat;
+import android.text.format.Time;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -46,7 +48,10 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.ZenModeController;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
import java.util.Objects;
public class ZenModePanel extends LinearLayout {
@@ -75,6 +80,8 @@
private final H mHandler = new H();
private final Favorites mFavorites;
private final Interpolator mFastOutSlowInInterpolator;
+ private final int mTextColor;
+ private final int mAccentColor;
private char mLogTag = '?';
private String mTag;
@@ -85,6 +92,7 @@
private TextView mZenSubheadExpanded;
private View mMoreSettings;
private LinearLayout mZenConditions;
+ private TextView mAlarmWarning;
private Callback mCallback;
private ZenModeController mController;
@@ -92,8 +100,10 @@
private Uri mExitConditionId;
private int mBucketIndex = -1;
private boolean mExpanded;
- private int mAttachedZen;
+ private int mSessionZen;
+ private Uri mSessionExitConditionId;
private String mExitConditionText;
+ private long mNextAlarm;
public ZenModePanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -102,6 +112,8 @@
mInflater = LayoutInflater.from(mContext.getApplicationContext());
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.fast_out_slow_in);
+ mTextColor = mContext.getResources().getColor(R.color.qs_text);
+ mAccentColor = mContext.getResources().getColor(R.color.system_accent_color);
updateTag();
if (DEBUG) Log.d(mTag, "new ZenModePanel");
}
@@ -144,14 +156,17 @@
});
mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
+ mAlarmWarning = (TextView) findViewById(R.id.zen_alarm_warning);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (DEBUG) Log.d(mTag, "onAttachedToWindow");
- mAttachedZen = getSelectedZen(-1);
+ mSessionZen = getSelectedZen(-1);
+ mSessionExitConditionId = mExitConditionId;
refreshExitConditionText();
+ refreshNextAlarm();
updateWidgets();
}
@@ -159,7 +174,8 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
- mAttachedZen = -1;
+ mSessionZen = -1;
+ mSessionExitConditionId = null;
setExpanded(false);
}
@@ -202,7 +218,7 @@
updateTag();
setExitConditionId(mController.getExitConditionId());
refreshExitConditionText();
- mAttachedZen = getSelectedZen(-1);
+ mSessionZen = getSelectedZen(-1);
handleUpdateZen(mController.getZen());
if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
mZenConditions.removeAllViews();
@@ -213,6 +229,7 @@
if (Objects.equals(mExitConditionId, exitConditionId)) return;
mExitConditionId = exitConditionId;
refreshExitConditionText();
+ updateWidgets();
}
private void refreshExitConditionText() {
@@ -248,9 +265,9 @@
}
private void handleUpdateZen(int zen) {
- if (mAttachedZen != -1 && mAttachedZen != zen) {
+ if (mSessionZen != -1 && mSessionZen != zen) {
setExpanded(zen != Global.ZEN_MODE_OFF);
- mAttachedZen = zen;
+ mSessionZen = zen;
}
mZenButtons.setSelectedValue(zen);
updateWidgets();
@@ -267,12 +284,34 @@
final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
final boolean foreverSelected = mExitConditionId == null;
+ final boolean hasNextAlarm = mNextAlarm != 0;
mZenSubhead.setVisibility(!zenOff && (mExpanded || !foreverSelected) ? VISIBLE : GONE);
mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE);
mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
+ mAlarmWarning.setVisibility(zenNone && mExpanded && hasNextAlarm ? VISIBLE : GONE);
+
+ if (zenNone && mExpanded && hasNextAlarm) {
+ final long exitTime = ZenModeConfig.tryParseCountdownConditionId(mExitConditionId);
+ final long now = System.currentTimeMillis();
+ final boolean alarmToday = time(mNextAlarm).yearDay == time(now).yearDay;
+ final String skeleton = (alarmToday ? "" : "E")
+ + (DateFormat.is24HourFormat(mContext) ? "Hm" : "hma");
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ final String alarm = new SimpleDateFormat(pattern).format(new Date(mNextAlarm));
+ final boolean isWarning = exitTime > 0 && mNextAlarm > now && mNextAlarm < exitTime;
+ if (isWarning) {
+ mAlarmWarning.setText(mContext.getString(R.string.zen_alarm_warning, alarm));
+ mAlarmWarning.setTextColor(mAccentColor);
+ } else {
+ mAlarmWarning.setText(mContext.getString(alarmToday
+ ? R.string.zen_alarm_information_time
+ : R.string.zen_alarm_information_day_time, alarm));
+ mAlarmWarning.setTextColor(mTextColor);
+ }
+ }
if (zenNone) {
mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning);
@@ -283,6 +322,12 @@
}
}
+ private static Time time(long millis) {
+ final Time t = new Time();
+ t.set(millis);
+ return t;
+ }
+
private Condition parseExistingTimeCondition(Uri conditionId) {
final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
if (time == 0) return null;
@@ -308,6 +353,15 @@
Condition.FLAG_RELEVANT_NOW);
}
+ private void refreshNextAlarm() {
+ mNextAlarm = mController.getNextAlarm();
+ }
+
+ private void handleNextAlarmChanged() {
+ refreshNextAlarm();
+ updateWidgets();
+ }
+
private void handleUpdateConditions(Condition[] conditions) {
final int newCount = conditions == null ? 0 : conditions.length;
if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
@@ -328,6 +382,8 @@
// are we left without anything selected? if so, set a default
for (int i = 0; i < mZenConditions.getChildCount(); i++) {
if (getConditionTagAt(i).rb.isChecked()) {
+ if (DEBUG) Log.d(mTag, "Not selecting a default, checked="
+ + getConditionTagAt(i).conditionId);
return;
}
}
@@ -371,7 +427,7 @@
}
tag.conditionId = condition != null ? condition.id : null;
tag.rb.setEnabled(enabled);
- if (Objects.equals(tag.conditionId, mExitConditionId)) {
+ if (mSessionExitConditionId != null && mSessionExitConditionId.equals(tag.conditionId)) {
tag.rb.setChecked(true);
}
tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@@ -484,6 +540,7 @@
} else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
mFavorites.setMinuteIndex(mBucketIndex);
}
+ mSessionExitConditionId = conditionId;
}
private void fireMoreSettings() {
@@ -518,12 +575,18 @@
public void onExitConditionChanged(Uri exitConditionId) {
mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget();
}
+
+ @Override
+ public void onNextAlarmChanged() {
+ mHandler.sendEmptyMessage(H.NEXT_ALARM_CHANGED);
+ }
};
private final class H extends Handler {
private static final int UPDATE_CONDITIONS = 1;
private static final int EXIT_CONDITION_CHANGED = 2;
private static final int UPDATE_ZEN = 3;
+ private static final int NEXT_ALARM_CHANGED = 4;
private H() {
super(Looper.getMainLooper());
@@ -532,12 +595,14 @@
@Override
public void handleMessage(Message msg) {
if (msg.what == UPDATE_CONDITIONS) {
- handleUpdateConditions((Condition[])msg.obj);
+ handleUpdateConditions((Condition[]) msg.obj);
checkForDefault();
} else if (msg.what == EXIT_CONDITION_CHANGED) {
- handleExitConditionChanged((Uri)msg.obj);
+ handleExitConditionChanged((Uri) msg.obj);
} else if (msg.what == UPDATE_ZEN) {
handleUpdateZen(msg.arg1);
+ } else if (msg.what == NEXT_ALARM_CHANGED) {
+ handleNextAlarmChanged();
}
}
}
@@ -601,7 +666,6 @@
if (value != null && mZenButtons.isShown()) {
if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value);
mController.setZen((Integer) value);
- mController.setExitConditionId(null);
}
}
};
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index edeb5b4..abbaacc 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2271,6 +2271,22 @@
}
@Override
+ public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
+ if (mOutsetBottom != null) {
+ final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+ int bottom = (int) mOutsetBottom.getDimension(metrics);
+ WindowInsets newInsets = insets.replaceSystemWindowInsets(
+ insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ insets.getSystemWindowInsetBottom() + bottom);
+ return super.dispatchApplyWindowInsets(newInsets);
+ } else {
+ return super.dispatchApplyWindowInsets(insets);
+ }
+ }
+
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
return onInterceptTouchEvent(event);
}
diff --git a/preloaded-classes b/preloaded-classes
index 4413cd3..b3fc517 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1208,7 +1208,6 @@
android.webkit.WebViewClient
android.webkit.WebViewDatabase
android.webkit.WebViewFactory
-android.webkit.WebViewFactory$Preloader
android.webkit.WebViewFactoryProvider
android.webkit.WebViewFactoryProvider$Statics
android.webkit.WebViewProvider
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 65cb6c9..09cf392 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -269,13 +269,39 @@
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.setComponent(component);
launchIntent.setSourceBounds(sourceBounds);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ launchIntent.setPackage(component.getPackageName());
long ident = Binder.clearCallingIdentity();
try {
- mContext.startActivityAsUser(launchIntent, opts, user);
+ IPackageManager pm = AppGlobals.getPackageManager();
+ ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
+ if (!info.exported) {
+ throw new SecurityException("Cannot launch non-exported components "
+ + component);
+ }
+
+ // Check that the component actually has Intent.CATEGORY_LAUCNCHER
+ // as calling startActivityAsUser ignores the category and just
+ // resolves based on the component if present.
+ List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent,
+ PackageManager.NO_CROSS_PROFILE, // We only want the apps for this user
+ user.getIdentifier());
+ final int size = apps.size();
+ for (int i = 0; i < size; ++i) {
+ ActivityInfo activityInfo = apps.get(i).activityInfo;
+ if (activityInfo.packageName.equals(component.getPackageName()) &&
+ activityInfo.name.equals(component.getClassName())) {
+ // Found an activity with category launcher that matches
+ // this component so ok to launch.
+ launchIntent.setComponent(component);
+ mContext.startActivityAsUser(launchIntent, opts, user);
+ return;
+ }
+ }
+ throw new SecurityException("Attempt to launch activity without "
+ + " category Intent.CATEGORY_LAUNCHER " + component);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a3fd1df..e16894e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3171,25 +3171,34 @@
file.delete();
file = getUserPackagesStateBackupFile(userId);
file.delete();
- removeCrossProfileIntentFiltersToUserLPr(userId);
+ removeCrossProfileIntentFiltersLPw(userId);
removeCrossProfilePackagesLPw(userId);
}
- void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) {
- for (int i = 0; i < mCrossProfileIntentResolvers.size(); i++) {
- int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
- CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
- boolean needsWriting = false;
- HashSet<CrossProfileIntentFilter> cpifs =
- new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
- for (CrossProfileIntentFilter cpif : cpifs) {
- if (cpif.getTargetUserId() == targetUserId) {
- needsWriting = true;
- cpir.removeFilter(cpif);
- }
+ void removeCrossProfileIntentFiltersLPw(int userId) {
+ synchronized (mCrossProfileIntentResolvers) {
+ // userId is the source user
+ if (mCrossProfileIntentResolvers.get(userId) != null) {
+ mCrossProfileIntentResolvers.remove(userId);
+ writePackageRestrictionsLPr(userId);
}
- if (needsWriting) {
- writePackageRestrictionsLPr(sourceUserId);
+ // userId is the target user
+ int count = mCrossProfileIntentResolvers.size();
+ for (int i = 0; i < count; i++) {
+ int sourceUserId = mCrossProfileIntentResolvers.keyAt(i);
+ CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId);
+ boolean needsWriting = false;
+ HashSet<CrossProfileIntentFilter> cpifs =
+ new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
+ for (CrossProfileIntentFilter cpif : cpifs) {
+ if (cpif.getTargetUserId() == userId) {
+ needsWriting = true;
+ cpir.removeFilter(cpif);
+ }
+ }
+ if (needsWriting) {
+ writePackageRestrictionsLPr(sourceUserId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
new file mode 100644
index 0000000..e8ae97c
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 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.webkit;
+
+import android.os.Binder;
+import android.os.Process;
+import android.util.Log;
+import android.webkit.IWebViewUpdateService;
+
+/**
+ * Private service to wait for the updatable WebView to be ready for use.
+ * @hide
+ */
+public class WebViewUpdateService extends IWebViewUpdateService.Stub {
+
+ private static final String TAG = "WebViewUpdateService";
+
+ private boolean mRelroReady32Bit = false;
+ private boolean mRelroReady64Bit = false;
+
+ public WebViewUpdateService() {
+ }
+
+ /**
+ * The shared relro process calls this to notify us that it's done trying to create a relro
+ * file.
+ */
+ public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) {
+ // Verify that the caller is the shared relro process.
+ if (Binder.getCallingUid() != Process.SHARED_RELRO_UID) {
+ return;
+ }
+
+ synchronized (this) {
+ if (is64Bit) {
+ mRelroReady64Bit = true;
+ } else {
+ mRelroReady32Bit = true;
+ }
+ this.notifyAll();
+ }
+ }
+
+ /**
+ * WebViewFactory calls this to block WebView loading until the relro file is created.
+ */
+ public void waitForRelroCreationCompleted(boolean is64Bit) {
+ synchronized (this) {
+ if (is64Bit) {
+ while (!mRelroReady64Bit) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {}
+ }
+ } else {
+ while (!mRelroReady32Bit) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {}
+ }
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8d38827..a6030cf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -49,6 +49,7 @@
import android.util.Log;
import android.util.Slog;
import android.view.WindowManager;
+import android.webkit.WebViewFactory;
import com.android.internal.R;
import com.android.internal.os.BinderInternal;
@@ -91,6 +92,7 @@
import com.android.server.twilight.TwilightService;
import com.android.server.usb.UsbService;
import com.android.server.wallpaper.WallpaperManagerService;
+import com.android.server.webkit.WebViewUpdateService;
import com.android.server.wm.WindowManagerService;
import dalvik.system.VMRuntime;
@@ -408,6 +410,12 @@
Slog.i(TAG, "Reading configuration...");
SystemConfig.getInstance();
+ Slog.i(TAG, "WebView Update Service");
+ ServiceManager.addService("webviewupdate", new WebViewUpdateService());
+
+ Slog.i(TAG, "WebViewFactory preparation");
+ WebViewFactory.prepareWebViewInSystemServer();
+
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());