Loading services/core/java/com/android/server/pm/PackageManagerService.java +15 −1 Original line number Diff line number Diff line Loading @@ -9684,6 +9684,20 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap, String loaderIsa) { if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) && Binder.getCallingUid() != Process.SYSTEM_UID) { Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid=" + Binder.getCallingUid()); // Do not record dex loads from processes pretending to be system server. // Only the system server should be assigned the package "android", so reject calls // that don't satisfy the constraint. // // notifyDexLoad is a PM API callable from the app process. So in theory, apps could // craft calls to this API and pretend to be system server. Doing so poses no particular // danger for dex load reporting or later dexopt, however it is a sensible check to do // in order to verify the expectations. return; } int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { services/core/java/com/android/server/pm/dex/DexManager.java +47 −9 Original line number Diff line number Diff line Loading @@ -17,13 +17,17 @@ package com.android.server.pm.dex; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; import static java.util.function.Function.identity; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackagePartitions; import android.os.FileUtils; import android.os.RemoteException; import android.os.SystemProperties; Loading Loading @@ -67,13 +71,12 @@ import java.util.zip.ZipEntry; */ public class DexManager { private static final String TAG = "DexManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST = "pm.dexopt.priv-apps-oob-list"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final Context mContext; // Maps package name to code locations. Loading Loading @@ -178,12 +181,14 @@ public class DexManager { boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY || searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT; if (primaryOrSplit && !isUsedByOtherApps) { if (primaryOrSplit && !isUsedByOtherApps && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. // Note this is just an optimization that makes things easier to read in the // package-dex-use file since we don't need to pollute it with redundant info. // However, we always record system server packages. continue; } Loading Loading @@ -216,6 +221,23 @@ public class DexManager { } } /** * Check if the dexPath belongs to system server. * System server can load code from different location, so we cast a wide-net here, and * assume that if the paths is on any of the registered system partitions then it can be loaded * by system server. */ private boolean isSystemServerDexPathSupportedForOdex(String dexPath) { ArrayList<PackagePartitions.SystemPartition> partitions = PackagePartitions.getOrderedPartitions(identity()); for (int i = 0; i < partitions.size(); i++) { if (partitions.get(i).containsPath(dexPath)) { return true; } } return false; } /** * Read the dex usage from disk and populate the code cache locations. * @param existingPackages a map containing information about what packages Loading Loading @@ -607,12 +629,6 @@ public class DexManager { */ private DexSearchResult getDexPackage( ApplicationInfo loadingAppInfo, String dexPath, int userId) { // Ignore framework code. // TODO(calin): is there a better way to detect it? if (dexPath.startsWith("/system/framework/")) { return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND); } // First, check if the package which loads the dex file actually owns it. // Most of the time this will be true and we can return early. PackageCodeLocations loadingPackageCodeLocations = Loading @@ -635,6 +651,28 @@ public class DexManager { } } // We could not find the owning package amongst regular apps. // If the loading package is system server, see if the dex file resides // on any of the potentially system server owning location and if so, // assuming system server ownership. // // Note: We don't have any way to detect which code paths are actually // owned by system server. We can only assume that such paths are on // system partitions. if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) { if (isSystemServerDexPathSupportedForOdex(dexPath)) { // We record system server dex files as secondary dex files. // The reason is that we only record the class loader context for secondary dex // files and we expect that all primary apks are loaded with an empty class loader. // System server dex files may be loaded in non-empty class loader so we need to // keep track of their context. return new DexSearchResult(PLATFORM_PACKAGE_NAME, DEX_SEARCH_FOUND_SECONDARY); } else { Slog.wtf(TAG, "System server loads dex files outside paths supported for odex: " + dexPath); } } if (DEBUG) { // TODO(calin): Consider checking for /data/data symlink. // /data/data/ symlinks /data/user/0/ and there's nothing stopping apps Loading services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.dex; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.content.pm.IPackageManager; import android.os.RemoteException; import android.util.Log; import android.util.Slog; import dalvik.system.BaseDexClassLoader; import dalvik.system.VMRuntime; import java.util.Map; /** * Reports dex file use to the package manager on behalf of system server. */ public class SystemServerDexLoadReporter implements BaseDexClassLoader.Reporter { private static final String TAG = "SystemServerDexLoadReporter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final IPackageManager mPackageManager; private SystemServerDexLoadReporter(IPackageManager pm) { mPackageManager = pm; } @Override public void report(Map<String, String> classLoaderContextMap) { if (DEBUG) { Slog.i(TAG, "Reporting " + classLoaderContextMap); } if (classLoaderContextMap.isEmpty()) { Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap"); return; } try { mPackageManager.notifyDexLoad( PLATFORM_PACKAGE_NAME, classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet()); } catch (RemoteException ignored) { // We're in system server, it can't happen. } } /** * Configures system server dex file reporting. * <p>The method will install a reporter in the BaseDexClassLoader and also * force the reporting of any dex files already loaded by the system server. */ public static void configureSystemServerDexReporter(IPackageManager pm) { Slog.i(TAG, "Configuring system server dex reporter"); SystemServerDexLoadReporter reporter = new SystemServerDexLoadReporter(pm); BaseDexClassLoader.setReporter(reporter); ClassLoader currrentClassLoader = reporter.getClass().getClassLoader(); if (currrentClassLoader instanceof BaseDexClassLoader) { ((BaseDexClassLoader) currrentClassLoader).reportClassLoaderChain(); } else { Slog.wtf(TAG, "System server class loader is not a BaseDexClassLoader. type=" + currrentClassLoader.getClass().getName()); } } } services/java/com/android/server/SystemServer.java +6 −0 Original line number Diff line number Diff line Loading @@ -136,6 +136,7 @@ import com.android.server.pm.OtaDexoptService; import com.android.server.pm.PackageManagerService; import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; import com.android.server.pm.dex.SystemServerDexLoadReporter; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.LegacyRoleResolutionPolicy; Loading Loading @@ -837,6 +838,11 @@ public final class SystemServer { Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); } // Now that the package manager has started, register the dex load reporter to capture any // dex files loaded by system server. // These dex files will be optimized by the BackgroundDexOptService. SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService); mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager(); t.traceEnd(); Loading services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +25 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,9 @@ public class DexManagerTests { private TestData mBarUser0UnsupportedClassLoader; private TestData mBarUser0DelegateLastClassLoader; private TestData mSystemServerJar; private TestData mSystemServerJarInvalid; private int mUser0; private int mUser1; Loading @@ -108,6 +111,9 @@ public class DexManagerTests { mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0, DELEGATE_LAST_CLASS_LOADER_NAME); mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock); Loading Loading @@ -587,6 +593,25 @@ public class DexManagerTests { assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries); } @Test public void testNotifySystemServerUse() { List<String> dexFiles = new ArrayList<String>(); dexFiles.add("/system/framework/foo"); notifyDexLoad(mSystemServerJar, dexFiles, mUser0); PackageUseInfo pui = getPackageUseInfo(mSystemServerJar); assertIsUsedByOtherApps(mSystemServerJar, pui, false); } @Test public void testNotifySystemServerInvalidUse() { List<String> dexFiles = new ArrayList<String>(); dexFiles.add("/data/foo"); notifyDexLoad(mSystemServerJarInvalid, dexFiles, mUser0); assertNoUseInfo(mSystemServerJarInvalid); assertNoDclInfo(mSystemServerJarInvalid); } private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId, String[] expectedContexts) { Loading Loading
services/core/java/com/android/server/pm/PackageManagerService.java +15 −1 Original line number Diff line number Diff line Loading @@ -9684,6 +9684,20 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap, String loaderIsa) { if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) && Binder.getCallingUid() != Process.SYSTEM_UID) { Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid=" + Binder.getCallingUid()); // Do not record dex loads from processes pretending to be system server. // Only the system server should be assigned the package "android", so reject calls // that don't satisfy the constraint. // // notifyDexLoad is a PM API callable from the app process. So in theory, apps could // craft calls to this API and pretend to be system server. Doing so poses no particular // danger for dex load reporting or later dexopt, however it is a sensible check to do // in order to verify the expectations. return; } int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) {
services/core/java/com/android/server/pm/dex/DexManager.java +47 −9 Original line number Diff line number Diff line Loading @@ -17,13 +17,17 @@ package com.android.server.pm.dex; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; import static java.util.function.Function.identity; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackagePartitions; import android.os.FileUtils; import android.os.RemoteException; import android.os.SystemProperties; Loading Loading @@ -67,13 +71,12 @@ import java.util.zip.ZipEntry; */ public class DexManager { private static final String TAG = "DexManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST = "pm.dexopt.priv-apps-oob-list"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final Context mContext; // Maps package name to code locations. Loading Loading @@ -178,12 +181,14 @@ public class DexManager { boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY || searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT; if (primaryOrSplit && !isUsedByOtherApps) { if (primaryOrSplit && !isUsedByOtherApps && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. // Note this is just an optimization that makes things easier to read in the // package-dex-use file since we don't need to pollute it with redundant info. // However, we always record system server packages. continue; } Loading Loading @@ -216,6 +221,23 @@ public class DexManager { } } /** * Check if the dexPath belongs to system server. * System server can load code from different location, so we cast a wide-net here, and * assume that if the paths is on any of the registered system partitions then it can be loaded * by system server. */ private boolean isSystemServerDexPathSupportedForOdex(String dexPath) { ArrayList<PackagePartitions.SystemPartition> partitions = PackagePartitions.getOrderedPartitions(identity()); for (int i = 0; i < partitions.size(); i++) { if (partitions.get(i).containsPath(dexPath)) { return true; } } return false; } /** * Read the dex usage from disk and populate the code cache locations. * @param existingPackages a map containing information about what packages Loading Loading @@ -607,12 +629,6 @@ public class DexManager { */ private DexSearchResult getDexPackage( ApplicationInfo loadingAppInfo, String dexPath, int userId) { // Ignore framework code. // TODO(calin): is there a better way to detect it? if (dexPath.startsWith("/system/framework/")) { return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND); } // First, check if the package which loads the dex file actually owns it. // Most of the time this will be true and we can return early. PackageCodeLocations loadingPackageCodeLocations = Loading @@ -635,6 +651,28 @@ public class DexManager { } } // We could not find the owning package amongst regular apps. // If the loading package is system server, see if the dex file resides // on any of the potentially system server owning location and if so, // assuming system server ownership. // // Note: We don't have any way to detect which code paths are actually // owned by system server. We can only assume that such paths are on // system partitions. if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) { if (isSystemServerDexPathSupportedForOdex(dexPath)) { // We record system server dex files as secondary dex files. // The reason is that we only record the class loader context for secondary dex // files and we expect that all primary apks are loaded with an empty class loader. // System server dex files may be loaded in non-empty class loader so we need to // keep track of their context. return new DexSearchResult(PLATFORM_PACKAGE_NAME, DEX_SEARCH_FOUND_SECONDARY); } else { Slog.wtf(TAG, "System server loads dex files outside paths supported for odex: " + dexPath); } } if (DEBUG) { // TODO(calin): Consider checking for /data/data symlink. // /data/data/ symlinks /data/user/0/ and there's nothing stopping apps Loading
services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.dex; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.content.pm.IPackageManager; import android.os.RemoteException; import android.util.Log; import android.util.Slog; import dalvik.system.BaseDexClassLoader; import dalvik.system.VMRuntime; import java.util.Map; /** * Reports dex file use to the package manager on behalf of system server. */ public class SystemServerDexLoadReporter implements BaseDexClassLoader.Reporter { private static final String TAG = "SystemServerDexLoadReporter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final IPackageManager mPackageManager; private SystemServerDexLoadReporter(IPackageManager pm) { mPackageManager = pm; } @Override public void report(Map<String, String> classLoaderContextMap) { if (DEBUG) { Slog.i(TAG, "Reporting " + classLoaderContextMap); } if (classLoaderContextMap.isEmpty()) { Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap"); return; } try { mPackageManager.notifyDexLoad( PLATFORM_PACKAGE_NAME, classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet()); } catch (RemoteException ignored) { // We're in system server, it can't happen. } } /** * Configures system server dex file reporting. * <p>The method will install a reporter in the BaseDexClassLoader and also * force the reporting of any dex files already loaded by the system server. */ public static void configureSystemServerDexReporter(IPackageManager pm) { Slog.i(TAG, "Configuring system server dex reporter"); SystemServerDexLoadReporter reporter = new SystemServerDexLoadReporter(pm); BaseDexClassLoader.setReporter(reporter); ClassLoader currrentClassLoader = reporter.getClass().getClassLoader(); if (currrentClassLoader instanceof BaseDexClassLoader) { ((BaseDexClassLoader) currrentClassLoader).reportClassLoaderChain(); } else { Slog.wtf(TAG, "System server class loader is not a BaseDexClassLoader. type=" + currrentClassLoader.getClass().getName()); } } }
services/java/com/android/server/SystemServer.java +6 −0 Original line number Diff line number Diff line Loading @@ -136,6 +136,7 @@ import com.android.server.pm.OtaDexoptService; import com.android.server.pm.PackageManagerService; import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; import com.android.server.pm.dex.SystemServerDexLoadReporter; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.LegacyRoleResolutionPolicy; Loading Loading @@ -837,6 +838,11 @@ public final class SystemServer { Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); } // Now that the package manager has started, register the dex load reporter to capture any // dex files loaded by system server. // These dex files will be optimized by the BackgroundDexOptService. SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService); mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager(); t.traceEnd(); Loading
services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +25 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,9 @@ public class DexManagerTests { private TestData mBarUser0UnsupportedClassLoader; private TestData mBarUser0DelegateLastClassLoader; private TestData mSystemServerJar; private TestData mSystemServerJarInvalid; private int mUser0; private int mUser1; Loading @@ -108,6 +111,9 @@ public class DexManagerTests { mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0, DELEGATE_LAST_CLASS_LOADER_NAME); mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock); Loading Loading @@ -587,6 +593,25 @@ public class DexManagerTests { assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries); } @Test public void testNotifySystemServerUse() { List<String> dexFiles = new ArrayList<String>(); dexFiles.add("/system/framework/foo"); notifyDexLoad(mSystemServerJar, dexFiles, mUser0); PackageUseInfo pui = getPackageUseInfo(mSystemServerJar); assertIsUsedByOtherApps(mSystemServerJar, pui, false); } @Test public void testNotifySystemServerInvalidUse() { List<String> dexFiles = new ArrayList<String>(); dexFiles.add("/data/foo"); notifyDexLoad(mSystemServerJarInvalid, dexFiles, mUser0); assertNoUseInfo(mSystemServerJarInvalid); assertNoDclInfo(mSystemServerJarInvalid); } private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId, String[] expectedContexts) { Loading