Don't copy reference profile beforehand.
It's better to copy the reference profile during the merge. If the
reference profile is copied beforehand, it's hard to make the remaining
logic fluent.
Bug: 248318911
Test: atest ArtServiceTests
Ignore-AOSP-First: ART Services.
Change-Id: Ib961944286be946ae1cd9a503faf12d9e14afd14
diff --git a/artd/artd.cc b/artd/artd.cc
index 8e55194..20dbe16 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -90,11 +90,9 @@
using ::android::base::Error;
using ::android::base::Join;
using ::android::base::make_scope_guard;
-using ::android::base::ReadFileToString;
using ::android::base::Result;
using ::android::base::Split;
using ::android::base::StringReplace;
-using ::android::base::WriteStringToFd;
using ::art::tools::CmdlineBuilder;
using ::ndk::ScopedAStatus;
@@ -422,29 +420,6 @@
return ScopedAStatus::ok();
}
-ndk::ScopedAStatus Artd::copyProfile(const ProfilePath& in_src, OutputProfile* in_dst) {
- std::string src_path = OR_RETURN_FATAL(BuildProfileOrDmPath(in_src));
- if (in_src.getTag() == ProfilePath::dexMetadataPath) {
- return Fatal("Does not support DM file, got '{}'"_format(src_path));
- }
- std::string dst_path = OR_RETURN_FATAL(BuildRefProfilePath(in_dst->profilePath.refProfilePath));
-
- std::string content;
- if (!ReadFileToString(src_path, &content)) {
- return NonFatal("Failed to read file '{}': {}"_format(src_path, strerror(errno)));
- }
-
- std::unique_ptr<NewFile> dst =
- OR_RETURN_NON_FATAL(NewFile::Create(dst_path, in_dst->fsPermission));
- if (!WriteStringToFd(content, dst->Fd())) {
- return NonFatal("Failed to write file '{}': {}"_format(dst_path, strerror(errno)));
- }
-
- OR_RETURN_NON_FATAL(dst->Keep());
- in_dst->profilePath.id = dst->TempId();
- return ScopedAStatus::ok();
-}
-
ndk::ScopedAStatus Artd::copyAndRewriteProfile(const ProfilePath& in_src,
OutputProfile* in_dst,
const std::string& in_dexFile,
diff --git a/artd/artd.h b/artd/artd.h
index d2952a2..dd4b022 100644
--- a/artd/artd.h
+++ b/artd/artd.h
@@ -88,9 +88,6 @@
const std::string& in_dexFile,
bool* _aidl_return) override;
- ndk::ScopedAStatus copyProfile(const aidl::com::android::server::art::ProfilePath& in_src,
- ::aidl::com::android::server::art::OutputProfile* in_dst) override;
-
ndk::ScopedAStatus copyAndRewriteProfile(
const aidl::com::android::server::art::ProfilePath& in_src,
aidl::com::android::server::art::OutputProfile* in_dst,
diff --git a/artd/artd_test.cc b/artd/artd_test.cc
index f39e2cd..63d13c2 100644
--- a/artd/artd_test.cc
+++ b/artd/artd_test.cc
@@ -979,32 +979,6 @@
EXPECT_THAT(status.getMessage(), HasSubstr("profman returned an unexpected code: 100"));
}
-TEST_F(ArtdTest, copyProfile) {
- const TmpRefProfilePath& src = profile_path_->get<ProfilePath::tmpRefProfilePath>();
- std::string src_file = OR_FATAL(BuildTmpRefProfilePath(src));
- CreateFile(src_file, "abc");
- OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
- dst.profilePath.id = "";
-
- EXPECT_TRUE(artd_->copyProfile(src, &dst).isOk());
-
- EXPECT_THAT(dst.profilePath.id, Not(IsEmpty()));
- CheckContent(OR_FATAL(BuildTmpRefProfilePath(dst.profilePath)), "abc");
-}
-
-TEST_F(ArtdTest, copyProfileFailed) {
- const TmpRefProfilePath& src = profile_path_->get<ProfilePath::tmpRefProfilePath>();
- OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
- dst.profilePath.id = "";
-
- ndk::ScopedAStatus status = artd_->copyProfile(src, &dst);
-
- EXPECT_FALSE(status.isOk());
- EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
- EXPECT_THAT(status.getMessage(),
- ContainsRegex(R"re(Failed to read file .*primary\.prof\.12345\.tmp)re"));
-}
-
TEST_F(ArtdTest, copyAndRewriteProfile) {
const TmpRefProfilePath& src = profile_path_->get<ProfilePath::tmpRefProfilePath>();
std::string src_file = OR_FATAL(BuildTmpRefProfilePath(src));
diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl
index 04ab0b5..04de116 100644
--- a/artd/binder/com/android/server/art/IArtd.aidl
+++ b/artd/binder/com/android/server/art/IArtd.aidl
@@ -46,16 +46,6 @@
@utf8InCpp String dexFile);
/**
- * Copies the profile. Throws if `src` does not exist. Fills `dst.profilePath.id` on success.
- *
- * Does not operate on a DM file.
- *
- * Throws fatal and non-fatal errors.
- */
- void copyProfile(in com.android.server.art.ProfilePath src,
- inout com.android.server.art.OutputProfile dst);
-
- /**
* Copies the profile and rewrites it for the given dex file. Returns true and fills
* `dst.profilePath.id` if the operation succeeds and `src` exists and contains entries that
* match the given dex file.
diff --git a/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java b/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java
index f30fe0b..fa2af7a 100644
--- a/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java
+++ b/libartservice/service/java/com/android/server/art/PrimaryDexOptimizer.java
@@ -38,6 +38,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.art.model.ArtFlags;
@@ -99,7 +100,7 @@
boolean isInDalvikCache = Utils.isInDalvikCache(pkgState);
for (DetailedPrimaryDexInfo dexInfo : PrimaryDexUtils.getDetailedDexInfo(pkgState, pkg)) {
- OutputProfile profile = null;
+ ProfilePath profile = null;
boolean succeeded = true;
try {
if (!dexInfo.hasCode()) {
@@ -112,13 +113,19 @@
boolean needsToBeShared = isSharedLibrary(pkg)
|| mInjector.isUsedByOtherApps(pkgState.getPackageName());
+ boolean isOtherReadable = true;
// If true, implies that the profile has changed since the last compilation.
boolean profileMerged = false;
if (DexFile.isProfileGuidedCompilerFilter(compilerFilter)) {
if (needsToBeShared) {
profile = initReferenceProfile(pkgState, dexInfo, uid, sharedGid);
} else {
- profile = copyOrInitReferenceProfile(pkgState, dexInfo, uid, sharedGid);
+ Pair<ProfilePath, Boolean> pair =
+ getOrInitReferenceProfile(pkgState, dexInfo, uid, sharedGid);
+ if (pair != null) {
+ profile = pair.first;
+ isOtherReadable = pair.second;
+ }
// TODO(jiakaiz): Merge profiles.
}
if (profile == null) {
@@ -135,8 +142,7 @@
DexFile.isProfileGuidedCompilerFilter(compilerFilter);
assert isProfileGuidedCompilerFilter == (profile != null);
- boolean canBePublic =
- !isProfileGuidedCompilerFilter || profile.fsPermission.isOtherReadable;
+ boolean canBePublic = !isProfileGuidedCompilerFilter || isOtherReadable;
assert Utils.implies(needsToBeShared, canBePublic);
PermissionSettings permissionSettings =
getPermissionSettings(sharedGid, canBePublic);
@@ -169,10 +175,6 @@
continue;
}
- ProfilePath inputProfile = profile != null
- ? ProfilePath.tmpRefProfilePath(profile.profilePath)
- : null;
-
IArtdCancellationSignal artdCancellationSignal =
mInjector.getArtd().createCancellationSignal();
cancellationSignal.setOnCancelListener(() -> {
@@ -184,7 +186,7 @@
}
});
- DexoptResult dexoptResult = dexoptFile(target, inputProfile,
+ DexoptResult dexoptResult = dexoptFile(target, profile,
getDexoptNeededResult, permissionSettings,
params.getPriorityClass(), dexoptOptions, artdCancellationSignal);
status = dexoptResult.cancelled ? OptimizeResult.OPTIMIZE_CANCELLED
@@ -219,16 +221,17 @@
}
if (profile != null && succeeded) {
- // Commit the profile only if dexopt succeeds.
- if (commitProfileChanges(profile)) {
- profile = null;
+ if (profile.getTag() == ProfilePath.tmpRefProfilePath) {
+ // Commit the profile only if dexopt succeeds.
+ if (commitProfileChanges(profile.getTmpRefProfilePath())) {
+ profile = null;
+ }
}
// TODO(jiakaiz): If profileMerged is true, clear current profiles.
}
} finally {
- if (profile != null) {
- mInjector.getArtd().deleteProfile(
- ProfilePath.tmpRefProfilePath(profile.profilePath));
+ if (profile != null && profile.getTag() == ProfilePath.tmpRefProfilePath) {
+ mInjector.getArtd().deleteProfile(profile);
}
}
}
@@ -283,7 +286,7 @@
* null otherwise.
*/
@Nullable
- private OutputProfile initReferenceProfile(@NonNull PackageState pkgState,
+ private ProfilePath initReferenceProfile(@NonNull PackageState pkgState,
@NonNull DetailedPrimaryDexInfo dexInfo, int uid, int gid) throws RemoteException {
String profileName = getProfileName(dexInfo.splitName());
OutputProfile output = AidlUtils.buildOutputProfile(
@@ -298,7 +301,7 @@
// case is necessary.
if (mInjector.getArtd().copyAndRewriteProfile(
prebuiltProfile, output, dexInfo.dexPath())) {
- return output;
+ return ProfilePath.tmpRefProfilePath(output.profilePath);
}
} catch (ServiceSpecificException e) {
Log.e(TAG,
@@ -311,7 +314,7 @@
ProfilePath dmProfile = AidlUtils.buildProfilePathForDm(dexInfo.dexPath());
try {
if (mInjector.getArtd().copyAndRewriteProfile(dmProfile, output, dexInfo.dexPath())) {
- return output;
+ return ProfilePath.tmpRefProfilePath(output.profilePath);
}
} catch (ServiceSpecificException e) {
Log.e(TAG,
@@ -325,11 +328,15 @@
}
/**
- * Copies the existing reference profile if exists, or initializes a reference profile
- * otherwise.
+ * Gets the existing reference profile if exists, or initializes a reference profile from an
+ * external profile.
+ *
+ * @return A pair where the first element is the found or initialized profile, and the second
+ * element is true if the profile is readable by others. Or null if there is no
+ * reference profile or external profile to use.
*/
@Nullable
- private OutputProfile copyOrInitReferenceProfile(@NonNull PackageState pkgState,
+ private Pair<ProfilePath, Boolean> getOrInitReferenceProfile(@NonNull PackageState pkgState,
@NonNull DetailedPrimaryDexInfo dexInfo, int uid, int gid) throws RemoteException {
String profileName = getProfileName(dexInfo.splitName());
ProfilePath refProfile =
@@ -338,10 +345,7 @@
if (mInjector.getArtd().isProfileUsable(refProfile, dexInfo.dexPath())) {
boolean isOtherReadable = mInjector.getArtd().getProfileVisibility(refProfile)
== FileVisibility.OTHER_READABLE;
- OutputProfile output = AidlUtils.buildOutputProfile(pkgState.getPackageName(),
- getProfileName(dexInfo.splitName()), uid, gid, isOtherReadable);
- mInjector.getArtd().copyProfile(refProfile, output);
- return output;
+ return Pair.create(refProfile, isOtherReadable);
}
} catch (ServiceSpecificException e) {
Log.e(TAG,
@@ -351,7 +355,8 @@
e);
}
- return initReferenceProfile(pkgState, dexInfo, uid, gid);
+ ProfilePath initializedProfile = initReferenceProfile(pkgState, dexInfo, uid, gid);
+ return initializedProfile != null ? Pair.create(initializedProfile, true) : null;
}
@NonNull
@@ -498,12 +503,12 @@
}
}
- boolean commitProfileChanges(@NonNull OutputProfile profile) throws RemoteException {
+ boolean commitProfileChanges(@NonNull TmpRefProfilePath profile) throws RemoteException {
try {
- mInjector.getArtd().commitTmpProfile(profile.profilePath);
+ mInjector.getArtd().commitTmpProfile(profile);
return true;
} catch (ServiceSpecificException e) {
- RefProfilePath refProfilePath = profile.profilePath.refProfilePath;
+ RefProfilePath refProfilePath = profile.refProfilePath;
Log.e(TAG,
String.format(
"Failed to commit profile changes [packageName = %s, profileName = %s]",
diff --git a/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java b/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java
index 8eb0bfe..7cce870 100644
--- a/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java
+++ b/libartservice/service/javatests/com/android/server/art/PrimaryDexOptimizerTest.java
@@ -78,8 +78,6 @@
private final String mSplit0DexPath = "/data/app/foo/split_0.apk";
private final ProfilePath mSplit0RefProfile =
AidlUtils.buildProfilePathForRef(PKG_NAME, "split_0.split");
- private final OutputProfile mSplit0PrivateOutputProfile = AidlUtils.buildOutputProfile(
- PKG_NAME, "split_0.split", UID, SHARED_GID, false /* isOtherReadable */);
private final int mDefaultDexoptTrigger = DexoptTrigger.COMPILER_FILTER_IS_BETTER
| DexoptTrigger.PRIMARY_BOOT_IMAGE_BECOMES_USABLE;
@@ -175,30 +173,22 @@
mPrimaryDexOptimizer.dexopt(mPkgState, mPkg, mOptimizeParams, mCancellationSignal);
- InOrder inOrder = inOrder(mArtd);
-
- inOrder.verify(mArtd).copyProfile(deepEq(mRefProfile), deepEq(mPrivateOutputProfile));
-
- inOrder.verify(mArtd).getDexoptNeeded(
+ verify(mArtd).getDexoptNeeded(
eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
- checkDexoptWithPrivateProfile(
- inOrder.verify(mArtd), mDexPath, "arm64", mPrivateOutputProfile);
+ checkDexoptWithPrivateProfile(verify(mArtd), mDexPath, "arm64", mRefProfile);
- inOrder.verify(mArtd).getDexoptNeeded(
+ verify(mArtd).getDexoptNeeded(
eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mDefaultDexoptTrigger));
- checkDexoptWithPrivateProfile(
- inOrder.verify(mArtd), mDexPath, "arm", mPrivateOutputProfile);
-
- inOrder.verify(mArtd).commitTmpProfile(deepEq(mPrivateOutputProfile.profilePath));
+ checkDexoptWithPrivateProfile(verify(mArtd), mDexPath, "arm", mRefProfile);
// There is no profile for split 0, so it should fall back to "verify".
- inOrder.verify(mArtd).getDexoptNeeded(
+ verify(mArtd).getDexoptNeeded(
eq(mSplit0DexPath), eq("arm64"), any(), eq("verify"), eq(mDefaultDexoptTrigger));
- checkDexoptWithNoProfile(inOrder.verify(mArtd), mSplit0DexPath, "arm64", "verify");
+ checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "verify");
- inOrder.verify(mArtd).getDexoptNeeded(
+ verify(mArtd).getDexoptNeeded(
eq(mSplit0DexPath), eq("arm"), any(), eq("verify"), eq(mDefaultDexoptTrigger));
- checkDexoptWithNoProfile(inOrder.verify(mArtd), mSplit0DexPath, "arm", "verify");
+ checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "verify");
verifyProfileNotUsed(mPrebuiltProfile);
verifyProfileNotUsed(mDmProfile);
@@ -217,10 +207,8 @@
mPrimaryDexOptimizer.dexopt(mPkgState, mPkg, mOptimizeParams, mCancellationSignal);
- verify(mArtd).copyProfile(deepEq(mRefProfile), deepEq(mPublicOutputProfile));
-
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm64", mPublicOutputProfile);
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm", mPublicOutputProfile);
+ checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm64", mRefProfile);
+ checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm", mRefProfile);
verifyProfileNotUsed(mPrebuiltProfile);
verifyProfileNotUsed(mDmProfile);
@@ -234,11 +222,17 @@
mPrimaryDexOptimizer.dexopt(mPkgState, mPkg, mOptimizeParams, mCancellationSignal);
- verify(mArtd).copyAndRewriteProfile(
+ InOrder inOrder = inOrder(mArtd);
+
+ inOrder.verify(mArtd).copyAndRewriteProfile(
deepEq(mPrebuiltProfile), deepEq(mPublicOutputProfile), eq(mDexPath));
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm64", mPublicOutputProfile);
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm", mPublicOutputProfile);
+ checkDexoptWithPublicProfile(inOrder.verify(mArtd), mDexPath, "arm64",
+ ProfilePath.tmpRefProfilePath(mPublicOutputProfile.profilePath));
+ checkDexoptWithPublicProfile(inOrder.verify(mArtd), mDexPath, "arm",
+ ProfilePath.tmpRefProfilePath(mPublicOutputProfile.profilePath));
+
+ inOrder.verify(mArtd).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath));
verifyProfileNotUsed(mRefProfile);
verifyProfileNotUsed(mDmProfile);
@@ -255,8 +249,10 @@
verify(mArtd).copyAndRewriteProfile(
deepEq(mDmProfile), deepEq(mPublicOutputProfile), eq(mDexPath));
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm64", mPublicOutputProfile);
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm", mPublicOutputProfile);
+ checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm64",
+ ProfilePath.tmpRefProfilePath(mPublicOutputProfile.profilePath));
+ checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm",
+ ProfilePath.tmpRefProfilePath(mPublicOutputProfile.profilePath));
verifyProfileNotUsed(mRefProfile);
verifyProfileNotUsed(mPrebuiltProfile);
@@ -264,9 +260,9 @@
@Test
public void testDexoptDeletesProfileOnFailure() throws Exception {
- makeProfileUsable(mRefProfile);
- when(mArtd.getProfileVisibility(deepEq(mRefProfile)))
- .thenReturn(FileVisibility.NOT_OTHER_READABLE);
+ makeProfileNotUsable(mRefProfile);
+ makeProfileNotUsable(mPrebuiltProfile);
+ makeProfileUsable(mDmProfile);
when(mArtd.dexopt(any(), eq(mDexPath), any(), any(), any(), any(), any(), anyInt(), any(),
any()))
@@ -275,8 +271,8 @@
mPrimaryDexOptimizer.dexopt(mPkgState, mPkg, mOptimizeParams, mCancellationSignal);
verify(mArtd).deleteProfile(
- deepEq(ProfilePath.tmpRefProfilePath(mPrivateOutputProfile.profilePath)));
- verify(mArtd, never()).commitTmpProfile(deepEq(mPrivateOutputProfile.profilePath));
+ deepEq(ProfilePath.tmpRefProfilePath(mPublicOutputProfile.profilePath)));
+ verify(mArtd, never()).commitTmpProfile(deepEq(mPublicOutputProfile.profilePath));
}
@Test
@@ -302,11 +298,13 @@
// It should re-compile anyway.
verify(mArtd).getDexoptNeeded(
eq(mDexPath), eq("arm64"), any(), eq("speed-profile"), eq(mForceDexoptTrigger));
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm64", mPublicOutputProfile);
+ checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm64",
+ ProfilePath.tmpRefProfilePath(mPublicOutputProfile.profilePath));
verify(mArtd).getDexoptNeeded(
eq(mDexPath), eq("arm"), any(), eq("speed-profile"), eq(mForceDexoptTrigger));
- checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm", mPublicOutputProfile);
+ checkDexoptWithPublicProfile(verify(mArtd), mDexPath, "arm",
+ ProfilePath.tmpRefProfilePath(mPublicOutputProfile.profilePath));
checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm64", "speed");
checkDexoptWithNoProfile(verify(mArtd), mSplit0DexPath, "arm", "speed");
@@ -343,17 +341,13 @@
mPrimaryDexOptimizer.dexopt(mPkgState, mPkg, mOptimizeParams, mCancellationSignal);
- verify(mArtd).copyProfile(deepEq(mSplit0RefProfile), deepEq(mSplit0PrivateOutputProfile));
-
verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm64"), any(), eq("speed-profile"),
eq(mDefaultDexoptTrigger));
- checkDexoptWithPrivateProfile(
- verify(mArtd), mSplit0DexPath, "arm64", mSplit0PrivateOutputProfile);
+ checkDexoptWithPrivateProfile(verify(mArtd), mSplit0DexPath, "arm64", mSplit0RefProfile);
verify(mArtd).getDexoptNeeded(eq(mSplit0DexPath), eq("arm"), any(), eq("speed-profile"),
eq(mDefaultDexoptTrigger));
- checkDexoptWithPrivateProfile(
- verify(mArtd), mSplit0DexPath, "arm", mSplit0PrivateOutputProfile);
+ checkDexoptWithPrivateProfile(verify(mArtd), mSplit0DexPath, "arm", mSplit0RefProfile);
}
@Test
@@ -435,22 +429,20 @@
}
private void checkDexoptWithPublicProfile(
- IArtd artd, String dexPath, String isa, OutputProfile profile) throws Exception {
+ IArtd artd, String dexPath, String isa, ProfilePath profile) throws Exception {
artd.dexopt(
argThat(artifacts
-> artifacts.permissionSettings.fileFsPermission.isOtherReadable == true),
- eq(dexPath), eq(isa), any(), eq("speed-profile"),
- deepEq(ProfilePath.tmpRefProfilePath(profile.profilePath)), any(), anyInt(),
+ eq(dexPath), eq(isa), any(), eq("speed-profile"), deepEq(profile), any(), anyInt(),
argThat(dexoptOptions -> dexoptOptions.generateAppImage == true), any());
}
private void checkDexoptWithPrivateProfile(
- IArtd artd, String dexPath, String isa, OutputProfile profile) throws Exception {
+ IArtd artd, String dexPath, String isa, ProfilePath profile) throws Exception {
artd.dexopt(
argThat(artifacts
-> artifacts.permissionSettings.fileFsPermission.isOtherReadable == false),
- eq(dexPath), eq(isa), any(), eq("speed-profile"),
- deepEq(ProfilePath.tmpRefProfilePath(profile.profilePath)), any(), anyInt(),
+ eq(dexPath), eq(isa), any(), eq("speed-profile"), deepEq(profile), any(), anyInt(),
argThat(dexoptOptions -> dexoptOptions.generateAppImage == true), any());
}