diff options
author | Jiakai Zhang <jiakaiz@google.com> | 2024-01-17 12:43:00 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2024-01-17 12:43:00 +0000 |
commit | 82a58fad0b5fd7a3246697102fda17b31a232cf7 (patch) | |
tree | 9edf633d088c14a042add75168ceaa602cbccffd | |
parent | fed47408742f8608db293aa7ff8cf1460a4f7b70 (diff) | |
parent | a1e314b375a81f4dc75f149baaadf26798fb5775 (diff) | |
download | art-82a58fad0b5fd7a3246697102fda17b31a232cf7.tar.gz |
Change `DexUseManager.getFilteredDetailedSecondaryDexInfo`. am: a1e314b375
Original change: https://android-review.googlesource.com/c/platform/art/+/2897923
Change-Id: I1f3dabf5a0d6f3970f0c7e3d04c870b1a605123d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
9 files changed, 178 insertions, 150 deletions
diff --git a/libartservice/service/java/com/android/server/art/ArtFileManager.java b/libartservice/service/java/com/android/server/art/ArtFileManager.java index 3db91642b6..51e8462b0c 100644 --- a/libartservice/service/java/com/android/server/art/ArtFileManager.java +++ b/libartservice/service/java/com/android/server/art/ArtFileManager.java @@ -69,13 +69,14 @@ public class ArtFileManager { } /** - * @param excludeNotFound If true, excludes secondary dex files that are not found on the - * filesystem. + * @param excludeObsoleteDexesAndLoaders If true, excludes secondary dex files and loaders based + * on file visibility. See details in {@link + * DexUseManagerLocal#getCheckedSecondaryDexInfo}. */ @NonNull public List<Pair<DetailedDexInfo, Abi>> getDexAndAbis(@NonNull PackageState pkgState, @NonNull AndroidPackage pkg, boolean forPrimaryDex, boolean forSecondaryDex, - boolean excludeNotFound) { + boolean excludeObsoleteDexesAndLoaders) { List<Pair<DetailedDexInfo, Abi>> dexAndAbis = new ArrayList<>(); if (forPrimaryDex) { for (DetailedPrimaryDexInfo dexInfo : @@ -86,9 +87,9 @@ public class ArtFileManager { } } if (forSecondaryDex) { - List<? extends SecondaryDexInfo> dexInfos = excludeNotFound - ? mInjector.getDexUseManager().getFilteredDetailedSecondaryDexInfo( - pkgState.getPackageName()) + List<? extends SecondaryDexInfo> dexInfos = excludeObsoleteDexesAndLoaders + ? mInjector.getDexUseManager().getCheckedSecondaryDexInfo( + pkgState.getPackageName(), true /* excludeObsoleteDexesAndLoaders */) : mInjector.getDexUseManager().getSecondaryDexInfo(pkgState.getPackageName()); for (SecondaryDexInfo dexInfo : dexInfos) { for (Abi abi : Utils.getAllAbisForNames(dexInfo.abiNames(), pkgState)) { @@ -141,7 +142,7 @@ public class ArtFileManager { for (Pair<DetailedDexInfo, Abi> pair : getDexAndAbis(pkgState, pkg, true /* forPrimaryDex */, true /* forSecondaryDex */, - true /* excludeNotFound */)) { + true /* excludeObsoleteDexesAndLoaders */)) { DetailedDexInfo dexInfo = pair.first; Abi abi = pair.second; try { @@ -178,9 +179,14 @@ public class ArtFileManager { return UsableArtifactLists.create(artifacts, vdexFiles, runtimeArtifacts); } + /** + * @param excludeForObsoleteDexesAndLoaders If true, excludes profiles for secondary dex files + * and loaders based on file visibility. See details in {@link + * DexUseManagerLocal#getCheckedSecondaryDexInfo}. + */ @NonNull public ProfileLists getProfiles(@NonNull PackageState pkgState, @NonNull AndroidPackage pkg, - boolean alsoForSecondaryDex, boolean excludeDexNotFound) { + boolean alsoForSecondaryDex, boolean excludeForObsoleteDexesAndLoaders) { List<ProfilePath> refProfiles = new ArrayList<>(); List<ProfilePath> curProfiles = new ArrayList<>(); @@ -190,9 +196,9 @@ public class ArtFileManager { PrimaryDexUtils.getCurProfiles(mInjector.getUserManager(), pkgState, dexInfo)); } if (alsoForSecondaryDex) { - List<? extends SecondaryDexInfo> dexInfos = excludeDexNotFound - ? mInjector.getDexUseManager().getFilteredDetailedSecondaryDexInfo( - pkgState.getPackageName()) + List<? extends SecondaryDexInfo> dexInfos = excludeForObsoleteDexesAndLoaders + ? mInjector.getDexUseManager().getCheckedSecondaryDexInfo( + pkgState.getPackageName(), true /* excludeForObsoleteDexesAndLoaders */) : mInjector.getDexUseManager().getSecondaryDexInfo(pkgState.getPackageName()); for (SecondaryDexInfo dexInfo : dexInfos) { refProfiles.add(AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath())); diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java index b8e05494f6..8e5c1eca62 100644 --- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java @@ -248,7 +248,8 @@ public final class ArtManagerLocal { AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); List<Pair<DetailedDexInfo, Abi>> dexAndAbis = mInjector.getArtFileManager().getDexAndAbis( pkgState, pkg, (flags & ArtFlags.FLAG_FOR_PRIMARY_DEX) != 0, - (flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) != 0, false /* excludeNotFound */); + (flags & ArtFlags.FLAG_FOR_SECONDARY_DEX) != 0, + false /* excludeObsoleteDexesAndLoaders */); try { List<DexContainerFileDexoptStatus> statuses = new ArrayList<>(); @@ -305,8 +306,8 @@ public final class ArtManagerLocal { // We want to delete as many profiles as possible, so this deletes profiles of all known // secondary dex files. If there are unknown secondary dex files, their profiles will be // deleted by `cleanup`. - ProfileLists list = mInjector.getArtFileManager().getProfiles( - pkgState, pkg, true /* alsoForSecondaryDex */, false /* excludeDexNotFound */); + ProfileLists list = mInjector.getArtFileManager().getProfiles(pkgState, pkg, + true /* alsoForSecondaryDex */, false /* excludeForObsoleteDexesAndLoaders */); for (ProfilePath profile : list.allProfiles()) { mInjector.getArtd().deleteProfile(profile); } @@ -804,7 +805,8 @@ public final class ArtManagerLocal { if (Utils.canDexoptPackage(appPkgState, null /* appHibernationManager */)) { AndroidPackage appPkg = Utils.getPackageOrThrow(appPkgState); ProfileLists list = mInjector.getArtFileManager().getProfiles(appPkgState, appPkg, - false /* alsoForSecondaryDex */, true /* excludeDexNotFound */); + false /* alsoForSecondaryDex */, + true /* excludeForObsoleteDexesAndLoaders */); profiles.addAll(list.allProfiles()); } }); @@ -908,8 +910,8 @@ public final class ArtManagerLocal { artifactsSize += artd.getRuntimeArtifactsSize(runtimeArtifacts); } - ProfileLists profileLists = mInjector.getArtFileManager().getProfiles( - pkgState, pkg, true /* alsoForSecondaryDex */, true /* excludeDexNotFound */); + ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg, + true /* alsoForSecondaryDex */, true /* excludeForObsoleteDexesAndLoaders */); for (ProfilePath profile : profileLists.refProfiles()) { refProfilesSize += artd.getProfileSize(profile); } @@ -960,7 +962,8 @@ public final class ArtManagerLocal { } AndroidPackage pkg = Utils.getPackageOrThrow(pkgState); ProfileLists profileLists = mInjector.getArtFileManager().getProfiles(pkgState, pkg, - true /* alsoForSecondaryDex */, true /* excludeDexNotFound */); + true /* alsoForSecondaryDex */, + true /* excludeForObsoleteDexesAndLoaders */); profilesToKeep.addAll(profileLists.allProfiles()); if (!Utils.shouldSkipDexoptDueToHibernation( pkgState, mInjector.getAppHibernationManager())) { diff --git a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java index b9d0afe0d9..e9ef1a3e37 100644 --- a/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java +++ b/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java @@ -230,23 +230,30 @@ public class DexUseManagerLocal { * method doesn't take dex file visibility into account, so it can only be used for debugging * purpose, such as dumpsys. * - * @see #getFilteredDetailedSecondaryDexInfo(String) + * @see #getCheckedSecondaryDexInfo(String) * @hide */ public @NonNull List<? extends SecondaryDexInfo> getSecondaryDexInfo( @NonNull String packageName) { - return getSecondaryDexInfoImpl(packageName, false /* checkDexFile */); + return getSecondaryDexInfoImpl( + packageName, false /* checkDexFile */, false /* excludeObsoleteDexesAndLoaders */); } /** * Same as above, but requires disk IO, and returns the detailed information, including dex file - * visibility, filtered by dex file existence and visibility. + * visibility. + * + * @param excludeObsoleteDexesAndLoaders If true, excludes secondary dex files and loaders based + * on file visibility. More specifically, excludes loaders that can no longer load a + * secondary dex file due to a file visibility change, and excludes secondary dex files + * that are not found or only have obsolete loaders * * @hide */ - public @NonNull List<DetailedSecondaryDexInfo> getFilteredDetailedSecondaryDexInfo( - @NonNull String packageName) { - return getSecondaryDexInfoImpl(packageName, true /* checkDexFile */); + public @NonNull List<CheckedSecondaryDexInfo> getCheckedSecondaryDexInfo( + @NonNull String packageName, boolean excludeObsoleteDexesAndLoaders) { + return getSecondaryDexInfoImpl( + packageName, true /* checkDexFile */, excludeObsoleteDexesAndLoaders); } /** @@ -282,20 +289,22 @@ public class DexUseManagerLocal { } /** - * @param checkDexFile if true, check the existence and visibility of the dex files, and filter - * the results accordingly. Note that the value of the {@link - * DetailedSecondaryDexInfo#isDexFilePublic()} field is undefined if this argument is - * false. + * @param checkDexFile if true, check the existence and visibility of the dex files. Note that + * the value of the {@link CheckedSecondaryDexInfo#fileVisibility()} field is undefined + * if this argument is false + * @param excludeObsoleteDexesAndLoaders see {@link #getCheckedSecondaryDexInfo}. Only takes + * effect if {@code checkDexFile} is true */ - private @NonNull List<DetailedSecondaryDexInfo> getSecondaryDexInfoImpl( - @NonNull String packageName, boolean checkDexFile) { + private @NonNull List<CheckedSecondaryDexInfo> getSecondaryDexInfoImpl( + @NonNull String packageName, boolean checkDexFile, + boolean excludeObsoleteDexesAndLoaders) { synchronized (mLock) { PackageDexUse packageDexUse = mDexUse.mPackageDexUseByOwningPackageName.get(packageName); if (packageDexUse == null) { return List.of(); } - var results = new ArrayList<DetailedSecondaryDexInfo>(); + var results = new ArrayList<CheckedSecondaryDexInfo>(); for (var entry : packageDexUse.mSecondaryDexUseByDexFile.entrySet()) { String dexPath = entry.getKey(); SecondaryDexUse secondaryDexUse = entry.getValue(); @@ -303,12 +312,13 @@ public class DexUseManagerLocal { @FileVisibility int visibility = checkDexFile ? getDexFileVisibility(dexPath) : FileVisibility.OTHER_READABLE; - if (visibility == FileVisibility.NOT_FOUND) { + if (visibility == FileVisibility.NOT_FOUND && excludeObsoleteDexesAndLoaders) { continue; } Map<DexLoader, SecondaryDexUseRecord> filteredRecordByLoader; - if (visibility == FileVisibility.OTHER_READABLE) { + if (visibility == FileVisibility.OTHER_READABLE + || !excludeObsoleteDexesAndLoaders) { filteredRecordByLoader = secondaryDexUse.mRecordByLoader; } else { // Only keep the entry that belongs to the same app. @@ -347,10 +357,9 @@ public class DexUseManagerLocal { .map(record -> Utils.assertNonEmpty(record.mAbiName)) .collect(Collectors.toSet()); Set<DexLoader> loaders = Set.copyOf(filteredRecordByLoader.keySet()); - results.add(DetailedSecondaryDexInfo.create(dexPath, + results.add(CheckedSecondaryDexInfo.create(dexPath, Objects.requireNonNull(secondaryDexUse.mUserHandle), clc, distinctAbiNames, - loaders, isUsedByOtherApps(loaders, packageName), - visibility == FileVisibility.OTHER_READABLE)); + loaders, isUsedByOtherApps(loaders, packageName), visibility)); } return Collections.unmodifiableList(results); } @@ -857,22 +866,19 @@ public class DexUseManagerLocal { */ @Immutable @AutoValue - public abstract static class DetailedSecondaryDexInfo + public abstract static class CheckedSecondaryDexInfo extends SecondaryDexInfo implements DetailedDexInfo { - static DetailedSecondaryDexInfo create(@NonNull String dexPath, + static CheckedSecondaryDexInfo create(@NonNull String dexPath, @NonNull UserHandle userHandle, @NonNull String displayClassLoaderContext, @NonNull Set<String> abiNames, @NonNull Set<DexLoader> loaders, - boolean isUsedByOtherApps, boolean isDexFilePublic) { - return new AutoValue_DexUseManagerLocal_DetailedSecondaryDexInfo(dexPath, userHandle, + boolean isUsedByOtherApps, @FileVisibility int fileVisibility) { + return new AutoValue_DexUseManagerLocal_CheckedSecondaryDexInfo(dexPath, userHandle, displayClassLoaderContext, Collections.unmodifiableSet(abiNames), - Collections.unmodifiableSet(loaders), isUsedByOtherApps, isDexFilePublic); + Collections.unmodifiableSet(loaders), isUsedByOtherApps, fileVisibility); } - /** - * Returns true if the filesystem permission of the dex file has the "read" bit for "others" - * (S_IROTH). - */ - public abstract boolean isDexFilePublic(); + /** Indicates the visibility of the dex file. */ + public abstract @FileVisibility int fileVisibility(); } private static class DexUse { diff --git a/libartservice/service/java/com/android/server/art/DumpHelper.java b/libartservice/service/java/com/android/server/art/DumpHelper.java index 2a640ec48d..d9523b9fae 100644 --- a/libartservice/service/java/com/android/server/art/DumpHelper.java +++ b/libartservice/service/java/com/android/server/art/DumpHelper.java @@ -16,8 +16,8 @@ package com.android.server.art; +import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; import static com.android.server.art.DexUseManagerLocal.DexLoader; -import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo; import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus; import android.annotation.NonNull; @@ -99,11 +99,13 @@ public class DumpHelper { mInjector.getArtManagerLocal() .getDexoptStatus(snapshot, packageName) .getDexContainerFileDexoptStatuses(); - Map<String, SecondaryDexInfo> secondaryDexInfoByDexPath = + Map<String, CheckedSecondaryDexInfo> secondaryDexInfoByDexPath = mInjector.getDexUseManager() - .getSecondaryDexInfo(packageName) + .getCheckedSecondaryDexInfo( + packageName, false /* excludeObsoleteDexesAndLoaders */) .stream() - .collect(Collectors.toMap(SecondaryDexInfo::dexPath, Function.identity())); + .collect(Collectors.toMap( + CheckedSecondaryDexInfo::dexPath, Function.identity())); // Use LinkedHashMap to keep the order. They are ordered by their split indexes. var primaryStatusesByDexPath = @@ -157,9 +159,9 @@ public class DumpHelper { private void dumpSecondaryDex(@NonNull IndentingPrintWriter ipw, @NonNull PackageManagerLocal.FilteredSnapshot snapshot, List<DexContainerFileDexoptStatus> fileStatuses, @NonNull String packageName, - @NonNull SecondaryDexInfo info) { + @NonNull CheckedSecondaryDexInfo info) { String dexPath = fileStatuses.get(0).getDexContainerFile(); - @FileVisibility int visibility = getDexFileVisibility(dexPath); + @FileVisibility int visibility = info.fileVisibility(); ipw.println(dexPath + (visibility == FileVisibility.NOT_FOUND ? " (removed)" @@ -236,15 +238,6 @@ public class DumpHelper { return result.toString(); } - private @FileVisibility int getDexFileVisibility(@NonNull String dexPath) { - try { - return mInjector.getArtd().getDexFileVisibility(dexPath); - } catch (ServiceSpecificException | RemoteException e) { - Log.e(TAG, "Failed to get visibility of " + dexPath, e); - return FileVisibility.NOT_FOUND; - } - } - /** Injector pattern for testing purpose. */ @VisibleForTesting public static class Injector { @@ -264,10 +257,5 @@ public class DumpHelper { return Objects.requireNonNull( LocalManagerRegistry.getManager(DexUseManagerLocal.class)); } - - @NonNull - public IArtd getArtd() { - return Utils.getArtd(); - } } } diff --git a/libartservice/service/java/com/android/server/art/SecondaryDexopter.java b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java index 892147de68..735103c10a 100644 --- a/libartservice/service/java/com/android/server/art/SecondaryDexopter.java +++ b/libartservice/service/java/com/android/server/art/SecondaryDexopter.java @@ -16,7 +16,7 @@ package com.android.server.art; -import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo; +import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; import static com.android.server.art.OutputArtifacts.PermissionSettings; import static com.android.server.art.OutputArtifacts.PermissionSettings.SeContext; import static com.android.server.art.Utils.Abi; @@ -38,7 +38,7 @@ import java.util.List; /** @hide */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) -public class SecondaryDexopter extends Dexopter<DetailedSecondaryDexInfo> { +public class SecondaryDexopter extends Dexopter<CheckedSecondaryDexInfo> { private static final String TAG = ArtManagerLocal.TAG; public SecondaryDexopter(@NonNull Context context, @NonNull PackageState pkgState, @@ -63,29 +63,29 @@ public class SecondaryDexopter extends Dexopter<DetailedSecondaryDexInfo> { @Override @NonNull - protected List<DetailedSecondaryDexInfo> getDexInfoList() { - return mInjector.getDexUseManager().getFilteredDetailedSecondaryDexInfo( - mPkgState.getPackageName()); + protected List<CheckedSecondaryDexInfo> getDexInfoList() { + return mInjector.getDexUseManager().getCheckedSecondaryDexInfo( + mPkgState.getPackageName(), true /* excludeObsoleteDexesAndLoaders */); } @Override - protected boolean isDexoptable(@NonNull DetailedSecondaryDexInfo dexInfo) { + protected boolean isDexoptable(@NonNull CheckedSecondaryDexInfo dexInfo) { return true; } @Override - protected boolean needsToBeShared(@NonNull DetailedSecondaryDexInfo dexInfo) { + protected boolean needsToBeShared(@NonNull CheckedSecondaryDexInfo dexInfo) { return dexInfo.isUsedByOtherApps(); } @Override - protected boolean isDexFilePublic(@NonNull DetailedSecondaryDexInfo dexInfo) { - return dexInfo.isDexFilePublic(); + protected boolean isDexFilePublic(@NonNull CheckedSecondaryDexInfo dexInfo) { + return dexInfo.fileVisibility() == FileVisibility.OTHER_READABLE; } @Override @NonNull - protected List<ProfilePath> getExternalProfiles(@NonNull DetailedSecondaryDexInfo dexInfo) { + protected List<ProfilePath> getExternalProfiles(@NonNull CheckedSecondaryDexInfo dexInfo) { // A secondary dex file doesn't have any external profile to use. return List.of(); } @@ -93,7 +93,7 @@ public class SecondaryDexopter extends Dexopter<DetailedSecondaryDexInfo> { @Override @NonNull protected PermissionSettings getPermissionSettings( - @NonNull DetailedSecondaryDexInfo dexInfo, boolean canBePublic) { + @NonNull CheckedSecondaryDexInfo dexInfo, boolean canBePublic) { int uid = getUid(dexInfo); // We need the "execute" bit for "others" even though `canBePublic` is false because the // directory can contain other artifacts that needs to be public. @@ -109,38 +109,38 @@ public class SecondaryDexopter extends Dexopter<DetailedSecondaryDexInfo> { @Override @NonNull - protected List<Abi> getAllAbis(@NonNull DetailedSecondaryDexInfo dexInfo) { + protected List<Abi> getAllAbis(@NonNull CheckedSecondaryDexInfo dexInfo) { return Utils.getAllAbisForNames(dexInfo.abiNames(), mPkgState); } @Override @NonNull - protected ProfilePath buildRefProfilePath(@NonNull DetailedSecondaryDexInfo dexInfo) { + protected ProfilePath buildRefProfilePath(@NonNull CheckedSecondaryDexInfo dexInfo) { return AidlUtils.buildProfilePathForSecondaryRef(dexInfo.dexPath()); } @Override @NonNull protected OutputProfile buildOutputProfile( - @NonNull DetailedSecondaryDexInfo dexInfo, boolean isPublic) { + @NonNull CheckedSecondaryDexInfo dexInfo, boolean isPublic) { int uid = getUid(dexInfo); return AidlUtils.buildOutputProfileForSecondary(dexInfo.dexPath(), uid, uid, isPublic); } @Override @NonNull - protected List<ProfilePath> getCurProfiles(@NonNull DetailedSecondaryDexInfo dexInfo) { + protected List<ProfilePath> getCurProfiles(@NonNull CheckedSecondaryDexInfo dexInfo) { // A secondary dex file can only be loaded by one user, so there is only one profile. return List.of(AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath())); } @Override @Nullable - protected DexMetadataPath buildDmPath(@NonNull DetailedSecondaryDexInfo dexInfo) { + protected DexMetadataPath buildDmPath(@NonNull CheckedSecondaryDexInfo dexInfo) { return null; } - private int getUid(@NonNull DetailedSecondaryDexInfo dexInfo) { + private int getUid(@NonNull CheckedSecondaryDexInfo dexInfo) { return dexInfo.userHandle().getUid(mPkgState.getAppId()); } } diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java index 94fbdaccd9..6e78de9127 100644 --- a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java +++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java @@ -18,7 +18,7 @@ package com.android.server.art; import static android.os.ParcelFileDescriptor.AutoCloseInputStream; -import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo; +import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult; import static com.android.server.art.model.DexoptResult.PackageDexoptResult; import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus; @@ -129,8 +129,8 @@ public class ArtManagerLocalTest { @Mock private StorageManager mStorageManager; private PackageState mPkgState1; private AndroidPackage mPkg1; - private DetailedSecondaryDexInfo mPkg1SecondaryDexInfo1; - private DetailedSecondaryDexInfo mPkg1SecondaryDexInfoNotFound; + private CheckedSecondaryDexInfo mPkg1SecondaryDexInfo1; + private CheckedSecondaryDexInfo mPkg1SecondaryDexInfoNotFound; private Config mConfig; // True if the artifacts should be in dalvik-cache. @@ -218,9 +218,15 @@ public class ArtManagerLocalTest { .when(mDexUseManager) .getSecondaryDexInfo(eq(PKG_NAME_1)); lenient() + .doReturn(List.of(mPkg1SecondaryDexInfo1, mPkg1SecondaryDexInfoNotFound)) + .when(mDexUseManager) + .getCheckedSecondaryDexInfo( + eq(PKG_NAME_1), eq(false) /* excludeObsoleteDexesAndLoaders */); + lenient() .doReturn(List.of(mPkg1SecondaryDexInfo1)) .when(mDexUseManager) - .getFilteredDetailedSecondaryDexInfo(eq(PKG_NAME_1)); + .getCheckedSecondaryDexInfo( + eq(PKG_NAME_1), eq(true) /* excludeObsoleteDexesAndLoaders */); simulateStorageNotLow(); @@ -1283,8 +1289,8 @@ public class ArtManagerLocalTest { return getDexoptStatusResult; } - private DetailedSecondaryDexInfo createSecondaryDexInfo(String dexPath) throws Exception { - var dexInfo = mock(DetailedSecondaryDexInfo.class); + private CheckedSecondaryDexInfo createSecondaryDexInfo(String dexPath) throws Exception { + var dexInfo = mock(CheckedSecondaryDexInfo.class); lenient().when(dexInfo.dexPath()).thenReturn(dexPath); lenient().when(dexInfo.abiNames()).thenReturn(Set.of("arm64-v8a")); lenient().when(dexInfo.classLoaderContext()).thenReturn("CLC"); diff --git a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java index c8ece398b9..5662090aeb 100644 --- a/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java +++ b/libartservice/service/javatests/com/android/server/art/DexUseManagerTest.java @@ -16,7 +16,7 @@ package com.android.server.art; -import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo; +import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; import static com.android.server.art.DexUseManagerLocal.DexLoader; import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo; @@ -87,11 +87,11 @@ public class DexUseManagerTest { private final UserHandle mUserHandle = Binder.getCallingUserHandle(); /** - * The default value of `isDexFilePublic` returned by `getSecondaryDexInfo`. The value doesn't + * The default value of `fileVisibility` returned by `getSecondaryDexInfo`. The value doesn't * matter because it's undefined, but it's needed for deep equality check, to make the test * simpler. */ - private final boolean mDefaultIsDexFilePublic = true; + private final @FileVisibility int mDefaultFileVisibility = FileVisibility.OTHER_READABLE; @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot; @Mock private DexUseManagerLocal.Injector mInjector; @@ -325,10 +325,10 @@ public class DexUseManagerTest { List<? extends SecondaryDexInfo> dexInfoList = mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME); assertThat(dexInfoList) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, "CLC", Set.of("arm64-v8a"), Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */)), - false /* isUsedByOtherApps */, mDefaultIsDexFilePublic)); + false /* isUsedByOtherApps */, FileVisibility.OTHER_READABLE)); assertThat(dexInfoList.get(0).classLoaderContext()).isEqualTo("CLC"); } @@ -341,10 +341,10 @@ public class DexUseManagerTest { List<? extends SecondaryDexInfo> dexInfoList = mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME); assertThat(dexInfoList) - .containsExactly(DetailedSecondaryDexInfo.create(mDeDir + "/foo.apk", mUserHandle, + .containsExactly(CheckedSecondaryDexInfo.create(mDeDir + "/foo.apk", mUserHandle, "CLC", Set.of("arm64-v8a"), Set.of(DexLoader.create(OWNING_PKG_NAME, true /* isolatedProcess */)), - true /* isUsedByOtherApps */, mDefaultIsDexFilePublic)); + true /* isUsedByOtherApps */, FileVisibility.OTHER_READABLE)); assertThat(dexInfoList.get(0).classLoaderContext()).isEqualTo("CLC"); } @@ -356,10 +356,10 @@ public class DexUseManagerTest { List<? extends SecondaryDexInfo> dexInfoList = mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME); assertThat(dexInfoList) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, "CLC", Set.of("armeabi-v7a"), Set.of(DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)), - true /* isUsedByOtherApps */, mDefaultIsDexFilePublic)); + true /* isUsedByOtherApps */, FileVisibility.OTHER_READABLE)); assertThat(dexInfoList.get(0).classLoaderContext()).isEqualTo("CLC"); } @@ -371,10 +371,10 @@ public class DexUseManagerTest { List<? extends SecondaryDexInfo> dexInfoList = mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME); assertThat(dexInfoList) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT, Set.of("armeabi-v7a"), Set.of(DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)), - true /* isUsedByOtherApps */, mDefaultIsDexFilePublic)); + true /* isUsedByOtherApps */, FileVisibility.OTHER_READABLE)); assertThat(dexInfoList.get(0).classLoaderContext()).isNull(); } @@ -388,12 +388,12 @@ public class DexUseManagerTest { List<? extends SecondaryDexInfo> dexInfoList = mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME); assertThat(dexInfoList) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, SecondaryDexInfo.VARYING_CLASS_LOADER_CONTEXTS, Set.of("arm64-v8a", "armeabi-v7a"), Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */), DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)), - true /* isUsedByOtherApps */, mDefaultIsDexFilePublic)); + true /* isUsedByOtherApps */, FileVisibility.OTHER_READABLE)); assertThat(dexInfoList.get(0).classLoaderContext()).isNull(); } @@ -489,7 +489,7 @@ public class DexUseManagerTest { List<? extends SecondaryDexInfo> dexInfoList = mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME); assertThat(dexInfoList) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, "UpdatedCLC", Set.of("arm64-v8a", "armeabi-v7a"), Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */), @@ -497,21 +497,21 @@ public class DexUseManagerTest { true /* isolatedProcess */), DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)), - true /* isUsedByOtherApps */, mDefaultIsDexFilePublic), - DetailedSecondaryDexInfo.create(mCeDir + "/bar.apk", mUserHandle, + true /* isUsedByOtherApps */, mDefaultFileVisibility), + CheckedSecondaryDexInfo.create(mCeDir + "/bar.apk", mUserHandle, SecondaryDexInfo.VARYING_CLASS_LOADER_CONTEXTS, Set.of("arm64-v8a", "armeabi-v7a"), Set.of(DexLoader.create( OWNING_PKG_NAME, false /* isolatedProcess */), DexLoader.create( LOADING_PKG_NAME, false /* isolatedProcess */)), - true /* isUsedByOtherApps */, mDefaultIsDexFilePublic), - DetailedSecondaryDexInfo.create(mCeDir + "/baz.apk", mUserHandle, + true /* isUsedByOtherApps */, mDefaultFileVisibility), + CheckedSecondaryDexInfo.create(mCeDir + "/baz.apk", mUserHandle, SecondaryDexInfo.UNSUPPORTED_CLASS_LOADER_CONTEXT, Set.of("arm64-v8a"), Set.of(DexLoader.create( OWNING_PKG_NAME, false /* isolatedProcess */)), - false /* isUsedByOtherApps */, mDefaultIsDexFilePublic)); + false /* isUsedByOtherApps */, mDefaultFileVisibility)); assertThat(mDexUseManager.getSecondaryDexContainerFileUseInfo(OWNING_PKG_NAME)) .containsExactly(DexContainerFileUseInfo.create(mCeDir + "/foo.apk", mUserHandle, @@ -525,7 +525,7 @@ public class DexUseManagerTest { } @Test - public void testFilteredDetailedSecondaryDexPublic() throws Exception { + public void testCheckedSecondaryDexPublic() throws Exception { when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk")) .thenReturn(FileVisibility.OTHER_READABLE); @@ -534,16 +534,17 @@ public class DexUseManagerTest { mDexUseManager.notifyDexContainersLoaded( mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC")); - assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME)) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + assertThat(mDexUseManager.getCheckedSecondaryDexInfo( + OWNING_PKG_NAME, true /* excludeObsoleteDexesAndLoaders */)) + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, "CLC", Set.of("arm64-v8a", "armeabi-v7a"), Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */), DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)), - true /* isUsedByOtherApps */, true /* isDexFilePublic */)); + true /* isUsedByOtherApps */, FileVisibility.OTHER_READABLE)); } @Test - public void testFilteredDetailedSecondaryDexPrivate() throws Exception { + public void testCheckedSecondaryDexPrivate() throws Exception { when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk")) .thenReturn(FileVisibility.NOT_OTHER_READABLE); @@ -556,36 +557,57 @@ public class DexUseManagerTest { mDexUseManager.notifyDexContainersLoaded( mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC")); - assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME)) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + assertThat(mDexUseManager.getCheckedSecondaryDexInfo( + OWNING_PKG_NAME, true /* excludeObsoleteDexesAndLoaders */)) + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, "CLC", Set.of("arm64-v8a"), Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */)), - false /* isUsedByOtherApps */, false /* isDexFilePublic */)); + false /* isUsedByOtherApps */, FileVisibility.NOT_OTHER_READABLE)); + + assertThat(mDexUseManager.getCheckedSecondaryDexInfo( + OWNING_PKG_NAME, false /* excludeObsoleteDexesAndLoaders */)) + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + "CLC", Set.of("arm64-v8a", "armeabi-v7a"), + Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */), + DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */), + DexLoader.create(OWNING_PKG_NAME, true /* isolatedProcess */)), + true /* isUsedByOtherApps */, FileVisibility.NOT_OTHER_READABLE)); } @Test - public void testFilteredDetailedSecondaryDexFilteredDueToVisibility() throws Exception { - when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk")) - .thenReturn(FileVisibility.NOT_OTHER_READABLE); - - mDexUseManager.notifyDexContainersLoaded( - mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC")); + public void testCheckedSecondaryDexNotFound() throws Exception { + when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk")).thenReturn(FileVisibility.NOT_FOUND); - when(Process.isIsolatedUid(anyInt())).thenReturn(true); mDexUseManager.notifyDexContainersLoaded( mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC")); - assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME)).isEmpty(); + assertThat(mDexUseManager.getCheckedSecondaryDexInfo( + OWNING_PKG_NAME, true /* excludeObsoleteDexesAndLoaders */)) + .isEmpty(); + + assertThat(mDexUseManager.getCheckedSecondaryDexInfo( + OWNING_PKG_NAME, false /* excludeObsoleteDexesAndLoaders */)) + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + "CLC", Set.of("arm64-v8a"), + Set.of(DexLoader.create(OWNING_PKG_NAME, false /* isolatedProcess */)), + false /* isUsedByOtherApps */, FileVisibility.NOT_FOUND)); } @Test - public void testFilteredDetailedSecondaryDexFilteredDueToNotFound() throws Exception { - when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk")).thenReturn(FileVisibility.NOT_FOUND); + public void testCheckedSecondaryDexFilteredDueToVisibility() throws Exception { + when(mArtd.getDexFileVisibility(mCeDir + "/foo.apk")) + .thenReturn(FileVisibility.NOT_OTHER_READABLE); mDexUseManager.notifyDexContainersLoaded( + mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC")); + + when(Process.isIsolatedUid(anyInt())).thenReturn(true); + mDexUseManager.notifyDexContainersLoaded( mSnapshot, OWNING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC")); - assertThat(mDexUseManager.getFilteredDetailedSecondaryDexInfo(OWNING_PKG_NAME)).isEmpty(); + assertThat(mDexUseManager.getCheckedSecondaryDexInfo( + OWNING_PKG_NAME, true /* excludeObsoleteDexesAndLoaders */)) + .isEmpty(); } @Test @@ -746,10 +768,10 @@ public class DexUseManagerTest { mSnapshot, LOADING_PKG_NAME, Map.of(mCeDir + "/foo.apk", "CLC")); assertThat(mDexUseManager.getSecondaryDexInfo(OWNING_PKG_NAME)) - .containsExactly(DetailedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, + .containsExactly(CheckedSecondaryDexInfo.create(mCeDir + "/foo.apk", mUserHandle, "CLC", Set.of("armeabi-v7a"), Set.of(DexLoader.create(LOADING_PKG_NAME, false /* isolatedProcess */)), - true /* isUsedByOtherApps */, mDefaultIsDexFilePublic)); + true /* isUsedByOtherApps */, mDefaultFileVisibility)); } private AndroidPackage createPackage(String packageName) { diff --git a/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java b/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java index 694bba7136..be2887d20f 100644 --- a/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java +++ b/libartservice/service/javatests/com/android/server/art/DumpHelperTest.java @@ -16,6 +16,7 @@ package com.android.server.art; +import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; import static com.android.server.art.DexUseManagerLocal.DexLoader; import static com.android.server.art.DexUseManagerLocal.SecondaryDexInfo; import static com.android.server.art.model.DexoptStatus.DexContainerFileDexoptStatus; @@ -68,7 +69,6 @@ public class DumpHelperTest { @Mock private ArtManagerLocal mArtManagerLocal; @Mock private DexUseManagerLocal mDexUseManagerLocal; @Mock private PackageManagerLocal.FilteredSnapshot mSnapshot; - @Mock private IArtd mArtd; private DumpHelper mDumpHelper; @@ -85,7 +85,6 @@ public class DumpHelperTest { lenient().when(mInjector.getArtManagerLocal()).thenReturn(mArtManagerLocal); lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManagerLocal); - lenient().when(mInjector.getArtd()).thenReturn(mArtd); Map<String, PackageState> pkgStates = createPackageStates(); lenient().when(mSnapshot.getPackageStates()).thenReturn(pkgStates); @@ -214,7 +213,7 @@ public class DumpHelperTest { .thenReturn(Set.of(DexLoader.create(PKG_NAME_FOO, false /* isolatedProcess */), DexLoader.create(PKG_NAME_BAR, false /* isolatedProcess */))); - var info1 = mock(SecondaryDexInfo.class); + var info1 = mock(CheckedSecondaryDexInfo.class); lenient().when(info1.dexPath()).thenReturn("/data/user_de/0/foo/1.apk"); lenient() .when(info1.displayClassLoaderContext()) @@ -239,11 +238,9 @@ public class DumpHelperTest { lenient().when(info1.loaders()).thenReturn(loaders); // The output should show the dex path with "(removed)". - lenient() - .when(mArtd.getDexFileVisibility("/data/user_de/0/foo/1.apk")) - .thenReturn(FileVisibility.NOT_FOUND); + lenient().when(info1.fileVisibility()).thenReturn(FileVisibility.NOT_FOUND); - var info2 = mock(SecondaryDexInfo.class); + var info2 = mock(CheckedSecondaryDexInfo.class); lenient().when(info2.dexPath()).thenReturn("/data/user_de/0/foo/2.apk"); lenient().when(info2.displayClassLoaderContext()).thenReturn("PCL[]"); lenient() @@ -255,14 +252,13 @@ public class DumpHelperTest { lenient() .when(info2.loaders()) .thenReturn(Set.of(DexLoader.create(PKG_NAME_FOO, false /* isolatedProcess */))); - lenient() - .when(mArtd.getDexFileVisibility("/data/user_de/0/foo/2.apk")) - .thenReturn(FileVisibility.OTHER_READABLE); + lenient().when(info2.fileVisibility()).thenReturn(FileVisibility.OTHER_READABLE); lenient() .doReturn(List.of(info1, info2)) .when(mDexUseManagerLocal) - .getSecondaryDexInfo(PKG_NAME_FOO); + .getCheckedSecondaryDexInfo( + PKG_NAME_FOO, false /* excludeObsoleteDexesAndLoaders */); } private void setUpForBar() { diff --git a/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java index 3ef256c08d..f799b7073f 100644 --- a/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java +++ b/libartservice/service/javatests/com/android/server/art/SecondaryDexopterTest.java @@ -16,7 +16,7 @@ package com.android.server.art; -import static com.android.server.art.DexUseManagerLocal.DetailedSecondaryDexInfo; +import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; import static com.android.server.art.OutputArtifacts.PermissionSettings; import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult; import static com.android.server.art.testing.TestingUtils.deepEq; @@ -130,9 +130,10 @@ public class SecondaryDexopterTest { lenient().when(mInjector.isLauncherPackage(any())).thenReturn(false); lenient().when(mInjector.getDexUseManager()).thenReturn(mDexUseManager); - List<DetailedSecondaryDexInfo> secondaryDexInfo = createSecondaryDexInfo(); + List<CheckedSecondaryDexInfo> secondaryDexInfo = createSecondaryDexInfo(); lenient() - .when(mDexUseManager.getFilteredDetailedSecondaryDexInfo(eq(PKG_NAME))) + .when(mDexUseManager.getCheckedSecondaryDexInfo( + eq(PKG_NAME), eq(true) /* excludeObsoleteDexesAndLoaders */)) .thenReturn(secondaryDexInfo); mPkgState = createPackageState(); @@ -235,33 +236,33 @@ public class SecondaryDexopterTest { return pkgState; } - private List<DetailedSecondaryDexInfo> createSecondaryDexInfo() throws Exception { + private List<CheckedSecondaryDexInfo> createSecondaryDexInfo() throws Exception { // This should be compiled with profile. - var dex1Info = mock(DetailedSecondaryDexInfo.class); + var dex1Info = mock(CheckedSecondaryDexInfo.class); lenient().when(dex1Info.dexPath()).thenReturn(DEX_1); lenient().when(dex1Info.userHandle()).thenReturn(USER_HANDLE); lenient().when(dex1Info.classLoaderContext()).thenReturn("CLC_FOR_DEX_1"); lenient().when(dex1Info.abiNames()).thenReturn(Set.of("arm64-v8a")); lenient().when(dex1Info.isUsedByOtherApps()).thenReturn(false); - lenient().when(dex1Info.isDexFilePublic()).thenReturn(true); + lenient().when(dex1Info.fileVisibility()).thenReturn(FileVisibility.OTHER_READABLE); // This should be compiled without profile because it's used by other apps. - var dex2Info = mock(DetailedSecondaryDexInfo.class); + var dex2Info = mock(CheckedSecondaryDexInfo.class); lenient().when(dex2Info.dexPath()).thenReturn(DEX_2); lenient().when(dex2Info.userHandle()).thenReturn(USER_HANDLE); lenient().when(dex2Info.classLoaderContext()).thenReturn("CLC_FOR_DEX_2"); lenient().when(dex2Info.abiNames()).thenReturn(Set.of("arm64-v8a", "armeabi-v7a")); lenient().when(dex2Info.isUsedByOtherApps()).thenReturn(true); - lenient().when(dex2Info.isDexFilePublic()).thenReturn(true); + lenient().when(dex2Info.fileVisibility()).thenReturn(FileVisibility.OTHER_READABLE); // This should be compiled with verify because the class loader context is invalid. - var dex3Info = mock(DetailedSecondaryDexInfo.class); + var dex3Info = mock(CheckedSecondaryDexInfo.class); lenient().when(dex3Info.dexPath()).thenReturn(DEX_3); lenient().when(dex3Info.userHandle()).thenReturn(USER_HANDLE); lenient().when(dex3Info.classLoaderContext()).thenReturn(null); lenient().when(dex3Info.abiNames()).thenReturn(Set.of("arm64-v8a")); lenient().when(dex3Info.isUsedByOtherApps()).thenReturn(false); - lenient().when(dex3Info.isDexFilePublic()).thenReturn(false); + lenient().when(dex3Info.fileVisibility()).thenReturn(FileVisibility.NOT_OTHER_READABLE); return List.of(dex1Info, dex2Info, dex3Info); } |