From fb7caa96f9511273ef32e1e80b740b28ca91a536 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Tue, 29 Jan 2019 11:45:56 +0100 Subject: [PATCH] Allow app to specify class name for app zygote preloading. Instead of using a fixed class name, define an interface for preloading application code that must be implemented by applications that use the application zygote. Also, add an ApplicationInfo parameter to said function, so the app knows where to look for its data and code. Bug: 111434506 Test: atest CtsApptestCases:ServiceTest atest CtsSeccompHostTestCases Change-Id: I1f8472da89dc90562dcb4e479e3d87ebf49b926c --- api/current.txt | 5 +++ core/java/android/app/ZygotePreload.java | 42 +++++++++++++++++++ .../android/content/pm/ApplicationInfo.java | 6 +++ .../android/content/pm/PackageParser.java | 3 ++ .../android/internal/os/AppZygoteInit.java | 39 ++++++++++------- core/res/res/values/attrs_manifest.xml | 21 +++++++--- core/res/res/values/public.xml | 1 + 7 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 core/java/android/app/ZygotePreload.java 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 @@ +