1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.art; 18 19 import static com.android.server.art.DexUseManagerLocal.CheckedSecondaryDexInfo; 20 import static com.android.server.art.OutputArtifacts.PermissionSettings; 21 import static com.android.server.art.OutputArtifacts.PermissionSettings.SeContext; 22 import static com.android.server.art.Utils.Abi; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.content.Context; 27 import android.os.Build; 28 import android.os.CancellationSignal; 29 30 import androidx.annotation.RequiresApi; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.server.art.model.Config; 34 import com.android.server.art.model.DexoptParams; 35 import com.android.server.pm.pkg.AndroidPackage; 36 import com.android.server.pm.pkg.PackageState; 37 38 import java.util.List; 39 import java.util.concurrent.Executor; 40 41 /** @hide */ 42 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 43 public class SecondaryDexopter extends Dexopter<CheckedSecondaryDexInfo> { SecondaryDexopter(@onNull Context context, @NonNull Config config, Executor reporterExecutor, @NonNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal)44 public SecondaryDexopter(@NonNull Context context, @NonNull Config config, 45 Executor reporterExecutor, @NonNull PackageState pkgState, @NonNull AndroidPackage pkg, 46 @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal) { 47 this(new Injector(context, config, reporterExecutor), pkgState, pkg, params, 48 cancellationSignal); 49 } 50 51 @VisibleForTesting SecondaryDexopter(@onNull Injector injector, @NonNull PackageState pkgState, @NonNull AndroidPackage pkg, @NonNull DexoptParams params, @NonNull CancellationSignal cancellationSignal)52 public SecondaryDexopter(@NonNull Injector injector, @NonNull PackageState pkgState, 53 @NonNull AndroidPackage pkg, @NonNull DexoptParams params, 54 @NonNull CancellationSignal cancellationSignal) { 55 super(injector, pkgState, pkg, params, cancellationSignal); 56 if (pkgState.getAppId() < 0) { 57 throw new IllegalStateException( 58 "Package '" + pkgState.getPackageName() + "' has invalid app ID"); 59 } 60 } 61 62 @Override isInDalvikCache()63 protected boolean isInDalvikCache() { 64 // A secondary dex file is added by the app, so it's always in a writable location and hence 65 // never uses dalvik-cache. 66 return false; 67 } 68 69 @Override 70 @NonNull getDexInfoList()71 protected List<CheckedSecondaryDexInfo> getDexInfoList() { 72 return mInjector.getDexUseManager().getCheckedSecondaryDexInfo( 73 mPkgState.getPackageName(), true /* excludeObsoleteDexesAndLoaders */); 74 } 75 76 @Override isDexoptable(@onNull CheckedSecondaryDexInfo dexInfo)77 protected boolean isDexoptable(@NonNull CheckedSecondaryDexInfo dexInfo) { 78 return true; 79 } 80 81 @Override needsToBeShared(@onNull CheckedSecondaryDexInfo dexInfo)82 protected boolean needsToBeShared(@NonNull CheckedSecondaryDexInfo dexInfo) { 83 return dexInfo.isUsedByOtherApps(); 84 } 85 86 @Override isDexFilePublic(@onNull CheckedSecondaryDexInfo dexInfo)87 protected boolean isDexFilePublic(@NonNull CheckedSecondaryDexInfo dexInfo) { 88 return dexInfo.fileVisibility() == FileVisibility.OTHER_READABLE; 89 } 90 91 @Override isDexFileFound(@onNull CheckedSecondaryDexInfo dexInfo)92 protected boolean isDexFileFound(@NonNull CheckedSecondaryDexInfo dexInfo) { 93 // `getDexInfoList` has already excluded non-existing dex files. 94 return true; 95 } 96 97 @Override 98 @NonNull getExternalProfiles(@onNull CheckedSecondaryDexInfo dexInfo)99 protected List<ProfilePath> getExternalProfiles(@NonNull CheckedSecondaryDexInfo dexInfo) { 100 // A secondary dex file doesn't have any external profile to use. 101 return List.of(); 102 } 103 104 @Override 105 @NonNull getPermissionSettings( @onNull CheckedSecondaryDexInfo dexInfo, boolean canBePublic)106 protected PermissionSettings getPermissionSettings( 107 @NonNull CheckedSecondaryDexInfo dexInfo, boolean canBePublic) { 108 int uid = getUid(dexInfo); 109 // We need the "execute" bit for "others" even though `canBePublic` is false because the 110 // directory can contain other artifacts that needs to be public. 111 // We don't need the "read" bit for "others" on the directories because others only need to 112 // access the files in the directories, but they don't need to "ls" the directories. 113 FsPermission dirFsPermission = AidlUtils.buildFsPermission(uid /* uid */, uid /* gid */, 114 false /* isOtherReadable */, true /* isOtherExecutable */); 115 FsPermission fileFsPermission = 116 AidlUtils.buildFsPermission(uid /* uid */, uid /* gid */, canBePublic); 117 SeContext seContext = AidlUtils.buildSeContext(mPkgState.getSeInfo(), uid); 118 return AidlUtils.buildPermissionSettings(dirFsPermission, fileFsPermission, seContext); 119 } 120 121 @Override 122 @NonNull getAllAbis(@onNull CheckedSecondaryDexInfo dexInfo)123 protected List<Abi> getAllAbis(@NonNull CheckedSecondaryDexInfo dexInfo) { 124 return Utils.getAllAbisForNames(dexInfo.abiNames(), mPkgState); 125 } 126 127 @Override 128 @NonNull buildRefProfilePathAsInput(@onNull CheckedSecondaryDexInfo dexInfo)129 protected ProfilePath buildRefProfilePathAsInput(@NonNull CheckedSecondaryDexInfo dexInfo) { 130 return AidlUtils.buildProfilePathForSecondaryRefAsInput(dexInfo.dexPath()); 131 } 132 133 @Override 134 @NonNull buildOutputProfile( @onNull CheckedSecondaryDexInfo dexInfo, boolean isPublic)135 protected OutputProfile buildOutputProfile( 136 @NonNull CheckedSecondaryDexInfo dexInfo, boolean isPublic) { 137 int uid = getUid(dexInfo); 138 return AidlUtils.buildOutputProfileForSecondary( 139 dexInfo.dexPath(), uid, uid, isPublic, mInjector.isPreReboot()); 140 } 141 142 @Override 143 @NonNull getCurProfiles(@onNull CheckedSecondaryDexInfo dexInfo)144 protected List<ProfilePath> getCurProfiles(@NonNull CheckedSecondaryDexInfo dexInfo) { 145 // A secondary dex file can only be loaded by one user, so there is only one profile. 146 return List.of(AidlUtils.buildProfilePathForSecondaryCur(dexInfo.dexPath())); 147 } 148 149 @Override 150 @Nullable buildDmPath(@onNull CheckedSecondaryDexInfo dexInfo)151 protected DexMetadataPath buildDmPath(@NonNull CheckedSecondaryDexInfo dexInfo) { 152 return null; 153 } 154 getUid(@onNull CheckedSecondaryDexInfo dexInfo)155 private int getUid(@NonNull CheckedSecondaryDexInfo dexInfo) { 156 return dexInfo.userHandle().getUid(mPkgState.getAppId()); 157 } 158 } 159