Merge "apk-in-apex-cache 1/n: Fix isCacheUpToDate for apk-in-apex" into tm-dev
This commit is contained in:
@@ -67,6 +67,7 @@ import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -119,16 +120,18 @@ public abstract class ApexManager {
|
||||
@Nullable public final String apexModuleName;
|
||||
public final File apexDirectory;
|
||||
public final File preInstalledApexPath;
|
||||
public final File apexFile;
|
||||
|
||||
private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) {
|
||||
this(null, apexDirectory, preInstalledApexPath);
|
||||
private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) {
|
||||
this(null, apexDirectory, preInstalledApexPath, apexFile);
|
||||
}
|
||||
|
||||
private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory,
|
||||
File preInstalledApexPath) {
|
||||
File preInstalledApexPath, File apexFile) {
|
||||
this.apexModuleName = apexModuleName;
|
||||
this.apexDirectory = apexDirectory;
|
||||
this.preInstalledApexPath = preInstalledApexPath;
|
||||
this.apexFile = apexFile;
|
||||
}
|
||||
|
||||
private ActiveApexInfo(ApexInfo apexInfo) {
|
||||
@@ -136,7 +139,8 @@ public abstract class ApexManager {
|
||||
apexInfo.moduleName,
|
||||
new File(Environment.getApexDirectory() + File.separator
|
||||
+ apexInfo.moduleName),
|
||||
new File(apexInfo.preinstalledModulePath));
|
||||
new File(apexInfo.preinstalledModulePath),
|
||||
new File(apexInfo.modulePath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +431,15 @@ public abstract class ApexManager {
|
||||
*/
|
||||
public abstract List<ApexSystemServiceInfo> getApexSystemServices();
|
||||
|
||||
/**
|
||||
* Returns an APEX file backing the mount point {@code file} is located on, or {@code null} if
|
||||
* {@code file} doesn't belong to a {@code /apex} mount point.
|
||||
*
|
||||
* <p>Also returns {@code null} if device doesn't support updatable APEX packages.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract File getBackingApexFile(@NonNull File file);
|
||||
|
||||
/**
|
||||
* Dumps various state information to the provided {@link PrintWriter} object.
|
||||
*
|
||||
@@ -451,6 +464,7 @@ public abstract class ApexManager {
|
||||
protected static class ApexManagerImpl extends ApexManager {
|
||||
private final Object mLock = new Object();
|
||||
|
||||
// TODO(ioffe): this should be either List or ArrayMap.
|
||||
@GuardedBy("mLock")
|
||||
private Set<ActiveApexInfo> mActiveApexInfosCache;
|
||||
|
||||
@@ -1184,6 +1198,25 @@ public abstract class ApexManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getBackingApexFile(File file) {
|
||||
Path path = file.toPath();
|
||||
if (!path.startsWith(Environment.getApexDirectory().toPath())) {
|
||||
return null;
|
||||
}
|
||||
if (path.getNameCount() < 2) {
|
||||
return null;
|
||||
}
|
||||
String moduleName = file.toPath().getName(1).toString();
|
||||
final List<ActiveApexInfo> apexes = getActiveApexInfos();
|
||||
for (int i = 0; i < apexes.size(); i++) {
|
||||
if (apexes.get(i).apexModuleName.equals(moduleName)) {
|
||||
return apexes.get(i).apexFile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump information about the packages contained in a particular cache
|
||||
* @param packagesCache the cache to print information about.
|
||||
@@ -1274,7 +1307,8 @@ public abstract class ApexManager {
|
||||
* An implementation of {@link ApexManager} that should be used in case device does not support
|
||||
* updating APEX packages.
|
||||
*/
|
||||
private static final class ApexManagerFlattenedApex extends ApexManager {
|
||||
@VisibleForTesting
|
||||
static final class ApexManagerFlattenedApex extends ApexManager {
|
||||
@Override
|
||||
public List<ActiveApexInfo> getActiveApexInfos() {
|
||||
// There is no apexd running in case of flattened apex
|
||||
@@ -1293,7 +1327,8 @@ public abstract class ApexManager {
|
||||
// In flattened configuration, init special-cases the art directory
|
||||
// and bind-mounts com.android.art.debug to com.android.art.
|
||||
&& !file.getName().equals("com.android.art.debug")) {
|
||||
result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
|
||||
result.add(
|
||||
new ActiveApexInfo(file, Environment.getRootDirectory(), file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1478,6 +1513,11 @@ public abstract class ApexManager {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getBackingApexFile(File file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void dump(PrintWriter pw, String packageName) {
|
||||
// No-op
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.pm.parsing;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.pm.PackageParserCacheHelper;
|
||||
import android.os.Environment;
|
||||
import android.os.FileUtils;
|
||||
import android.os.Parcel;
|
||||
import android.system.ErrnoException;
|
||||
@@ -27,6 +28,7 @@ import android.system.StructStat;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.pm.ApexManager;
|
||||
import com.android.server.pm.parsing.pkg.PackageImpl;
|
||||
import com.android.server.pm.parsing.pkg.ParsedPackage;
|
||||
|
||||
@@ -118,6 +120,17 @@ public class PackageCacher {
|
||||
*/
|
||||
private static boolean isCacheUpToDate(File packageFile, File cacheFile) {
|
||||
try {
|
||||
// In case packageFile is located on one of /apex mount points it's mtime will always be
|
||||
// 0. Instead, we can use mtime of the APEX file backing the corresponding mount point.
|
||||
if (packageFile.toPath().startsWith(Environment.getApexDirectory().toPath())) {
|
||||
File backingApexFile = ApexManager.getInstance().getBackingApexFile(packageFile);
|
||||
if (backingApexFile == null) {
|
||||
Slog.w(TAG,
|
||||
"Failed to find APEX file backing " + packageFile.getAbsolutePath());
|
||||
} else {
|
||||
packageFile = backingApexFile;
|
||||
}
|
||||
}
|
||||
// NOTE: We don't use the File.lastModified API because it has the very
|
||||
// non-ideal failure mode of returning 0 with no excepions thrown.
|
||||
// The nio2 Files API is a little better but is considerably more expensive.
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.apex.IApexService;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.Environment;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
@@ -503,6 +504,48 @@ public class ApexManagerTest {
|
||||
.isEqualTo(TEST_APEX_PKG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackingApexFiles() throws Exception {
|
||||
final ApexInfo apex = createApexInfoForTestPkg(true, true, 37);
|
||||
when(mApexService.getActivePackages()).thenReturn(new ApexInfo[]{apex});
|
||||
|
||||
final File backingApexFile = mApexManager.getBackingApexFile(
|
||||
new File("/apex/" + TEST_APEX_PKG + "/apk/App/App.apk"));
|
||||
assertThat(backingApexFile.getAbsolutePath()).isEqualTo(apex.modulePath);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackingApexFile_fileNotOnApexMountPoint_returnsNull() throws Exception {
|
||||
File result = mApexManager.getBackingApexFile(
|
||||
new File("/data/local/tmp/whatever/does-not-matter"));
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackingApexFiles_unknownApex_returnsNull() throws Exception {
|
||||
final ApexInfo apex = createApexInfoForTestPkg(true, true, 37);
|
||||
when(mApexService.getActivePackages()).thenReturn(new ApexInfo[]{apex});
|
||||
|
||||
final File backingApexFile = mApexManager.getBackingApexFile(
|
||||
new File("/apex/com.wrong.apex/apk/App"));
|
||||
assertThat(backingApexFile).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackingApexFiles_topLevelApexDir_returnsNull() throws Exception {
|
||||
assertThat(mApexManager.getBackingApexFile(Environment.getApexDirectory())).isNull();
|
||||
assertThat(mApexManager.getBackingApexFile(new File("/apex/"))).isNull();
|
||||
assertThat(mApexManager.getBackingApexFile(new File("/apex//"))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackingApexFiles_flattenedApex() throws Exception {
|
||||
ApexManager flattenedApexManager = new ApexManager.ApexManagerFlattenedApex();
|
||||
final File backingApexFile = flattenedApexManager.getBackingApexFile(
|
||||
new File("/apex/com.android.apex.cts.shim/app/CtsShim/CtsShim.apk"));
|
||||
assertThat(backingApexFile).isNull();
|
||||
}
|
||||
|
||||
private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) {
|
||||
File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME);
|
||||
ApexInfo apexInfo = new ApexInfo();
|
||||
|
||||
Reference in New Issue
Block a user