diff --git a/api/current.txt b/api/current.txt index 8ad59fff2721c..41f6d6f26ba0d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1622,6 +1622,7 @@ package android { field @Deprecated public static final int yearListSelectorColor = 16843930; // 0x101049a field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090 field public static final int zAdjustment = 16843201; // 0x10101c1 + field public static final int zygotePreloadName = 16844195; // 0x10105a3 } public static final class R.bool { @@ -6477,6 +6478,10 @@ package android.app { method public void onColorsChanged(android.app.WallpaperColors, int); } + public interface ZygotePreload { + method public void doPreload(android.content.pm.ApplicationInfo); + } + } package android.app.admin { diff --git a/core/java/android/app/ZygotePreload.java b/core/java/android/app/ZygotePreload.java new file mode 100644 index 0000000000000..a295af352c0aa --- /dev/null +++ b/core/java/android/app/ZygotePreload.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 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.app; + +import android.content.pm.ApplicationInfo; + +/** + * This is the interface to be implemented for the class that is specified by the + * {@link android.R.styleable#AndroidManifestApplication_zygotePreloadName + * android:zygotePreloadName} of the <application> tag. + * + * It is responsible for preloading application code and data, that will be shared by all + * isolated services that have the + * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} attribute + * of the <service> tag set to true. + * + * Note that this implementations of this class must provide a default constructor with no + * arguments. + */ +public interface ZygotePreload { + /** + * This method is called once every time the Application Zygote is started. It is normally + * started the first time an isolated service that uses it is started. The Application Zygote + * will be stopped when all isolated services that use it are stopped. + * + * @param appInfo The ApplicationInfo object belonging to the application + */ + void doPreload(ApplicationInfo appInfo); +} diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 5d6d1444eaf32..1358bc25a07b1 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1192,6 +1192,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** @hide */ public boolean hiddenUntilInstalled; + /** @hide */ + public String zygotePreloadName; + /** * Represents the default policy. The actual policy used will depend on other properties of * the application, e.g. the target SDK version. @@ -1533,6 +1536,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { compileSdkVersionCodename = orig.compileSdkVersionCodename; mHiddenApiPolicy = orig.mHiddenApiPolicy; hiddenUntilInstalled = orig.hiddenUntilInstalled; + zygotePreloadName = orig.zygotePreloadName; } public String toString() { @@ -1609,6 +1613,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString(appComponentFactory); dest.writeInt(mHiddenApiPolicy); dest.writeInt(hiddenUntilInstalled ? 1 : 0); + dest.writeString(zygotePreloadName); } public static final Parcelable.Creator CREATOR @@ -1682,6 +1687,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { appComponentFactory = source.readString(); mHiddenApiPolicy = source.readInt(); hiddenUntilInstalled = source.readInt() != 0; + zygotePreloadName = source.readString(); } /** diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index eb59cfc0fc4ba..1fab443629a4f 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3907,6 +3907,9 @@ public class PackageParser { outError[0] = "Invalid class loader name: " + ai.classLoaderName; } + ai.zygotePreloadName = sa.getString( + com.android.internal.R.styleable.AndroidManifestApplication_zygotePreloadName); + sa.recycle(); if (outError[0] != null) { diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java index 6ba584d76ee84..0e83e41a74239 100644 --- a/core/java/com/android/internal/os/AppZygoteInit.java +++ b/core/java/com/android/internal/os/AppZygoteInit.java @@ -17,13 +17,15 @@ package com.android.internal.os; import android.app.LoadedApk; +import android.app.ZygotePreload; +import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.net.LocalSocket; import android.util.Log; import java.io.DataOutputStream; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** @@ -76,20 +78,29 @@ class AppZygoteInit { Zygote.allowAppFilesAcrossFork(appInfo); - Class cl; - Method m; - try { - cl = Class.forName(appInfo.packageName + ".ZygotePreload", true, loader); - m = cl.getMethod("doPreload"); - m.setAccessible(true); - m.invoke(null); - } catch (ClassNotFoundException e) { - // Don't treat this as an error since an app may not want to do any preloads - Log.w(TAG, "No ZygotePreload class found for " + appInfo.packageName); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Log.e(TAG, "AppZygote application preload failed for " - + appInfo.packageName, e); + if (appInfo.zygotePreloadName != null) { + Class cl; + Method m; + try { + ComponentName preloadName = ComponentName.createRelative(appInfo.packageName, + appInfo.zygotePreloadName); + cl = Class.forName(preloadName.getClassName(), true, loader); + if (!ZygotePreload.class.isAssignableFrom(cl)) { + Log.e(TAG, preloadName.getClassName() + " does not implement " + + ZygotePreload.class.getName()); + } else { + Constructor ctor = cl.getConstructor(); + ZygotePreload preloadObject = (ZygotePreload) ctor.newInstance(); + preloadObject.doPreload(appInfo); + } + } catch (ReflectiveOperationException e) { + Log.e(TAG, "AppZygote application preload failed for " + + appInfo.zygotePreloadName, e); + } + } else { + Log.i(TAG, "No zygotePreloadName attribute specified."); } + try { DataOutputStream socketOut = getSocketOutputStream(); socketOut.writeInt(loader != null ? 1 : 0); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index de6468dbb72a4..752d2e108fdb9 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1113,6 +1113,15 @@ --> + + + + + and how much memory those services save by preloading and sharing memory with + the app zygote. Therefore, it is recommended to measure memory usage under + typical workloads to determine whether it makes sense to use this flag. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index ec1bac1a41d62..b5266e247e3af 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2936,6 +2936,7 @@ +