Loading services/core/java/com/android/server/pm/ApexManager.java +46 −7 Original line number Diff line number Diff line Loading @@ -62,6 +62,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.List; Loading Loading @@ -122,17 +123,19 @@ public abstract class ApexManager { public final File apexDirectory; public final File preInstalledApexPath; public final boolean isFactory; public final File apexFile; private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) { this(null, apexDirectory, preInstalledApexPath, true); private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) { this(null, apexDirectory, preInstalledApexPath, true, apexFile); } private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, File preInstalledApexPath, boolean isFactory) { File preInstalledApexPath, boolean isFactory, File apexFile) { this.apexModuleName = apexModuleName; this.apexDirectory = apexDirectory; this.preInstalledApexPath = preInstalledApexPath; this.isFactory = isFactory; this.apexFile = apexFile; } private ActiveApexInfo(ApexInfo apexInfo) { Loading @@ -141,7 +144,8 @@ public abstract class ApexManager { new File(Environment.getApexDirectory() + File.separator + apexInfo.moduleName), new File(apexInfo.preinstalledModulePath), apexInfo.isFactory); apexInfo.isFactory, new File(apexInfo.modulePath)); } } Loading Loading @@ -370,6 +374,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. * Loading @@ -392,6 +405,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; Loading Loading @@ -943,6 +957,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; } @Override void dump(PrintWriter pw) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); Loading Loading @@ -987,7 +1020,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 ApexInfo[] getAllApexInfos() { return null; Loading Loading @@ -1016,7 +1050,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)); } } } Loading Loading @@ -1173,7 +1208,11 @@ public abstract class ApexManager { @Override void dump(PrintWriter pw) { // No-op } @Override public File getBackingApexFile(File file) { return null; } } } services/core/java/com/android/server/pm/parsing/PackageCacher.java +13 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. Loading services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +43 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -531,6 +532,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(); Loading Loading
services/core/java/com/android/server/pm/ApexManager.java +46 −7 Original line number Diff line number Diff line Loading @@ -62,6 +62,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.List; Loading Loading @@ -122,17 +123,19 @@ public abstract class ApexManager { public final File apexDirectory; public final File preInstalledApexPath; public final boolean isFactory; public final File apexFile; private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) { this(null, apexDirectory, preInstalledApexPath, true); private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) { this(null, apexDirectory, preInstalledApexPath, true, apexFile); } private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, File preInstalledApexPath, boolean isFactory) { File preInstalledApexPath, boolean isFactory, File apexFile) { this.apexModuleName = apexModuleName; this.apexDirectory = apexDirectory; this.preInstalledApexPath = preInstalledApexPath; this.isFactory = isFactory; this.apexFile = apexFile; } private ActiveApexInfo(ApexInfo apexInfo) { Loading @@ -141,7 +144,8 @@ public abstract class ApexManager { new File(Environment.getApexDirectory() + File.separator + apexInfo.moduleName), new File(apexInfo.preinstalledModulePath), apexInfo.isFactory); apexInfo.isFactory, new File(apexInfo.modulePath)); } } Loading Loading @@ -370,6 +374,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. * Loading @@ -392,6 +405,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; Loading Loading @@ -943,6 +957,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; } @Override void dump(PrintWriter pw) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); Loading Loading @@ -987,7 +1020,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 ApexInfo[] getAllApexInfos() { return null; Loading Loading @@ -1016,7 +1050,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)); } } } Loading Loading @@ -1173,7 +1208,11 @@ public abstract class ApexManager { @Override void dump(PrintWriter pw) { // No-op } @Override public File getBackingApexFile(File file) { return null; } } }
services/core/java/com/android/server/pm/parsing/PackageCacher.java +13 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. Loading
services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +43 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -531,6 +532,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(); Loading