Loading core/java/android/content/pm/PackageManager.java +2 −18 Original line number Diff line number Diff line Loading @@ -10134,25 +10134,9 @@ public abstract class PackageManager { /** * Register an application dex module with the package manager. * The package manager will keep track of the given module for future optimizations. * * Dex module optimizations will disable the classpath checking at runtime. The client bares * the responsibility to ensure that the static assumptions on classes in the optimized code * hold at runtime (e.g. there's no duplicate classes in the classpath). * * Note that the package manager already keeps track of dex modules loaded with * {@link dalvik.system.DexClassLoader} and {@link dalvik.system.PathClassLoader}. * This can be called for an eager registration. * * The call might take a while and the results will be posted on the main thread, using * the given callback. * * If the module is intended to be shared with other apps, make sure that the file * permissions allow for it. * If at registration time the permissions allow for others to read it, the module would * be marked as a shared module which might undergo a different optimization strategy. * (usually shared modules will generated larger optimizations artifacts, * taking more disk space). * This call no longer does anything. If a callback is given it is called with a false success * value. * * @param dexModulePath the absolute path of the dex module. * @param callback if not null, {@link DexModuleRegisterCallback#onDexModuleRegistered} will Loading services/core/java/com/android/server/pm/PackageManagerService.java +12 −26 Original line number Diff line number Diff line Loading @@ -5561,32 +5561,18 @@ public class PackageManagerService implements PackageSender, TestUtilityService public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { if (useArtService()) { // ART Service currently doesn't support this explicit dexopting and instead relies // on background dexopt for secondary dex files. This API is problematic since it // doesn't provide the correct classloader context. // ART Service doesn't support this explicit dexopting and instead relies on background // dexopt for secondary dex files. For compat parity between ART Service and the legacy // code it's disabled for both. // // Also, this API is problematic anyway since it doesn't provide the correct classloader // context, so it is hard to produce dexopt artifacts that the runtime can load // successfully. Slog.i(TAG, "Ignored unsupported registerDexModule call for " + dexModulePath + " in " + packageName); return; } int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = snapshot().getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(PackageManagerService.TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { try { result = mDexManager.registerDexModule( ai, dexModulePath, isSharedModule, userId); } catch (LegacyDexoptDisabledException e) { throw new RuntimeException(e); } } DexManager.RegisterDexModuleResult result = new DexManager.RegisterDexModuleResult( false, "registerDexModule call not supported since Android U"); if (callback != null) { mHandler.post(() -> { Loading services/core/java/com/android/server/pm/dex/DexManager.java +0 −57 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ 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; Loading Loading @@ -659,62 +658,6 @@ public class DexManager { } } // TODO(calin): questionable API in the presence of class loaders context. Needs amends as the // compilation happening here will use a pessimistic context. public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath, boolean isSharedModule, int userId) throws LegacyDexoptDisabledException { // Find the owning package record. DexSearchResult searchResult = getDexPackage(info, dexPath, userId); if (searchResult.mOutcome == DEX_SEARCH_NOT_FOUND) { return new RegisterDexModuleResult(false, "Package not found"); } if (!info.packageName.equals(searchResult.mOwningPackageName)) { return new RegisterDexModuleResult(false, "Dex path does not belong to package"); } if (searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY || searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT) { return new RegisterDexModuleResult(false, "Main apks cannot be registered"); } // We found the package. Now record the usage for all declared ISAs. boolean update = false; // If this is a shared module set the loading package to an arbitrary package name // so that we can mark that module as usedByOthers. String loadingPackage = isSharedModule ? ".shared.module" : searchResult.mOwningPackageName; for (String isa : getAppDexInstructionSets(info.primaryCpuAbi, info.secondaryCpuAbi)) { boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, userId, isa, /*primaryOrSplit*/ false, loadingPackage, PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, /*overwriteCLC=*/ false); update |= newUpdate; } if (update) { mPackageDexUsage.maybeWriteAsync(); } DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName) .getDexUseInfoMap().get(dexPath); // Try to optimize the package according to the install reason. DexoptOptions options = new DexoptOptions(info.packageName, PackageManagerService.REASON_INSTALL, /*flags*/0); int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo, options); // If we fail to optimize the package log an error but don't propagate the error // back to the app. The app cannot do much about it and the background job // will rety again when it executes. // TODO(calin): there might be some value to return the error here but it may // cause red herrings since that doesn't mean the app cannot use the module. if (result != PackageDexOptimizer.DEX_OPT_FAILED) { Slog.e(TAG, "Failed to optimize dex module " + dexPath); } return new RegisterDexModuleResult(true, "Dex module registered successfully"); } /** * Return all packages that contain records of secondary dex files. */ Loading services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java +0 −105 Original line number Diff line number Diff line Loading @@ -84,9 +84,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Collections; import java.util.HashSet; import java.util.List; Loading Loading @@ -2904,108 +2901,6 @@ public class PackageManagerTests extends AndroidTestCase { PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } private static class TestDexModuleRegisterCallback extends PackageManager.DexModuleRegisterCallback { private String mDexModulePath = null; private boolean mSuccess = false; private String mMessage = null; CountDownLatch doneSignal = new CountDownLatch(1); @Override public void onDexModuleRegistered(String dexModulePath, boolean success, String message) { mDexModulePath = dexModulePath; mSuccess = success; mMessage = message; doneSignal.countDown(); } boolean waitTillDone() { long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime < MAX_WAIT_TIME) { try { return doneSignal.await(MAX_WAIT_TIME, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Log.i(TAG, "Interrupted during sleep", e); } } return false; } } // Verify that the base code path cannot be registered. public void testRegisterDexModuleBaseCode() throws Exception { PackageManager pm = getPm(); ApplicationInfo info = getContext().getApplicationInfo(); TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); pm.registerDexModule(info.getBaseCodePath(), callback); assertTrue(callback.waitTillDone()); assertEquals(info.getBaseCodePath(), callback.mDexModulePath); assertFalse("BaseCodePath should not be registered", callback.mSuccess); } // Verify that modules which are not own by the calling package are not registered. public void testRegisterDexModuleNotOwningModule() throws Exception { TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); String moduleBelongingToOtherPackage = "/data/user/0/com.google.android.gms/module.apk"; getPm().registerDexModule(moduleBelongingToOtherPackage, callback); assertTrue(callback.waitTillDone()); assertEquals(moduleBelongingToOtherPackage, callback.mDexModulePath); assertTrue(callback.waitTillDone()); assertFalse("Only modules belonging to the calling package can be registered", callback.mSuccess); } // Verify that modules owned by the package are successfully registered. public void testRegisterDexModuleSuccessfully() throws Exception { ApplicationInfo info = getContext().getApplicationInfo(); // Copy the main apk into the data folder and use it as a "module". File dexModuleDir = new File(info.dataDir, "module-dir"); File dexModule = new File(dexModuleDir, "module.apk"); try { assertNotNull(FileUtils.createDir( dexModuleDir.getParentFile(), dexModuleDir.getName())); Files.copy(Paths.get(info.getBaseCodePath()), dexModule.toPath(), StandardCopyOption.REPLACE_EXISTING); TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); getPm().registerDexModule(dexModule.toString(), callback); assertTrue(callback.waitTillDone()); assertEquals(dexModule.toString(), callback.mDexModulePath); assertTrue(callback.waitTillDone()); assertTrue(callback.mMessage, callback.mSuccess); // NOTE: // This actually verifies internal behaviour which might change. It's not // ideal but it's the best we can do since there's no other place we can currently // write a better test. for(String isa : getAppDexInstructionSets(info)) { Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.odex")); Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.vdex")); } } finally { FileUtils.deleteContentsAndDir(dexModuleDir); } } // If the module does not exist on disk we should get a failure. public void testRegisterDexModuleNotExists() throws Exception { ApplicationInfo info = getContext().getApplicationInfo(); String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString(); TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); getPm().registerDexModule(nonExistentApk, callback); assertTrue(callback.waitTillDone()); assertEquals(nonExistentApk, callback.mDexModulePath); assertTrue(callback.waitTillDone()); assertFalse("DexModule registration should fail", callback.mSuccess); } // If the module does not exist on disk we should get a failure. public void testRegisterDexModuleNotExistsNoCallback() throws Exception { ApplicationInfo info = getContext().getApplicationInfo(); String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString(); getPm().registerDexModule(nonExistentApk, null); } @LargeTest public void testMinInstallableTargetSdkPass() throws Exception { // Test installing a package that meets the minimum installable sdk requirement Loading Loading
core/java/android/content/pm/PackageManager.java +2 −18 Original line number Diff line number Diff line Loading @@ -10134,25 +10134,9 @@ public abstract class PackageManager { /** * Register an application dex module with the package manager. * The package manager will keep track of the given module for future optimizations. * * Dex module optimizations will disable the classpath checking at runtime. The client bares * the responsibility to ensure that the static assumptions on classes in the optimized code * hold at runtime (e.g. there's no duplicate classes in the classpath). * * Note that the package manager already keeps track of dex modules loaded with * {@link dalvik.system.DexClassLoader} and {@link dalvik.system.PathClassLoader}. * This can be called for an eager registration. * * The call might take a while and the results will be posted on the main thread, using * the given callback. * * If the module is intended to be shared with other apps, make sure that the file * permissions allow for it. * If at registration time the permissions allow for others to read it, the module would * be marked as a shared module which might undergo a different optimization strategy. * (usually shared modules will generated larger optimizations artifacts, * taking more disk space). * This call no longer does anything. If a callback is given it is called with a false success * value. * * @param dexModulePath the absolute path of the dex module. * @param callback if not null, {@link DexModuleRegisterCallback#onDexModuleRegistered} will Loading
services/core/java/com/android/server/pm/PackageManagerService.java +12 −26 Original line number Diff line number Diff line Loading @@ -5561,32 +5561,18 @@ public class PackageManagerService implements PackageSender, TestUtilityService public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, IDexModuleRegisterCallback callback) { if (useArtService()) { // ART Service currently doesn't support this explicit dexopting and instead relies // on background dexopt for secondary dex files. This API is problematic since it // doesn't provide the correct classloader context. // ART Service doesn't support this explicit dexopting and instead relies on background // dexopt for secondary dex files. For compat parity between ART Service and the legacy // code it's disabled for both. // // Also, this API is problematic anyway since it doesn't provide the correct classloader // context, so it is hard to produce dexopt artifacts that the runtime can load // successfully. Slog.i(TAG, "Ignored unsupported registerDexModule call for " + dexModulePath + " in " + packageName); return; } int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = snapshot().getApplicationInfo(packageName, /*flags*/ 0, userId); DexManager.RegisterDexModuleResult result; if (ai == null) { Slog.w(PackageManagerService.TAG, "Registering a dex module for a package that does not exist for the" + " calling user. package=" + packageName + ", user=" + userId); result = new DexManager.RegisterDexModuleResult(false, "Package not installed"); } else { try { result = mDexManager.registerDexModule( ai, dexModulePath, isSharedModule, userId); } catch (LegacyDexoptDisabledException e) { throw new RuntimeException(e); } } DexManager.RegisterDexModuleResult result = new DexManager.RegisterDexModuleResult( false, "registerDexModule call not supported since Android U"); if (callback != null) { mHandler.post(() -> { Loading
services/core/java/com/android/server/pm/dex/DexManager.java +0 −57 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ 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; Loading Loading @@ -659,62 +658,6 @@ public class DexManager { } } // TODO(calin): questionable API in the presence of class loaders context. Needs amends as the // compilation happening here will use a pessimistic context. public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath, boolean isSharedModule, int userId) throws LegacyDexoptDisabledException { // Find the owning package record. DexSearchResult searchResult = getDexPackage(info, dexPath, userId); if (searchResult.mOutcome == DEX_SEARCH_NOT_FOUND) { return new RegisterDexModuleResult(false, "Package not found"); } if (!info.packageName.equals(searchResult.mOwningPackageName)) { return new RegisterDexModuleResult(false, "Dex path does not belong to package"); } if (searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY || searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT) { return new RegisterDexModuleResult(false, "Main apks cannot be registered"); } // We found the package. Now record the usage for all declared ISAs. boolean update = false; // If this is a shared module set the loading package to an arbitrary package name // so that we can mark that module as usedByOthers. String loadingPackage = isSharedModule ? ".shared.module" : searchResult.mOwningPackageName; for (String isa : getAppDexInstructionSets(info.primaryCpuAbi, info.secondaryCpuAbi)) { boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, userId, isa, /*primaryOrSplit*/ false, loadingPackage, PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, /*overwriteCLC=*/ false); update |= newUpdate; } if (update) { mPackageDexUsage.maybeWriteAsync(); } DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName) .getDexUseInfoMap().get(dexPath); // Try to optimize the package according to the install reason. DexoptOptions options = new DexoptOptions(info.packageName, PackageManagerService.REASON_INSTALL, /*flags*/0); int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo, options); // If we fail to optimize the package log an error but don't propagate the error // back to the app. The app cannot do much about it and the background job // will rety again when it executes. // TODO(calin): there might be some value to return the error here but it may // cause red herrings since that doesn't mean the app cannot use the module. if (result != PackageDexOptimizer.DEX_OPT_FAILED) { Slog.e(TAG, "Failed to optimize dex module " + dexPath); } return new RegisterDexModuleResult(true, "Dex module registered successfully"); } /** * Return all packages that contain records of secondary dex files. */ Loading
services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java +0 −105 Original line number Diff line number Diff line Loading @@ -84,9 +84,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Collections; import java.util.HashSet; import java.util.List; Loading Loading @@ -2904,108 +2901,6 @@ public class PackageManagerTests extends AndroidTestCase { PackageInfo.INSTALL_LOCATION_UNSPECIFIED); } private static class TestDexModuleRegisterCallback extends PackageManager.DexModuleRegisterCallback { private String mDexModulePath = null; private boolean mSuccess = false; private String mMessage = null; CountDownLatch doneSignal = new CountDownLatch(1); @Override public void onDexModuleRegistered(String dexModulePath, boolean success, String message) { mDexModulePath = dexModulePath; mSuccess = success; mMessage = message; doneSignal.countDown(); } boolean waitTillDone() { long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime < MAX_WAIT_TIME) { try { return doneSignal.await(MAX_WAIT_TIME, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Log.i(TAG, "Interrupted during sleep", e); } } return false; } } // Verify that the base code path cannot be registered. public void testRegisterDexModuleBaseCode() throws Exception { PackageManager pm = getPm(); ApplicationInfo info = getContext().getApplicationInfo(); TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); pm.registerDexModule(info.getBaseCodePath(), callback); assertTrue(callback.waitTillDone()); assertEquals(info.getBaseCodePath(), callback.mDexModulePath); assertFalse("BaseCodePath should not be registered", callback.mSuccess); } // Verify that modules which are not own by the calling package are not registered. public void testRegisterDexModuleNotOwningModule() throws Exception { TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); String moduleBelongingToOtherPackage = "/data/user/0/com.google.android.gms/module.apk"; getPm().registerDexModule(moduleBelongingToOtherPackage, callback); assertTrue(callback.waitTillDone()); assertEquals(moduleBelongingToOtherPackage, callback.mDexModulePath); assertTrue(callback.waitTillDone()); assertFalse("Only modules belonging to the calling package can be registered", callback.mSuccess); } // Verify that modules owned by the package are successfully registered. public void testRegisterDexModuleSuccessfully() throws Exception { ApplicationInfo info = getContext().getApplicationInfo(); // Copy the main apk into the data folder and use it as a "module". File dexModuleDir = new File(info.dataDir, "module-dir"); File dexModule = new File(dexModuleDir, "module.apk"); try { assertNotNull(FileUtils.createDir( dexModuleDir.getParentFile(), dexModuleDir.getName())); Files.copy(Paths.get(info.getBaseCodePath()), dexModule.toPath(), StandardCopyOption.REPLACE_EXISTING); TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); getPm().registerDexModule(dexModule.toString(), callback); assertTrue(callback.waitTillDone()); assertEquals(dexModule.toString(), callback.mDexModulePath); assertTrue(callback.waitTillDone()); assertTrue(callback.mMessage, callback.mSuccess); // NOTE: // This actually verifies internal behaviour which might change. It's not // ideal but it's the best we can do since there's no other place we can currently // write a better test. for(String isa : getAppDexInstructionSets(info)) { Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.odex")); Files.exists(Paths.get(dexModuleDir.toString(), "oat", isa, "module.vdex")); } } finally { FileUtils.deleteContentsAndDir(dexModuleDir); } } // If the module does not exist on disk we should get a failure. public void testRegisterDexModuleNotExists() throws Exception { ApplicationInfo info = getContext().getApplicationInfo(); String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString(); TestDexModuleRegisterCallback callback = new TestDexModuleRegisterCallback(); getPm().registerDexModule(nonExistentApk, callback); assertTrue(callback.waitTillDone()); assertEquals(nonExistentApk, callback.mDexModulePath); assertTrue(callback.waitTillDone()); assertFalse("DexModule registration should fail", callback.mSuccess); } // If the module does not exist on disk we should get a failure. public void testRegisterDexModuleNotExistsNoCallback() throws Exception { ApplicationInfo info = getContext().getApplicationInfo(); String nonExistentApk = Paths.get(info.dataDir, "non-existent.apk").toString(); getPm().registerDexModule(nonExistentApk, null); } @LargeTest public void testMinInstallableTargetSdkPass() throws Exception { // Test installing a package that meets the minimum installable sdk requirement Loading