Loading services/core/java/com/android/server/pm/ApexManager.java +46 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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)); } } Loading Loading @@ -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. * Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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 Loading @@ -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)); } } } Loading Loading @@ -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 Loading 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 @@ -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(); Loading Loading
services/core/java/com/android/server/pm/ApexManager.java +46 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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)); } } Loading Loading @@ -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. * Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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 Loading @@ -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)); } } } Loading Loading @@ -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 Loading
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 @@ -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(); Loading