Merge "QS: Back button closes detail (if showing)" into lmp-dev
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 5262a5f..2a17fa6 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -25,4 +25,6 @@
// Called by the power manager.
public abstract void goingToSleep();
public abstract void wakingUp();
+ public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
+ String processName, String abiOverride, int uid, Runnable crashHandler);
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5347f03..772e132 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -454,7 +454,7 @@
* Private non-Binder interfaces
*/
/* package */ boolean testIsSystemReady();
-
+
/** Information you can retrieve about a particular application. */
public static class ContentProviderHolder implements Parcelable {
public final ProviderInfo info;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 6278e69..5248131 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -50,14 +50,17 @@
/**
* Indicates that recognition for the given keyphrase is not available on the system
* because of the hardware configuration.
+ * No further interaction should be performed with the detector that returns this availability.
*/
public static final int STATE_HARDWARE_UNAVAILABLE = -2;
/**
* Indicates that recognition for the given keyphrase is not supported.
+ * No further interaction should be performed with the detector that returns this availability.
*/
public static final int STATE_KEYPHRASE_UNSUPPORTED = -1;
/**
* Indicates that the given keyphrase is not enrolled.
+ * The caller may choose to begin an enrollment flow for the keyphrase.
*/
public static final int STATE_KEYPHRASE_UNENROLLED = 1;
/**
@@ -92,12 +95,14 @@
// Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags.
/**
- * Simple recognition of the key phrase. Returned by {@link #getSupportedRecognitionModes()}
+ * Simple recognition of the key phrase.
+ * Returned by {@link #getSupportedRecognitionModes()}
*/
public static final int RECOGNITION_MODE_VOICE_TRIGGER
= SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
/**
- * Trigger only if one user is identified. Returned by {@link #getSupportedRecognitionModes()}
+ * User identification performed with the keyphrase recognition.
+ * Returned by {@link #getSupportedRecognitionModes()}
*/
public static final int RECOGNITION_MODE_USER_IDENTIFICATION
= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
@@ -150,13 +155,11 @@
*
* Availability implies whether the hardware on this system is capable of listening for
* the given keyphrase or not. <p/>
- * If the return code is one of {@link #STATE_HARDWARE_UNAVAILABLE} or
- * {@link #STATE_KEYPHRASE_UNSUPPORTED},
- * detection is not possible and no further interaction should be
- * performed with this detector. <br/>
- * If it is {@link #STATE_KEYPHRASE_UNENROLLED} the caller may choose to begin
- * an enrollment flow for the keyphrase. <br/>
- * and for {@link #STATE_KEYPHRASE_ENROLLED} a recognition can be started as desired. <p/>
+ *
+ * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE
+ * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNSUPPORTED
+ * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
+ * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
*/
void onAvailabilityChanged(int status);
/**
@@ -215,6 +218,9 @@
/**
* Gets the recognition modes supported by the associated keyphrase.
*
+ * @see #RECOGNITION_MODE_USER_IDENTIFICATION
+ * @see #RECOGNITION_MODE_VOICE_TRIGGER
+ *
* @throws UnsupportedOperationException if the keyphrase itself isn't supported.
* Callers should only call this method after a supported state callback on
* {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 2c7b3eb..2e836fb 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,16 +16,26 @@
package android.webkit;
+import android.app.ActivityManagerInternal;
+import android.app.Application;
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
+import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import com.android.server.LocalServices;
import dalvik.system.VMRuntime;
import java.io.File;
+import java.util.Arrays;
import com.android.internal.os.Zygote;
@@ -42,17 +52,15 @@
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";
+ public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
+ "persist.sys.webview.vmsize";
+ private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024;
+
private static final String LOGTAG = "WebViewFactory";
private static final boolean DEBUG = false;
@@ -64,8 +72,8 @@
private static boolean sAddressSpaceReserved = false;
public static String getWebViewPackageName() {
- // TODO: Make this dynamic based on resource configuration.
- return "com.android.webview";
+ return AppGlobals.getInitialApplication().getString(
+ com.android.internal.R.string.config_webViewPackageName);
}
static WebViewFactoryProvider getProvider() {
@@ -99,10 +107,18 @@
}
private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
+ Application initialApplication = AppGlobals.getInitialApplication();
try {
- return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
- } catch (ClassNotFoundException e) {
- Log.e(LOGTAG, "Chromium WebView does not exist");
+ Context webViewContext = initialApplication.createPackageContext(
+ getWebViewPackageName(),
+ Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+ initialApplication.getAssets().addAssetPath(
+ webViewContext.getApplicationInfo().sourceDir);
+ ClassLoader clazzLoader = webViewContext.getClassLoader();
+ return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true,
+ clazzLoader);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOGTAG, "Chromium WebView package does not exist");
return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
}
}
@@ -114,79 +130,197 @@
public static void prepareWebViewInZygote() {
try {
System.loadLibrary("webviewchromium_loader");
- sAddressSpaceReserved = nativeReserveAddressSpace(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
- CHROMIUM_WEBVIEW_NATIVE_LIB_64);
+ long addressSpaceToReserve =
+ SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
+ CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
+ sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
+
if (sAddressSpaceReserved) {
- if (DEBUG) Log.v(LOGTAG, "address space reserved");
+ if (DEBUG) {
+ Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes");
+ }
} else {
- Log.e(LOGTAG, "reserving address space failed");
+ Log.e(LOGTAG, "reserving " + addressSpaceToReserve +
+ " bytes of address space failed");
}
- } catch (Throwable e) {
+ } catch (Throwable t) {
// Log and discard errors at this stage as we must not crash the zygote.
- Log.e(LOGTAG, "error preparing native loader", e);
+ Log.e(LOGTAG, "error preparing native loader", t);
}
}
/**
* Perform any WebView loading preparations that must happen at boot from the system server,
- * after the package manager has started.
+ * after the package manager has started or after an update to the webview is installed.
* 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]);
+ String[] nativePaths = null;
+ try {
+ nativePaths = getWebViewNativeLibraryPaths();
+ } catch (PackageManager.NameNotFoundException e) {
}
- if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) {
- createRelroFile(Build.SUPPORTED_32_BIT_ABIS[0]);
+ prepareWebViewInSystemServer(nativePaths);
+ }
+
+ private static void prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
+ if (DEBUG) Log.v(LOGTAG, "creating relro files");
+
+ // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any
+ // unexpected values will be handled there to ensure that we trigger notifying any process
+ // waiting on relreo creation.
+ if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
+ createRelroFile(false /* is64Bit */, nativeLibraryPaths);
+ }
+
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
+ createRelroFile(true /* is64Bit */, nativeLibraryPaths);
}
}
- private static void createRelroFile(String abi) {
+ public static void onWebViewUpdateInstalled() {
+ String[] nativeLibs = null;
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) {
+ nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths();
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+
+ if (nativeLibs != null) {
+ long newVmSize = 0L;
+
+ for (String path : nativeLibs) {
+ if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path);
+ if (path == null) continue;
+ File f = new File(path);
+ if (f.exists()) {
+ long length = f.length();
+ if (length > newVmSize) {
+ newVmSize = length;
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Log.v(LOGTAG, "Based on library size, need " + newVmSize +
+ " bytes of address space.");
+ }
+ // The required memory can be larger than the file on disk (due to .bss), and an
+ // upgraded version of the library will likely be larger, so always attempt to reserve
+ // twice as much as we think to allow for the library to grow during this boot cycle.
+ newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
+ Log.d(LOGTAG, "Setting new address space to " + newVmSize);
+ SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
+ Long.toString(newVmSize));
+ }
+ prepareWebViewInSystemServer(nativeLibs);
+ }
+
+ private static String[] getWebViewNativeLibraryPaths()
+ throws PackageManager.NameNotFoundException {
+ final String NATIVE_LIB_FILE_NAME = "libwebviewchromium.so";
+
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ ApplicationInfo ai = pm.getApplicationInfo(getWebViewPackageName(), 0);
+
+ String path32;
+ String path64;
+ boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);
+ if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
+ // Multi-arch case.
+ if (primaryArchIs64bit) {
+ // Primary arch: 64-bit, secondary: 32-bit.
+ path64 = ai.nativeLibraryDir;
+ path32 = ai.secondaryNativeLibraryDir;
+ } else {
+ // Primary arch: 32-bit, secondary: 64-bit.
+ path64 = ai.secondaryNativeLibraryDir;
+ path32 = ai.nativeLibraryDir;
+ }
+ } else if (primaryArchIs64bit) {
+ // Single-arch 64-bit.
+ path64 = ai.nativeLibraryDir;
+ path32 = "";
+ } else {
+ // Single-arch 32-bit.
+ path32 = ai.nativeLibraryDir;
+ path64 = "";
+ }
+ if (!TextUtils.isEmpty(path32)) path32 += "/" + NATIVE_LIB_FILE_NAME;
+ if (!TextUtils.isEmpty(path64)) path64 += "/" + NATIVE_LIB_FILE_NAME;
+ return new String[] { path32, path64 };
+ }
+
+ private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
+ final String abi =
+ is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
+
+ // crashHandler is invoked by the ActivityManagerService when the isolated process crashes.
+ Runnable crashHandler = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without");
+ getUpdateService().notifyRelroCreationCompleted(is64Bit, false);
+ } catch (RemoteException e) {
+ Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage());
+ }
+ }
+ };
+
+ try {
+ if (nativeLibraryPaths == null
+ || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
+ throw new IllegalArgumentException(
+ "Native library paths to the WebView RelRo process must not be null!");
+ }
+ int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
+ RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi,
+ Process.SHARED_RELRO_UID, crashHandler);
+ if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
+ } catch (Throwable t) {
// Log and discard errors as we must not crash the system server.
- Log.e(LOGTAG, "error starting relro file creator for abi " + abi, e);
+ Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
+ crashHandler.run();
}
}
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);
- }
+ boolean result = false;
+ boolean is64Bit = VMRuntime.getRuntime().is64Bit();
+ try{
+ if (args.length != 2 || args[0] == null || args[1] == null) {
+ Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
+ return;
+ }
+ Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " +
+ " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
+ if (!sAddressSpaceReserved) {
+ Log.e(LOGTAG, "can't create relro file; address space not reserved");
+ return;
+ }
+ result = nativeCreateRelroFile(args[0] /* path32 */,
+ args[1] /* path64 */,
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
+ CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+ if (result && DEBUG) Log.v(LOGTAG, "created relro file");
+ } finally {
+ // We must do our best to always notify the update service, even if something fails.
+ try {
+ getUpdateService().notifyRelroCreationCompleted(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);
+ if (!result) Log.e(LOGTAG, "failed to create relro file");
+
+ // Must explicitly exit or else this process will just sit around after we return.
+ System.exit(0);
+ }
}
}
@@ -203,14 +337,19 @@
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");
+ try {
+ String[] args = getWebViewNativeLibraryPaths();
+ boolean result = nativeLoadWithRelroFile(args[0] /* path32 */,
+ args[1] /* path64 */,
+ 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");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e);
}
}
@@ -218,7 +357,7 @@
return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
}
- private static native boolean nativeReserveAddressSpace(String lib32, String lib64);
+ private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
private static native boolean nativeCreateRelroFile(String lib32, String lib64,
String relro32, String relro64);
private static native boolean nativeLoadWithRelroFile(String lib32, String lib64,
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 217658c..b9e5c66 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1661,4 +1661,8 @@
<!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet
string that's stored in 8-bit unpacked format) characters.-->
<bool translatable="false" name="config_sms_decode_gsm_8bit_data">false</bool>
+
+ <!-- Package name providing WebView implementation. -->
+ <string name="config_webViewPackageName" translatable="false">com.android.webview</string>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d37e468..ccd7005 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1839,6 +1839,7 @@
<java-symbol type="attr" name="actionModeWebSearchDrawable" />
<java-symbol type="string" name="websearch" />
<java-symbol type="drawable" name="ic_media_video_poster" />
+ <java-symbol type="string" name="config_webViewPackageName" />
<!-- From SubtitleView -->
<java-symbol type="dimen" name="subtitle_corner_radius" />
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 2fad73e..898b6f1 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -49,6 +49,11 @@
private static final int MSG_TRY_CONNECTING = 1;
+ private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
+ private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
+ private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
+ private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
+
private Context mContext;
// The actual MMS service instance to invoke
private volatile IMms mService;
@@ -275,7 +280,9 @@
mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message");
if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
callingPkg) != AppOpsManager.MODE_ALLOWED) {
- return null;
+ // Silently fail AppOps failure due to not being the default SMS app
+ // while writing the TelephonyProvider
+ return FAKE_SMS_SENT_URI;
}
return getServiceGuarded().importTextMessage(
callingPkg, address, type, text, timestampMillis, seen, read);
@@ -287,7 +294,9 @@
mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
callingPkg) != AppOpsManager.MODE_ALLOWED) {
- return null;
+ // Silently fail AppOps failure due to not being the default SMS app
+ // while writing the TelephonyProvider
+ return FAKE_MMS_SENT_URI;
}
return getServiceGuarded().importMultimediaMessage(
callingPkg, pdu, messageId, timestampSecs, seen, read);
@@ -340,7 +349,9 @@
mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft");
if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
callingPkg) != AppOpsManager.MODE_ALLOWED) {
- return null;
+ // Silently fail AppOps failure due to not being the default SMS app
+ // while writing the TelephonyProvider
+ return FAKE_SMS_DRAFT_URI;
}
return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
}
@@ -350,7 +361,9 @@
mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
callingPkg) != AppOpsManager.MODE_ALLOWED) {
- return null;
+ // Silently fail AppOps failure due to not being the default SMS app
+ // while writing the TelephonyProvider
+ return FAKE_MMS_DRAFT_URI;
}
return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f3ac0f5..0ad36fc 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2034,7 +2034,7 @@
mSystemThread.installSystemApplicationInfo(info);
synchronized (this) {
- ProcessRecord app = newProcessRecordLocked(info, info.processName, false);
+ ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
app.persistent = true;
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
@@ -2834,10 +2834,45 @@
|| transit == AppTransition.TRANSIT_TASK_TO_FRONT;
}
+ int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+ String processName, String abiOverride, int uid, Runnable crashHandler) {
+ synchronized(this) {
+ ApplicationInfo info = new ApplicationInfo();
+ // In general the ApplicationInfo.uid isn't neccesarily equal to ProcessRecord.uid.
+ // For isolated processes, the former contains the parent's uid and the latter the
+ // actual uid of the isolated process.
+ // In the special case introduced by this method (which is, starting an isolated
+ // process directly from the SystemServer without an actual parent app process) the
+ // closest thing to a parent's uid is SYSTEM_UID.
+ // The only important thing here is to keep AI.uid != PR.uid, in order to trigger
+ // the |isolated| logic in the ProcessRecord constructor.
+ info.uid = Process.SYSTEM_UID;
+ info.processName = processName;
+ info.className = entryPoint;
+ info.packageName = "android";
+ ProcessRecord proc = startProcessLocked(processName, info /* info */,
+ false /* knownToBeDead */, 0 /* intentFlags */, "" /* hostingType */,
+ null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
+ uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
+ crashHandler);
+ return proc != null ? proc.pid : 0;
+ }
+ }
+
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
+ return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
+ hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
+ null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
+ null /* crashHandler */);
+ }
+
+ final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
+ boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
+ boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
+ String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
@@ -2905,7 +2940,8 @@
}
if (app == null) {
- app = newProcessRecordLocked(info, processName, isolated);
+ app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+ app.crashHandler = crashHandler;
if (app == null) {
Slog.w(TAG, "Failed making new process record for "
+ processName + "/" + info.uid + " isolated=" + isolated);
@@ -2932,7 +2968,8 @@
return app;
}
- startProcessLocked(app, hostingType, hostingNameStr, null /* ABI override */);
+ startProcessLocked(
+ app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
return (app.pid != 0) ? app : null;
}
@@ -2941,7 +2978,13 @@
}
private final void startProcessLocked(ProcessRecord app,
- String hostingType, String hostingNameStr, String abiOverride) {
+ String hostingType, String hostingNameStr) {
+ startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,
+ null /* entryPoint */, null /* entryPointArgs */);
+ }
+
+ private final void startProcessLocked(ProcessRecord app, String hostingType,
+ String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
if (app.pid > 0 && app.pid != MY_PID) {
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
@@ -3034,9 +3077,11 @@
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
- Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
+ boolean isActivityProcess = (entryPoint == null);
+ if (entryPoint == null) entryPoint = "android.app.ActivityThread";
+ Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
- app.info.targetSdkVersion, app.info.seinfo, requiredAbi, null);
+ app.info.targetSdkVersion, app.info.seinfo, requiredAbi, entryPointArgs);
if (app.isolated) {
mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
@@ -3056,6 +3101,11 @@
buf.setLength(0);
buf.append("Start proc ");
buf.append(app.processName);
+ if (!isActivityProcess) {
+ buf.append(" [");
+ buf.append(entryPoint);
+ buf.append("]");
+ }
buf.append(" for ");
buf.append(hostingType);
if (hostingNameStr != null) {
@@ -3086,10 +3136,12 @@
app.killedByAm = false;
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(startResult.pid, app);
- Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
- msg.obj = app;
- mHandler.sendMessageDelayed(msg, startResult.usingWrapper
- ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+ if (isActivityProcess) {
+ Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+ msg.obj = app;
+ mHandler.sendMessageDelayed(msg, startResult.usingWrapper
+ ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+ }
}
} catch (RuntimeException e) {
// XXX do better error recovery.
@@ -5359,7 +5411,7 @@
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
- startProcessLocked(app, "link fail", processName, null /* ABI override */);
+ startProcessLocked(app, "link fail", processName);
return false;
}
@@ -5452,7 +5504,7 @@
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
- startProcessLocked(app, "bind fail", processName, null /* ABI override */);
+ startProcessLocked(app, "bind fail", processName);
return false;
}
@@ -5607,7 +5659,7 @@
for (int ip=0; ip<NP; ip++) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
+ procs.get(ip));
- startProcessLocked(procs.get(ip), "on-hold", null, null /* ABI override */);
+ startProcessLocked(procs.get(ip), "on-hold", null);
}
}
@@ -8955,29 +9007,35 @@
// =========================================================
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
- boolean isolated) {
+ boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
BatteryStatsImpl.Uid.Proc ps = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
int uid = info.uid;
if (isolated) {
- int userId = UserHandle.getUserId(uid);
- int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
- while (true) {
- if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
- || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
- mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
+ if (isolatedUid == 0) {
+ int userId = UserHandle.getUserId(uid);
+ int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
+ while (true) {
+ if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
+ || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
+ mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
+ }
+ uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
+ mNextIsolatedProcessUid++;
+ if (mIsolatedProcesses.indexOfKey(uid) < 0) {
+ // No process for this uid, use it.
+ break;
+ }
+ stepsLeft--;
+ if (stepsLeft <= 0) {
+ return null;
+ }
}
- uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
- mNextIsolatedProcessUid++;
- if (mIsolatedProcesses.indexOfKey(uid) < 0) {
- // No process for this uid, use it.
- break;
- }
- stepsLeft--;
- if (stepsLeft <= 0) {
- return null;
- }
+ } else {
+ // Special case for startIsolatedProcess (internal only), where
+ // the uid of the isolated process is specified by the caller.
+ uid = isolatedUid;
}
}
return new ProcessRecord(stats, info, proc, uid);
@@ -8993,7 +9051,7 @@
}
if (app == null) {
- app = newProcessRecordLocked(info, null, isolated);
+ app = newProcessRecordLocked(info, null, isolated, 0);
mProcessNames.put(info.processName, app.uid, app);
if (isolated) {
mIsolatedProcesses.put(app.uid, app);
@@ -9019,8 +9077,8 @@
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
- startProcessLocked(app, "added application", app.processName,
- abiOverride);
+ startProcessLocked(app, "added application", app.processName, abiOverride,
+ null /* entryPoint */, null /* entryPointArgs */);
}
return app;
@@ -10519,6 +10577,7 @@
mProcessCrashTimes.put(app.info.processName, app.uid, now);
}
+ if (app.crashHandler != null) mHandler.post(app.crashHandler);
return true;
}
@@ -13517,7 +13576,7 @@
// We have components that still need to be running in the
// process, so re-launch it.
mProcessNames.put(app.processName, app.uid, app);
- startProcessLocked(app, "restart", app.processName, null /* ABI override */);
+ startProcessLocked(app, "restart", app.processName);
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
boolean removed;
@@ -17849,6 +17908,13 @@
public void wakingUp() {
ActivityManagerService.this.wakingUp();
}
+
+ @Override
+ public int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+ String processName, String abiOverride, int uid, Runnable crashHandler) {
+ return ActivityManagerService.this.startIsolatedProcess(entryPoint, entryPointArgs,
+ processName, abiOverride, uid, crashHandler);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b33f7b7..f1bcb60 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -127,7 +127,8 @@
Object adjSource; // Debugging: option dependent object.
int adjSourceProcState; // Debugging: proc state of adjSource's process.
Object adjTarget; // Debugging: target component impacting oom_adj.
-
+ Runnable crashHandler; // Optional local handler to be invoked in the process crash.
+
// contains HistoryRecord objects
final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
// all ServiceRecord running in this process
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 60724e7..ff68cf7 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -22,17 +22,20 @@
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Process;
-import android.util.Log;
+import android.util.Slog;
import android.webkit.IWebViewUpdateService;
import android.webkit.WebViewFactory;
+import com.android.server.SystemService;
+
/**
* Private service to wait for the updatable WebView to be ready for use.
* @hide
*/
-public class WebViewUpdateService extends IWebViewUpdateService.Stub {
+public class WebViewUpdateService extends SystemService {
private static final String TAG = "WebViewUpdateService";
+ private static final int WAIT_TIMEOUT_MS = 5000; // Same as KEY_DISPATCHING_TIMEOUT.
private boolean mRelroReady32Bit = false;
private boolean mRelroReady64Bit = false;
@@ -40,6 +43,11 @@
private BroadcastReceiver mWebViewUpdatedReceiver;
public WebViewUpdateService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
mWebViewUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -52,57 +60,76 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
- context.registerReceiver(mWebViewUpdatedReceiver, filter);
- }
+ getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
- /**
- * 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) {}
- }
- }
- }
+ publishBinderService("webviewupdate", new BinderService());
}
private void onWebViewUpdateInstalled() {
- Log.d(TAG, "WebView Package updated!");
+ Slog.d(TAG, "WebView Package updated!");
synchronized (this) {
mRelroReady32Bit = false;
mRelroReady64Bit = false;
}
- WebViewFactory.prepareWebViewInSystemServer();
+ WebViewFactory.onWebViewUpdateInstalled();
}
+
+ private class BinderService extends IWebViewUpdateService.Stub {
+
+ /**
+ * The shared relro process calls this to notify us that it's done trying to create a relro
+ * file. This method gets called even if the relro creation has failed or the process
+ * crashed.
+ */
+ @Override // Binder call
+ public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) {
+ // Verify that the caller is either the shared relro process (nominal case) or the
+ // system server (only in the case the relro process crashes and we get here via the
+ // crashHandler).
+ if (Binder.getCallingUid() != Process.SHARED_RELRO_UID &&
+ Binder.getCallingUid() != Process.SYSTEM_UID) {
+ return;
+ }
+
+ synchronized (WebViewUpdateService.this) {
+ if (is64Bit) {
+ mRelroReady64Bit = true;
+ } else {
+ mRelroReady32Bit = true;
+ }
+ WebViewUpdateService.this.notifyAll();
+ }
+ }
+
+ /**
+ * WebViewFactory calls this to block WebView loading until the relro file is created.
+ */
+ @Override // Binder call
+ public void waitForRelroCreationCompleted(boolean is64Bit) {
+ // The WebViewUpdateService depends on the prepareWebViewInSystemServer call, which
+ // happens later (during the PHASE_ACTIVITY_MANAGER_READY) in SystemServer.java. If
+ // another service there tries to bring up a WebView in the between, the wait below
+ // would deadlock without the check below.
+ if (Binder.getCallingPid() == Process.myPid()) {
+ throw new IllegalStateException("Cannot create a WebView from the SystemServer");
+ }
+
+ final long NS_PER_MS = 1000000;
+ final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+ boolean relroReady = false;
+ synchronized (WebViewUpdateService.this) {
+ while (!relroReady) {
+ final long timeNowMs = System.nanoTime() / NS_PER_MS;
+ if (timeNowMs >= timeoutTimeMs) break;
+ try {
+ WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
+ } catch (InterruptedException e) {}
+ relroReady = (is64Bit ? mRelroReady64Bit : mRelroReady32Bit);
+ }
+ }
+ if (!relroReady) Slog.w(TAG, "creating relro file timed out");
+ }
+ }
+
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d955354..e8d6773 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -376,6 +376,9 @@
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
+
+ // Tracks whether the updatable WebView is in a ready state and watches for update installs.
+ mSystemServiceManager.startService(WebViewUpdateService.class);
}
/**
@@ -422,12 +425,6 @@
Slog.i(TAG, "Reading configuration...");
SystemConfig.getInstance();
- Slog.i(TAG, "WebView Update Service");
- ServiceManager.addService("webviewupdate", new WebViewUpdateService(context));
-
- Slog.i(TAG, "WebViewFactory preparation");
- WebViewFactory.prepareWebViewInSystemServer();
-
Slog.i(TAG, "Scheduling Policy");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
@@ -1080,6 +1077,10 @@
} catch (Throwable e) {
reportWtf("observing native crashes", e);
}
+
+ Slog.i(TAG, "WebViewFactory preparation");
+ WebViewFactory.prepareWebViewInSystemServer();
+
try {
startSystemUi(context);
} catch (Throwable e) {