Merge "Make registerDexModule a nop regardless ART Service is enabled or not." into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
feba032951
@@ -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
|
||||
|
||||
@@ -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.
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
DexManager.RegisterDexModuleResult result = new DexManager.RegisterDexModuleResult(
|
||||
false, "registerDexModule call not supported since Android U");
|
||||
|
||||
if (callback != null) {
|
||||
mHandler.post(() -> {
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user