blob: 6134d3098e3b20360040752f82eb34cacdc41e61 [file] [log] [blame]
David Brazdil6b4736d2016-02-04 11:54:17 +00001/*
2 * Copyright (C) 2016 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
17package com.android.server.pm;
18
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070019import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
20import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070021import static android.system.OsConstants.O_CREAT;
22import static android.system.OsConstants.O_RDWR;
23
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070024import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
25import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
26import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
27import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
28import static com.android.server.pm.PackageManagerService.TAG;
Shubham Ajmera246dccf2017-05-24 17:46:36 -070029
Andreas Gamped3e07d42016-09-06 18:22:19 -070030import android.annotation.NonNull;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070031import android.annotation.Nullable;
David Brazdil6b4736d2016-02-04 11:54:17 +000032import android.app.AppGlobals;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070033import android.content.Context;
David Brazdil6b4736d2016-02-04 11:54:17 +000034import android.content.Intent;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070035import android.content.pm.PackageInfoLite;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070036import android.content.pm.PackageManager;
David Brazdil6b4736d2016-02-04 11:54:17 +000037import android.content.pm.PackageParser;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070038import android.content.pm.PackageParser.PackageParserException;
David Brazdil6b4736d2016-02-04 11:54:17 +000039import android.content.pm.ResolveInfo;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070040import android.content.pm.Signature;
Calin Juravle03181622016-12-01 17:53:07 +000041import android.os.Build;
Todd Kennedy0eb97382017-10-03 16:57:22 -070042import android.os.Debug;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070043import android.os.Environment;
44import android.os.FileUtils;
Todd Kennedy0eb97382017-10-03 16:57:22 -070045import android.os.Process;
David Brazdil6b4736d2016-02-04 11:54:17 +000046import android.os.RemoteException;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070047import android.os.SystemProperties;
Jeff Sharkeyd5896632016-03-04 16:16:00 -070048import android.os.UserHandle;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070049import android.service.pm.PackageServiceDumpProto;
Narayan Kamath6d99f792016-05-16 17:34:48 +010050import android.system.ErrnoException;
Tobias Thierer96aac9b32017-10-17 20:26:20 +010051import android.system.Os;
David Brazdil6b4736d2016-02-04 11:54:17 +000052import android.util.ArraySet;
53import android.util.Log;
Nicolas Geoffray20a894e2017-09-08 13:01:40 +010054import android.util.Slog;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070055import android.util.proto.ProtoOutputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +000056
Sudheer Shankaa05a9942018-08-29 23:28:23 -070057import com.android.internal.content.NativeLibraryHelper;
58import com.android.internal.content.PackageHelper;
59import com.android.internal.util.ArrayUtils;
60import com.android.internal.util.FastPrintWriter;
61import com.android.server.EventLogTags;
62import com.android.server.pm.dex.DexManager;
63import com.android.server.pm.dex.PackageDexUsage;
64
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070065import dalvik.system.VMRuntime;
66
67import libcore.io.IoUtils;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070068
69import java.io.BufferedReader;
Narayan Kamath6d99f792016-05-16 17:34:48 +010070import java.io.File;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070071import java.io.FileDescriptor;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070072import java.io.FileInputStream;
73import java.io.FileOutputStream;
74import java.io.FileReader;
75import java.io.FilenameFilter;
Narayan Kamath6d99f792016-05-16 17:34:48 +010076import java.io.IOException;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070077import java.io.InputStream;
78import java.io.OutputStream;
79import java.io.PrintWriter;
80import java.security.cert.CertificateEncodingException;
81import java.security.cert.CertificateException;
82import java.text.SimpleDateFormat;
David Brazdil6b4736d2016-02-04 11:54:17 +000083import java.util.ArrayList;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070084import java.util.Arrays;
David Brazdil6b4736d2016-02-04 11:54:17 +000085import java.util.Collection;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +010086import java.util.Collections;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070087import java.util.Date;
David Brazdil6b4736d2016-02-04 11:54:17 +000088import java.util.LinkedList;
89import java.util.List;
Andreas Gamped3e07d42016-09-06 18:22:19 -070090import java.util.function.Predicate;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070091import java.util.zip.GZIPInputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +000092
David Brazdil6b4736d2016-02-04 11:54:17 +000093/**
94 * Class containing helper methods for the PackageManagerService.
95 *
96 * {@hide}
97 */
98public class PackageManagerServiceUtils {
David Brazdilb62d6902016-02-11 13:36:07 +000099 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
David Brazdil6b4736d2016-02-04 11:54:17 +0000100
101 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
102 List<ResolveInfo> ris = null;
103 try {
Jeff Sharkeyd5896632016-03-04 16:16:00 -0700104 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
105 .getList();
David Brazdil6b4736d2016-02-04 11:54:17 +0000106 } catch (RemoteException e) {
107 }
108 ArraySet<String> pkgNames = new ArraySet<String>();
109 if (ris != null) {
110 for (ResolveInfo ri : ris) {
111 pkgNames.add(ri.activityInfo.packageName);
112 }
113 }
114 return pkgNames;
115 }
116
Andreas Gamped3e07d42016-09-06 18:22:19 -0700117 // Sort a list of apps by their last usage, most recently used apps first. The order of
118 // packages without usage data is undefined (but they will be sorted after the packages
119 // that do have usage data).
120 public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs,
121 PackageManagerService packageManagerService) {
122 if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
123 return;
124 }
125
126 Collections.sort(pkgs, (pkg1, pkg2) ->
127 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(),
128 pkg1.getLatestForegroundPackageUseTimeInMills()));
129 }
130
131 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
132 // package will be removed from {@code packages} and added to {@code result} with its
133 // dependencies. If usage data is available, the positive packages will be sorted by usage
134 // data (with {@code sortTemp} as temporary storage).
135 private static void applyPackageFilter(Predicate<PackageParser.Package> filter,
136 Collection<PackageParser.Package> result,
137 Collection<PackageParser.Package> packages,
138 @NonNull List<PackageParser.Package> sortTemp,
139 PackageManagerService packageManagerService) {
140 for (PackageParser.Package pkg : packages) {
141 if (filter.test(pkg)) {
142 sortTemp.add(pkg);
David Brazdil6b4736d2016-02-04 11:54:17 +0000143 }
144 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700145
146 sortPackagesByUsageDate(sortTemp, packageManagerService);
147 packages.removeAll(sortTemp);
148
149 for (PackageParser.Package pkg : sortTemp) {
150 result.add(pkg);
151
152 Collection<PackageParser.Package> deps =
153 packageManagerService.findSharedNonSystemLibraries(pkg);
154 if (!deps.isEmpty()) {
155 deps.removeAll(result);
156 result.addAll(deps);
157 packages.removeAll(deps);
158 }
David Brazdil6b4736d2016-02-04 11:54:17 +0000159 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700160
161 sortTemp.clear();
David Brazdil6b4736d2016-02-04 11:54:17 +0000162 }
163
164 // Sort apps by importance for dexopt ordering. Important apps are given
165 // more priority in case the device runs out of space.
166 public static List<PackageParser.Package> getPackagesForDexopt(
167 Collection<PackageParser.Package> packages,
168 PackageManagerService packageManagerService) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700169 return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
170 }
171
172 public static List<PackageParser.Package> getPackagesForDexopt(
173 Collection<PackageParser.Package> packages,
174 PackageManagerService packageManagerService,
175 boolean debug) {
David Brazdil6b4736d2016-02-04 11:54:17 +0000176 ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
177 LinkedList<PackageParser.Package> result = new LinkedList<>();
Andreas Gamped3e07d42016-09-06 18:22:19 -0700178 ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
David Brazdil6b4736d2016-02-04 11:54:17 +0000179
180 // Give priority to core apps.
Andreas Gamped3e07d42016-09-06 18:22:19 -0700181 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp,
182 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000183
184 // Give priority to system apps that listen for pre boot complete.
185 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
Andreas Gamped3e07d42016-09-06 18:22:19 -0700186 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
187 applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs,
188 sortTemp, packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000189
David Brazdil90e26992016-04-18 14:08:52 +0100190 // Give priority to apps used by other apps.
Calin Juravle3b74c412017-08-03 19:48:37 -0700191 DexManager dexManager = packageManagerService.getDexManager();
Calin Juravle07b6eab2017-03-01 19:55:35 -0800192 applyPackageFilter((pkg) ->
Calin Juravle52a452c2017-08-04 01:42:17 -0700193 dexManager.getPackageUseInfoOrDefault(pkg.packageName)
194 .isAnyCodePathUsedByOtherApps(),
195 result, remainingPkgs, sortTemp, packageManagerService);
David Brazdil90e26992016-04-18 14:08:52 +0100196
David Brazdil6b4736d2016-02-04 11:54:17 +0000197 // Filter out packages that aren't recently used, add all remaining apps.
198 // TODO: add a property to control this?
Andreas Gamped3e07d42016-09-06 18:22:19 -0700199 Predicate<PackageParser.Package> remainingPredicate;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100200 if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700201 if (debug) {
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100202 Log.i(TAG, "Looking at historical package use");
203 }
204 // Get the package that was used last.
205 PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
206 Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
207 pkg2.getLatestForegroundPackageUseTimeInMills()));
Andreas Gampe069a91c2018-09-05 16:11:50 -0700208 if (debug) {
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100209 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
210 }
211 long estimatedPreviousSystemUseTime =
212 lastUsed.getLatestForegroundPackageUseTimeInMills();
213 // Be defensive if for some reason package usage has bogus data.
214 if (estimatedPreviousSystemUseTime != 0) {
Andreas Gamped3e07d42016-09-06 18:22:19 -0700215 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
216 remainingPredicate =
217 (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
218 } else {
219 // No meaningful historical info. Take all.
220 remainingPredicate = (pkg) -> true;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100221 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700222 sortPackagesByUsageDate(remainingPkgs, packageManagerService);
223 } else {
224 // No historical info. Take all.
225 remainingPredicate = (pkg) -> true;
David Brazdil6b4736d2016-02-04 11:54:17 +0000226 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700227 applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
228 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000229
Andreas Gampe069a91c2018-09-05 16:11:50 -0700230 if (debug) {
Andreas Gampedab38e02016-09-09 17:50:20 -0700231 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
232 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
David Brazdil6b4736d2016-02-04 11:54:17 +0000233 }
234
235 return result;
236 }
Narayan Kamath6d99f792016-05-16 17:34:48 +0100237
238 /**
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700239 * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
240 * Package is considered active, if:
241 * 1) It was active in foreground.
242 * 2) It was active in background and also used by other apps.
243 *
244 * If it doesn't have sufficient information about the package, it return <code>false</code>.
245 */
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700246 public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700247 long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
248 long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
249
250 if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
251 return false;
252 }
253
254 // If the app was active in foreground during the threshold period.
255 boolean isActiveInForeground = (currentTimeInMillis
256 - latestForegroundPackageUseTimeInMillis)
257 < thresholdTimeinMillis;
258
259 if (isActiveInForeground) {
260 return false;
261 }
262
263 // If the app was active in background during the threshold period and was used
264 // by other packages.
265 boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
266 - latestPackageUseTimeInMillis)
267 < thresholdTimeinMillis)
Calin Juravle52a452c2017-08-04 01:42:17 -0700268 && packageUseInfo.isAnyCodePathUsedByOtherApps();
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700269
270 return !isActiveInBackgroundAndUsedByOtherPackages;
271 }
272
273 /**
Narayan Kamath6d99f792016-05-16 17:34:48 +0100274 * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
275 * semantics.
276 */
277 public static String realpath(File path) throws IOException {
278 try {
Tobias Thierer96aac9b32017-10-17 20:26:20 +0100279 return Os.realpath(path.getAbsolutePath());
Narayan Kamath6d99f792016-05-16 17:34:48 +0100280 } catch (ErrnoException ee) {
281 throw ee.rethrowAsIOException();
282 }
283 }
Andreas Gampedab38e02016-09-09 17:50:20 -0700284
285 public static String packagesToString(Collection<PackageParser.Package> c) {
286 StringBuilder sb = new StringBuilder();
287 for (PackageParser.Package pkg : c) {
288 if (sb.length() > 0) {
289 sb.append(", ");
290 }
291 sb.append(pkg.packageName);
292 }
293 return sb.toString();
294 }
Calin Juravle03181622016-12-01 17:53:07 +0000295
296 /**
297 * Verifies that the given string {@code isa} is a valid supported isa on
298 * the running device.
299 */
300 public static boolean checkISA(String isa) {
301 for (String abi : Build.SUPPORTED_ABIS) {
302 if (VMRuntime.getInstructionSet(abi).equals(isa)) {
303 return true;
304 }
305 }
306 return false;
307 }
Nicolas Geoffray20a894e2017-09-08 13:01:40 +0100308
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800309 public static long getLastModifiedTime(PackageParser.Package pkg) {
310 final File srcFile = new File(pkg.codePath);
311 if (!srcFile.isDirectory()) {
312 return srcFile.lastModified();
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700313 }
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800314 final File baseFile = new File(pkg.baseCodePath);
315 long maxModifiedTime = baseFile.lastModified();
316 if (pkg.splitCodePaths != null) {
317 for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
318 final File splitFile = new File(pkg.splitCodePaths[i]);
319 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
320 }
321 }
322 return maxModifiedTime;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700323 }
324
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700325 private static File getSettingsProblemFile() {
326 File dataDir = Environment.getDataDirectory();
327 File systemDir = new File(dataDir, "system");
328 File fname = new File(systemDir, "uiderrors.txt");
329 return fname;
330 }
331
332 public static void dumpCriticalInfo(ProtoOutputStream proto) {
333 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
334 String line = null;
335 while ((line = in.readLine()) != null) {
336 if (line.contains("ignored: updated version")) continue;
337 proto.write(PackageServiceDumpProto.MESSAGES, line);
338 }
339 } catch (IOException ignored) {
340 }
341 }
342
343 public static void dumpCriticalInfo(PrintWriter pw, String msg) {
344 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
345 String line = null;
346 while ((line = in.readLine()) != null) {
347 if (line.contains("ignored: updated version")) continue;
348 if (msg != null) {
349 pw.print(msg);
350 }
351 pw.println(line);
352 }
353 } catch (IOException ignored) {
354 }
355 }
356
357 public static void logCriticalInfo(int priority, String msg) {
358 Slog.println(priority, TAG, msg);
359 EventLogTags.writePmCriticalInfo(msg);
360 try {
361 File fname = getSettingsProblemFile();
362 FileOutputStream out = new FileOutputStream(fname, true);
363 PrintWriter pw = new FastPrintWriter(out);
364 SimpleDateFormat formatter = new SimpleDateFormat();
365 String dateString = formatter.format(new Date(System.currentTimeMillis()));
366 pw.println(dateString + ": " + msg);
367 pw.close();
368 FileUtils.setPermissions(
369 fname.toString(),
370 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH,
371 -1, -1);
372 } catch (java.io.IOException e) {
373 }
374 }
375
Todd Kennedy0eb97382017-10-03 16:57:22 -0700376 public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
377 if (callingUid == Process.SHELL_UID) {
378 if (userHandle >= 0
379 && PackageManagerService.sUserManager.hasUserRestriction(
380 restriction, userHandle)) {
381 throw new SecurityException("Shell does not have permission to access user "
382 + userHandle);
383 } else if (userHandle < 0) {
384 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
385 + userHandle + "\n\t" + Debug.getCallers(3));
386 }
387 }
388 }
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700389
390 /**
391 * Derive the value of the {@code cpuAbiOverride} based on the provided
392 * value and an optional stored value from the package settings.
393 */
394 public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
395 String cpuAbiOverride = null;
396 if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
397 cpuAbiOverride = null;
398 } else if (abiOverride != null) {
399 cpuAbiOverride = abiOverride;
400 } else if (settings != null) {
401 cpuAbiOverride = settings.cpuAbiOverrideString;
402 }
403 return cpuAbiOverride;
404 }
405
406 /**
407 * Compares two sets of signatures. Returns:
408 * <br />
409 * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
410 * <br />
411 * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
412 * <br />
413 * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
414 * <br />
415 * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
416 * <br />
417 * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
418 */
419 public static int compareSignatures(Signature[] s1, Signature[] s2) {
420 if (s1 == null) {
421 return s2 == null
422 ? PackageManager.SIGNATURE_NEITHER_SIGNED
423 : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
424 }
425
426 if (s2 == null) {
427 return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
428 }
429
430 if (s1.length != s2.length) {
431 return PackageManager.SIGNATURE_NO_MATCH;
432 }
433
434 // Since both signature sets are of size 1, we can compare without HashSets.
435 if (s1.length == 1) {
436 return s1[0].equals(s2[0]) ?
437 PackageManager.SIGNATURE_MATCH :
438 PackageManager.SIGNATURE_NO_MATCH;
439 }
440
441 ArraySet<Signature> set1 = new ArraySet<Signature>();
442 for (Signature sig : s1) {
443 set1.add(sig);
444 }
445 ArraySet<Signature> set2 = new ArraySet<Signature>();
446 for (Signature sig : s2) {
447 set2.add(sig);
448 }
449 // Make sure s2 contains all signatures in s1.
450 if (set1.equals(set2)) {
451 return PackageManager.SIGNATURE_MATCH;
452 }
453 return PackageManager.SIGNATURE_NO_MATCH;
454 }
455
456 /**
457 * Used for backward compatibility to make sure any packages with
458 * certificate chains get upgraded to the new style. {@code existingSigs}
459 * will be in the old format (since they were stored on disk from before the
460 * system upgrade) and {@code scannedSigs} will be in the newer format.
461 */
462 private static boolean matchSignaturesCompat(String packageName,
Patrick Baumann420d58a2017-12-19 10:17:21 -0800463 PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700464 ArraySet<Signature> existingSet = new ArraySet<Signature>();
Daniel Cashman77029c52018-01-18 16:19:29 -0800465 for (Signature sig : packageSignatures.mSigningDetails.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700466 existingSet.add(sig);
467 }
468 ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
Patrick Baumann420d58a2017-12-19 10:17:21 -0800469 for (Signature sig : parsedSignatures.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700470 try {
471 Signature[] chainSignatures = sig.getChainSignatures();
472 for (Signature chainSig : chainSignatures) {
473 scannedCompatSet.add(chainSig);
474 }
475 } catch (CertificateEncodingException e) {
476 scannedCompatSet.add(sig);
477 }
478 }
479 // make sure the expanded scanned set contains all signatures in the existing one
480 if (scannedCompatSet.equals(existingSet)) {
481 // migrate the old signatures to the new scheme
Daniel Cashman77029c52018-01-18 16:19:29 -0800482 packageSignatures.mSigningDetails = parsedSignatures;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700483 return true;
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800484 } else if (parsedSignatures.hasPastSigningCertificates()) {
485
486 // well this sucks: the parsed package has probably rotated signing certificates, but
487 // we don't have enough information to determine if the new signing certificate was
488 // blessed by the old one
489 logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing "
490 + "certificate chain. Unable to install newer version with rotated signing "
491 + "certificate.");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700492 }
493 return false;
494 }
495
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800496 private static boolean matchSignaturesRecover(
497 String packageName,
498 PackageParser.SigningDetails existingSignatures,
499 PackageParser.SigningDetails parsedSignatures,
500 @PackageParser.SigningDetails.CertCapabilities int flags) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700501 String msg = null;
502 try {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800503 if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
504 logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
505 + packageName);
506 return true;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700507 }
508 } catch (CertificateException e) {
509 msg = e.getMessage();
510 }
511 logCriticalInfo(Log.INFO,
512 "Failed to recover certificates for " + packageName + ": " + msg);
513 return false;
514 }
515
516 /**
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700517 * Make sure the updated priv app is signed with the same key as the original APK file on the
518 * /system partition.
519 *
520 * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
521 * and is not tamperproof.
522 */
523 private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
524 PackageSetting disabledPkgSetting) {
525 try {
Victor Hsieh5f761242018-01-20 10:30:12 -0800526 PackageParser.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800527 if (pkgSetting.signatures.mSigningDetails.checkCapability(
528 disabledPkgSetting.signatures.mSigningDetails,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700529 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
530 || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
531 pkgSetting.signatures.mSigningDetails,
532 PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800533 return true;
534 } else {
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700535 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
536 pkgSetting.name);
537 return false;
538 }
539 } catch (PackageParserException e) {
540 logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " +
541 e.getMessage());
542 return false;
543 }
Daniel Cashman5cdda342018-01-19 07:22:52 -0800544 }
545
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700546 /** Default is to not use fs-verity since it depends on kernel support. */
547 private static final int FSVERITY_DISABLED = 0;
548
549 /**
550 * Experimental implementation targeting priv apps, with Android specific kernel patches to
551 * extend fs-verity.
552 */
553 private static final int FSVERITY_LEGACY = 1;
554
555 /** Standard fs-verity. */
556 private static final int FSVERITY_ENABLED = 2;
557
Victor Hsieh0663df42019-01-07 15:28:27 -0800558 /** Returns true if standard APK Verity is enabled. */
Victor Hsieh55f14992018-01-13 14:12:59 -0800559 static boolean isApkVerityEnabled() {
Victor Hsieh0663df42019-01-07 15:28:27 -0800560 return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700561 }
562
Victor Hsieh0663df42019-01-07 15:28:27 -0800563 static boolean isLegacyApkVerityEnabled() {
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700564 return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
Victor Hsieh55f14992018-01-13 14:12:59 -0800565 }
566
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700567 /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
568 static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
Victor Hsieh0663df42019-01-07 15:28:27 -0800569 return disabledPs != null && disabledPs.isPrivileged() && (
570 isApkVerityEnabled() || isLegacyApkVerityEnabled());
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700571 }
572
573 /**
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700574 * Verifies that signatures match.
575 * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
576 * @throws PackageManagerException if the signatures did not match.
577 */
578 public static boolean verifySignatures(PackageSetting pkgSetting,
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700579 PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
580 boolean compareCompat, boolean compareRecover)
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700581 throws PackageManagerException {
582 final String packageName = pkgSetting.name;
583 boolean compatMatch = false;
Daniel Cashman77029c52018-01-18 16:19:29 -0800584 if (pkgSetting.signatures.mSigningDetails.signatures != null) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800585
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700586 // Already existing package. Make sure signatures match
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800587 boolean match = parsedSignatures.checkCapability(
588 pkgSetting.signatures.mSigningDetails,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700589 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
590 || pkgSetting.signatures.mSigningDetails.checkCapability(
591 parsedSignatures,
592 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700593 if (!match && compareCompat) {
Patrick Baumann420d58a2017-12-19 10:17:21 -0800594 match = matchSignaturesCompat(packageName, pkgSetting.signatures,
595 parsedSignatures);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700596 compatMatch = match;
597 }
598 if (!match && compareRecover) {
599 match = matchSignaturesRecover(
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800600 packageName,
601 pkgSetting.signatures.mSigningDetails,
602 parsedSignatures,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700603 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
604 || matchSignaturesRecover(
605 packageName,
606 parsedSignatures,
607 pkgSetting.signatures.mSigningDetails,
608 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700609 }
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700610
611 if (!match && isApkVerificationForced(disabledPkgSetting)) {
612 match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
613 }
614
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700615 if (!match) {
616 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
617 "Package " + packageName +
Todd Kennedy96cb94b2017-11-29 13:17:12 -0800618 " signatures do not match previously installed version; ignoring!");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700619 }
620 }
621 // Check for shared user signatures
Daniel Cashman77029c52018-01-18 16:19:29 -0800622 if (pkgSetting.sharedUser != null
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800623 && pkgSetting.sharedUser.signatures.mSigningDetails
624 != PackageParser.SigningDetails.UNKNOWN) {
625
626 // Already existing package. Make sure signatures match. In case of signing certificate
627 // rotation, the packages with newer certs need to be ok with being sharedUserId with
628 // the older ones. We check to see if either the new package is signed by an older cert
629 // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
630 // with being sharedUser with the existing signing cert.
Daniel Cashman77029c52018-01-18 16:19:29 -0800631 boolean match =
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800632 parsedSignatures.checkCapability(
633 pkgSetting.sharedUser.signatures.mSigningDetails,
634 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
635 || pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(
636 parsedSignatures,
637 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy3e654842017-11-29 13:58:53 -0800638 if (!match && compareCompat) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700639 match = matchSignaturesCompat(
640 packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
641 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800642 if (!match && compareRecover) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800643 match =
644 matchSignaturesRecover(packageName,
645 pkgSetting.sharedUser.signatures.mSigningDetails,
646 parsedSignatures,
647 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
648 || matchSignaturesRecover(packageName,
649 parsedSignatures,
650 pkgSetting.sharedUser.signatures.mSigningDetails,
651 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700652 compatMatch |= match;
653 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800654 if (!match) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700655 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
656 "Package " + packageName
657 + " has no signatures that match those in shared user "
658 + pkgSetting.sharedUser.name + "; ignoring!");
659 }
660 }
661 return compatMatch;
662 }
663
664 public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
665 if (DEBUG_COMPRESSION) {
666 Slog.i(TAG, "Decompress file"
667 + "; src: " + srcFile.getAbsolutePath()
668 + ", dst: " + dstFile.getAbsolutePath());
669 }
670 try (
671 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
672 OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
673 ) {
Jeff Sharkeyb18f8992018-01-31 21:47:09 -0700674 FileUtils.copy(fileIn, fileOut);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700675 Os.chmod(dstFile.getAbsolutePath(), 0644);
676 return PackageManager.INSTALL_SUCCEEDED;
677 } catch (IOException e) {
678 logCriticalInfo(Log.ERROR, "Failed to decompress file"
679 + "; src: " + srcFile.getAbsolutePath()
680 + ", dst: " + dstFile.getAbsolutePath());
681 }
682 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
683 }
684
685 public static File[] getCompressedFiles(String codePath) {
686 final File stubCodePath = new File(codePath);
687 final String stubName = stubCodePath.getName();
688
689 // The layout of a compressed package on a given partition is as follows :
690 //
691 // Compressed artifacts:
692 //
693 // /partition/ModuleName/foo.gz
694 // /partation/ModuleName/bar.gz
695 //
696 // Stub artifact:
697 //
698 // /partition/ModuleName-Stub/ModuleName-Stub.apk
699 //
700 // In other words, stub is on the same partition as the compressed artifacts
701 // and in a directory that's suffixed with "-Stub".
702 int idx = stubName.lastIndexOf(STUB_SUFFIX);
703 if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) {
704 return null;
705 }
706
707 final File stubParentDir = stubCodePath.getParentFile();
708 if (stubParentDir == null) {
709 Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath);
710 return null;
711 }
712
713 final File compressedPath = new File(stubParentDir, stubName.substring(0, idx));
714 final File[] files = compressedPath.listFiles(new FilenameFilter() {
715 @Override
716 public boolean accept(File dir, String name) {
717 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
718 }
719 });
720
721 if (DEBUG_COMPRESSION && files != null && files.length > 0) {
722 Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files));
723 }
724
725 return files;
726 }
727
728 public static boolean compressedFileExists(String codePath) {
729 final File[] compressedFiles = getCompressedFiles(codePath);
730 return compressedFiles != null && compressedFiles.length > 0;
731 }
Sudheer Shankaa05a9942018-08-29 23:28:23 -0700732
733 /**
734 * Parse given package and return minimal details.
735 */
736 public static PackageInfoLite getMinimalPackageInfo(Context context, String packagePath,
737 int flags, String abiOverride) {
738 final PackageInfoLite ret = new PackageInfoLite();
739 if (packagePath == null) {
740 Slog.i(TAG, "Invalid package file " + packagePath);
741 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
742 return ret;
743 }
744
745 final File packageFile = new File(packagePath);
746 final PackageParser.PackageLite pkg;
747 final long sizeBytes;
748 try {
749 pkg = PackageParser.parsePackageLite(packageFile, 0);
750 sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
751 } catch (PackageParserException | IOException e) {
752 Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
753
754 if (!packageFile.exists()) {
755 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
756 } else {
757 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
758 }
759
760 return ret;
761 }
762
763 final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
764 pkg.packageName, pkg.installLocation, sizeBytes, flags);
765
766 ret.packageName = pkg.packageName;
767 ret.splitNames = pkg.splitNames;
768 ret.versionCode = pkg.versionCode;
769 ret.versionCodeMajor = pkg.versionCodeMajor;
770 ret.baseRevisionCode = pkg.baseRevisionCode;
771 ret.splitRevisionCodes = pkg.splitRevisionCodes;
772 ret.installLocation = pkg.installLocation;
773 ret.verifiers = pkg.verifiers;
774 ret.recommendedInstallLocation = recommendedInstallLocation;
775 ret.multiArch = pkg.multiArch;
776
777 return ret;
778 }
779
780 /**
781 * Calculate estimated footprint of given package post-installation.
782 *
783 * @return -1 if there's some error calculating the size, otherwise installed size of the
784 * package.
785 */
786 public static long calculateInstalledSize(String packagePath, String abiOverride) {
787 final File packageFile = new File(packagePath);
788 final PackageParser.PackageLite pkg;
789 try {
790 pkg = PackageParser.parsePackageLite(packageFile, 0);
791 return PackageHelper.calculateInstalledSize(pkg, abiOverride);
792 } catch (PackageParserException | IOException e) {
793 Slog.w(TAG, "Failed to calculate installed size: " + e);
794 return -1;
795 }
796 }
797
798 /**
799 * Copy package to the target location.
800 *
801 * @param packagePath absolute path to the package to be copied. Can be
802 * a single monolithic APK file or a cluster directory
803 * containing one or more APKs.
804 * @return returns status code according to those in
805 * {@link PackageManager}
806 */
807 public static int copyPackage(String packagePath, File targetDir) {
808 if (packagePath == null) {
809 return PackageManager.INSTALL_FAILED_INVALID_URI;
810 }
811
812 try {
813 final File packageFile = new File(packagePath);
814 final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
815 copyFile(pkg.baseCodePath, targetDir, "base.apk");
816 if (!ArrayUtils.isEmpty(pkg.splitNames)) {
817 for (int i = 0; i < pkg.splitNames.length; i++) {
818 copyFile(pkg.splitCodePaths[i], targetDir,
819 "split_" + pkg.splitNames[i] + ".apk");
820 }
821 }
822 return PackageManager.INSTALL_SUCCEEDED;
823 } catch (PackageParserException | IOException | ErrnoException e) {
824 Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
825 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
826 }
827 }
828
829 private static void copyFile(String sourcePath, File targetDir, String targetName)
830 throws ErrnoException, IOException {
831 if (!FileUtils.isValidExtFilename(targetName)) {
832 throw new IllegalArgumentException("Invalid filename: " + targetName);
833 }
834 Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
835
836 final File targetFile = new File(targetDir, targetName);
837 final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
838 O_RDWR | O_CREAT, 0644);
839 Os.chmod(targetFile.getAbsolutePath(), 0644);
840 FileInputStream source = null;
841 try {
842 source = new FileInputStream(sourcePath);
843 FileUtils.copy(source.getFD(), targetFd);
844 } finally {
845 IoUtils.closeQuietly(source);
846 }
847 }
Narayan Kamath6d99f792016-05-16 17:34:48 +0100848}