1 /*
2  * Copyright (C) 2020 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.pm.parsing.pkg;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.PackageInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.SharedLibraryInfo;
25 import android.content.pm.VersionedPackage;
26 import android.content.pm.dex.DexMetadataHelper;
27 import android.content.pm.parsing.result.ParseResult;
28 import android.content.pm.parsing.result.ParseTypeImpl;
29 import android.os.incremental.IncrementalManager;
30 
31 import com.android.internal.content.NativeLibraryHelper;
32 import com.android.internal.pm.parsing.PackageParserException;
33 import com.android.internal.pm.parsing.pkg.AndroidPackageHidden;
34 import com.android.internal.pm.parsing.pkg.PackageImpl;
35 import com.android.internal.pm.pkg.component.ParsedActivity;
36 import com.android.internal.pm.pkg.component.ParsedInstrumentation;
37 import com.android.internal.pm.pkg.component.ParsedProvider;
38 import com.android.internal.pm.pkg.component.ParsedService;
39 import com.android.internal.pm.pkg.parsing.ParsingPackageHidden;
40 import com.android.internal.util.ArrayUtils;
41 import com.android.server.SystemConfig;
42 import com.android.server.pm.pkg.AndroidPackage;
43 import com.android.server.pm.pkg.PackageState;
44 import com.android.server.pm.pkg.PackageStateInternal;
45 
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.Collection;
49 import java.util.Collections;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Objects;
53 
54 /** @hide */
55 public class AndroidPackageUtils {
56 
AndroidPackageUtils()57     private AndroidPackageUtils() {
58     }
59 
getAllCodePathsExcludingResourceOnly( AndroidPackage aPkg)60     public static List<String> getAllCodePathsExcludingResourceOnly(
61             AndroidPackage aPkg) {
62         PackageImpl pkg = (PackageImpl) aPkg;
63         ArrayList<String> paths = new ArrayList<>();
64         if (pkg.isDeclaredHavingCode()) {
65             paths.add(pkg.getBaseApkPath());
66         }
67         String[] splitCodePaths = pkg.getSplitCodePaths();
68         if (!ArrayUtils.isEmpty(splitCodePaths)) {
69             for (int i = 0; i < splitCodePaths.length; i++) {
70                 if ((pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
71                     paths.add(splitCodePaths[i]);
72                 }
73             }
74         }
75         return paths;
76     }
77 
78     /**
79      * @return a list of the base and split code paths.
80      */
getAllCodePaths(AndroidPackage aPkg)81     public static List<String> getAllCodePaths(AndroidPackage aPkg) {
82         PackageImpl pkg = (PackageImpl) aPkg;
83         ArrayList<String> paths = new ArrayList<>();
84         paths.add(pkg.getBaseApkPath());
85 
86         String[] splitCodePaths = pkg.getSplitCodePaths();
87         if (!ArrayUtils.isEmpty(splitCodePaths)) {
88             Collections.addAll(paths, splitCodePaths);
89         }
90         return paths;
91     }
92 
createSharedLibraryForSdk(AndroidPackage pkg)93     public static SharedLibraryInfo createSharedLibraryForSdk(AndroidPackage pkg) {
94         return new SharedLibraryInfo(null, pkg.getPackageName(),
95                 AndroidPackageUtils.getAllCodePaths(pkg),
96                 pkg.getSdkLibraryName(),
97                 pkg.getSdkLibVersionMajor(),
98                 SharedLibraryInfo.TYPE_SDK_PACKAGE,
99                 new VersionedPackage(pkg.getManifestPackageName(),
100                         pkg.getLongVersionCode()),
101                 null, null, false /* isNative */);
102     }
103 
createSharedLibraryForStatic(AndroidPackage pkg)104     public static SharedLibraryInfo createSharedLibraryForStatic(AndroidPackage pkg) {
105         return new SharedLibraryInfo(null, pkg.getPackageName(),
106                 AndroidPackageUtils.getAllCodePaths(pkg),
107                 pkg.getStaticSharedLibraryName(),
108                 pkg.getStaticSharedLibraryVersion(),
109                 SharedLibraryInfo.TYPE_STATIC,
110                 new VersionedPackage(pkg.getManifestPackageName(),
111                         pkg.getLongVersionCode()),
112                 null, null, false /* isNative */);
113     }
114 
createSharedLibraryForDynamic(AndroidPackage pkg, String name)115     public static SharedLibraryInfo createSharedLibraryForDynamic(AndroidPackage pkg, String name) {
116         return new SharedLibraryInfo(null, pkg.getPackageName(),
117                 AndroidPackageUtils.getAllCodePaths(pkg), name,
118                 SharedLibraryInfo.VERSION_UNDEFINED,
119                 SharedLibraryInfo.TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(),
120                 pkg.getLongVersionCode()),
121                 null, null, false /* isNative */);
122     }
123 
124     /**
125      * Return the dex metadata files for the given package as a map
126      * [code path -> dex metadata path].
127      *
128      * NOTE: involves I/O checks.
129      */
getPackageDexMetadata(AndroidPackage pkg)130     public static Map<String, String> getPackageDexMetadata(AndroidPackage pkg) {
131         return DexMetadataHelper.buildPackageApkToDexMetadataMap
132                 (AndroidPackageUtils.getAllCodePaths(pkg));
133     }
134 
135     /**
136      * Validate the dex metadata files installed for the given package.
137      *
138      * @throws PackageParserException in case of errors.
139      */
validatePackageDexMetadata(AndroidPackage pkg)140     public static void validatePackageDexMetadata(AndroidPackage pkg)
141             throws PackageParserException {
142         Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
143         String packageName = pkg.getPackageName();
144         long versionCode = pkg.getLongVersionCode();
145         final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
146         for (String dexMetadata : apkToDexMetadataList) {
147             final ParseResult result = DexMetadataHelper.validateDexMetadataFile(
148                     input.reset(), dexMetadata, packageName, versionCode);
149             if (result.isError()) {
150                 throw new PackageParserException(
151                         result.getErrorCode(), result.getErrorMessage(), result.getException());
152             }
153         }
154     }
155 
createNativeLibraryHandle(AndroidPackage pkg)156     public static NativeLibraryHelper.Handle createNativeLibraryHandle(AndroidPackage pkg)
157             throws IOException {
158         return NativeLibraryHelper.Handle.create(
159                 AndroidPackageUtils.getAllCodePaths(pkg),
160                 pkg.isMultiArch(),
161                 pkg.isExtractNativeLibrariesRequested(),
162                 pkg.isDebuggable()
163         );
164     }
165 
canHaveOatDir(@onNull PackageState packageState, @NonNull AndroidPackage pkg)166     public static boolean canHaveOatDir(@NonNull PackageState packageState,
167             @NonNull AndroidPackage pkg) {
168         // The following app types CANNOT have oat directory
169         // - non-updated system apps,
170         // - incrementally installed apps.
171         if (packageState.isSystem() && !packageState.isUpdatedSystemApp()) {
172             return false;
173         }
174         if (IncrementalManager.isIncrementalPath(pkg.getPath())) {
175             return false;
176         }
177         return true;
178     }
179 
hasComponentClassName(AndroidPackage pkg, String className)180     public static boolean hasComponentClassName(AndroidPackage pkg, String className) {
181         List<ParsedActivity> activities = pkg.getActivities();
182         int activitiesSize = activities.size();
183         for (int index = 0; index < activitiesSize; index++) {
184             if (Objects.equals(className, activities.get(index).getName())) {
185                 return true;
186             }
187         }
188 
189         List<ParsedActivity> receivers = pkg.getReceivers();
190         int receiversSize = receivers.size();
191         for (int index = 0; index < receiversSize; index++) {
192             if (Objects.equals(className, receivers.get(index).getName())) {
193                 return true;
194             }
195         }
196 
197         List<ParsedProvider> providers = pkg.getProviders();
198         int providersSize = providers.size();
199         for (int index = 0; index < providersSize; index++) {
200             if (Objects.equals(className, providers.get(index).getName())) {
201                 return true;
202             }
203         }
204 
205         List<ParsedService> services = pkg.getServices();
206         int servicesSize = services.size();
207         for (int index = 0; index < servicesSize; index++) {
208             if (Objects.equals(className, services.get(index).getName())) {
209                 return true;
210             }
211         }
212 
213         List<ParsedInstrumentation> instrumentations = pkg.getInstrumentations();
214         int instrumentationsSize = instrumentations.size();
215         for (int index = 0; index < instrumentationsSize; index++) {
216             if (Objects.equals(className, instrumentations.get(index).getName())) {
217                 return true;
218             }
219         }
220 
221         if (pkg.getBackupAgentName() != null) {
222             if (Objects.equals(className, pkg.getBackupAgentName())) {
223                 return true;
224             }
225         }
226 
227         return false;
228     }
229 
isEncryptionAware(AndroidPackage pkg)230     public static boolean isEncryptionAware(AndroidPackage pkg) {
231         return pkg.isDirectBootAware() || pkg.isPartiallyDirectBootAware();
232     }
233 
isLibrary(AndroidPackage pkg)234     public static boolean isLibrary(AndroidPackage pkg) {
235         // TODO(b/135203078): Can parsing just enforce these always match?
236         return pkg.getSdkLibraryName() != null || pkg.getStaticSharedLibraryName() != null
237                 || !pkg.getLibraryNames().isEmpty();
238     }
239 
getHiddenApiEnforcementPolicy(@onNull AndroidPackage pkg, @NonNull PackageStateInternal packageState)240     public static int getHiddenApiEnforcementPolicy(@NonNull AndroidPackage pkg,
241             @NonNull PackageStateInternal packageState) {
242         boolean isAllowedToUseHiddenApis;
243         if (pkg == null) {
244             isAllowedToUseHiddenApis = false;
245         } else if (pkg.isSignedWithPlatformKey()) {
246             isAllowedToUseHiddenApis = true;
247         } else if (packageState.isSystem()) {
248             isAllowedToUseHiddenApis = pkg.isNonSdkApiRequested()
249                     || SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(
250                     pkg.getPackageName());
251         } else {
252             isAllowedToUseHiddenApis = false;
253         }
254 
255         if (isAllowedToUseHiddenApis) {
256             return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
257         }
258 
259         // TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done
260         //  entirely through ApplicationInfo and shouldn't touch this specific class, but that
261         //  may not always hold true.
262 //        if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) {
263 //            return mHiddenApiPolicy;
264 //        }
265         return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED;
266     }
267 
268     /**
269      * Returns false iff the provided flags include the {@link PackageManager#MATCH_SYSTEM_ONLY}
270      * flag and the provided package is not a system package. Otherwise returns {@code true}.
271      */
isMatchForSystemOnly(@onNull PackageState packageState, long flags)272     public static boolean isMatchForSystemOnly(@NonNull PackageState packageState, long flags) {
273         if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
274             return packageState.isSystem();
275         }
276         return true;
277     }
278 
279     /**
280      * Returns the primary ABI as parsed from the package. Used only during parsing and derivation.
281      * Otherwise prefer {@link PackageState#getPrimaryCpuAbi()}.
282      */
getRawPrimaryCpuAbi(AndroidPackage pkg)283     public static String getRawPrimaryCpuAbi(AndroidPackage pkg) {
284         return ((AndroidPackageHidden) pkg).getPrimaryCpuAbi();
285     }
286 
287     /**
288      * Returns the secondary ABI as parsed from the package. Used only during parsing and
289      * derivation. Otherwise prefer {@link PackageState#getSecondaryCpuAbi()}.
290      */
getRawSecondaryCpuAbi(@onNull AndroidPackage pkg)291     public static String getRawSecondaryCpuAbi(@NonNull AndroidPackage pkg) {
292         return ((AndroidPackageHidden) pkg).getSecondaryCpuAbi();
293     }
294 
295     @Deprecated
296     @NonNull
generateAppInfoWithoutState(AndroidPackage pkg)297     public static ApplicationInfo generateAppInfoWithoutState(AndroidPackage pkg) {
298         return ((AndroidPackageHidden) pkg).toAppInfoWithoutState();
299     }
300 
301     /**
302      * Replacement of unnecessary legacy getRealPackage. Only returns a value if the package was
303      * actually renamed.
304      */
305     @Nullable
getRealPackageOrNull(@onNull AndroidPackage pkg, boolean isSystem)306     public static String getRealPackageOrNull(@NonNull AndroidPackage pkg, boolean isSystem) {
307         if (pkg.getOriginalPackages().isEmpty() || !isSystem) {
308             return null;
309         }
310 
311         return pkg.getManifestPackageName();
312     }
313 
fillVersionCodes(@onNull AndroidPackage pkg, @NonNull PackageInfo info)314     public static void fillVersionCodes(@NonNull AndroidPackage pkg, @NonNull PackageInfo info) {
315         info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
316         info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
317     }
318 }
319