blob: a12005bc371971fa2b485f8a8d650b8e91c7417f [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;
Nikita Ioffe8bbb8152019-02-21 15:54:54 +000035import android.content.pm.ApplicationInfo;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070036import android.content.pm.PackageInfoLite;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070037import android.content.pm.PackageManager;
David Brazdil6b4736d2016-02-04 11:54:17 +000038import android.content.pm.PackageParser;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070039import android.content.pm.PackageParser.PackageParserException;
David Brazdil6b4736d2016-02-04 11:54:17 +000040import android.content.pm.ResolveInfo;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070041import android.content.pm.Signature;
Calin Juravle03181622016-12-01 17:53:07 +000042import android.os.Build;
Todd Kennedy0eb97382017-10-03 16:57:22 -070043import android.os.Debug;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070044import android.os.Environment;
45import android.os.FileUtils;
Todd Kennedy0eb97382017-10-03 16:57:22 -070046import android.os.Process;
David Brazdil6b4736d2016-02-04 11:54:17 +000047import android.os.RemoteException;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070048import android.os.SystemProperties;
Jeff Sharkeyd5896632016-03-04 16:16:00 -070049import android.os.UserHandle;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070050import android.service.pm.PackageServiceDumpProto;
Narayan Kamath6d99f792016-05-16 17:34:48 +010051import android.system.ErrnoException;
Tobias Thierer96aac9b32017-10-17 20:26:20 +010052import android.system.Os;
David Brazdil6b4736d2016-02-04 11:54:17 +000053import android.util.ArraySet;
54import android.util.Log;
Nicolas Geoffray20a894e2017-09-08 13:01:40 +010055import android.util.Slog;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070056import android.util.proto.ProtoOutputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +000057
Sudheer Shankaa05a9942018-08-29 23:28:23 -070058import com.android.internal.content.NativeLibraryHelper;
59import com.android.internal.content.PackageHelper;
60import com.android.internal.util.ArrayUtils;
61import com.android.internal.util.FastPrintWriter;
62import com.android.server.EventLogTags;
63import com.android.server.pm.dex.DexManager;
64import com.android.server.pm.dex.PackageDexUsage;
65
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070066import dalvik.system.VMRuntime;
67
68import libcore.io.IoUtils;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070069
70import java.io.BufferedReader;
Narayan Kamath6d99f792016-05-16 17:34:48 +010071import java.io.File;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070072import java.io.FileDescriptor;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070073import java.io.FileInputStream;
74import java.io.FileOutputStream;
75import java.io.FileReader;
76import java.io.FilenameFilter;
Narayan Kamath6d99f792016-05-16 17:34:48 +010077import java.io.IOException;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070078import java.io.InputStream;
79import java.io.OutputStream;
80import java.io.PrintWriter;
81import java.security.cert.CertificateEncodingException;
82import java.security.cert.CertificateException;
83import java.text.SimpleDateFormat;
David Brazdil6b4736d2016-02-04 11:54:17 +000084import java.util.ArrayList;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070085import java.util.Arrays;
David Brazdil6b4736d2016-02-04 11:54:17 +000086import java.util.Collection;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +010087import java.util.Collections;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070088import java.util.Date;
David Brazdil6b4736d2016-02-04 11:54:17 +000089import java.util.LinkedList;
90import java.util.List;
Andreas Gamped3e07d42016-09-06 18:22:19 -070091import java.util.function.Predicate;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070092import java.util.zip.GZIPInputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +000093
David Brazdil6b4736d2016-02-04 11:54:17 +000094/**
95 * Class containing helper methods for the PackageManagerService.
96 *
97 * {@hide}
98 */
99public class PackageManagerServiceUtils {
David Brazdilb62d6902016-02-11 13:36:07 +0000100 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
David Brazdil6b4736d2016-02-04 11:54:17 +0000101
102 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
103 List<ResolveInfo> ris = null;
104 try {
Jeff Sharkeyd5896632016-03-04 16:16:00 -0700105 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
106 .getList();
David Brazdil6b4736d2016-02-04 11:54:17 +0000107 } catch (RemoteException e) {
108 }
109 ArraySet<String> pkgNames = new ArraySet<String>();
110 if (ris != null) {
111 for (ResolveInfo ri : ris) {
112 pkgNames.add(ri.activityInfo.packageName);
113 }
114 }
115 return pkgNames;
116 }
117
Andreas Gamped3e07d42016-09-06 18:22:19 -0700118 // Sort a list of apps by their last usage, most recently used apps first. The order of
119 // packages without usage data is undefined (but they will be sorted after the packages
120 // that do have usage data).
121 public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs,
122 PackageManagerService packageManagerService) {
123 if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
124 return;
125 }
126
127 Collections.sort(pkgs, (pkg1, pkg2) ->
128 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(),
129 pkg1.getLatestForegroundPackageUseTimeInMills()));
130 }
131
132 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
133 // package will be removed from {@code packages} and added to {@code result} with its
134 // dependencies. If usage data is available, the positive packages will be sorted by usage
135 // data (with {@code sortTemp} as temporary storage).
136 private static void applyPackageFilter(Predicate<PackageParser.Package> filter,
137 Collection<PackageParser.Package> result,
138 Collection<PackageParser.Package> packages,
139 @NonNull List<PackageParser.Package> sortTemp,
140 PackageManagerService packageManagerService) {
141 for (PackageParser.Package pkg : packages) {
142 if (filter.test(pkg)) {
143 sortTemp.add(pkg);
David Brazdil6b4736d2016-02-04 11:54:17 +0000144 }
145 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700146
147 sortPackagesByUsageDate(sortTemp, packageManagerService);
148 packages.removeAll(sortTemp);
149
150 for (PackageParser.Package pkg : sortTemp) {
151 result.add(pkg);
152
153 Collection<PackageParser.Package> deps =
154 packageManagerService.findSharedNonSystemLibraries(pkg);
155 if (!deps.isEmpty()) {
156 deps.removeAll(result);
157 result.addAll(deps);
158 packages.removeAll(deps);
159 }
David Brazdil6b4736d2016-02-04 11:54:17 +0000160 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700161
162 sortTemp.clear();
David Brazdil6b4736d2016-02-04 11:54:17 +0000163 }
164
165 // Sort apps by importance for dexopt ordering. Important apps are given
166 // more priority in case the device runs out of space.
167 public static List<PackageParser.Package> getPackagesForDexopt(
168 Collection<PackageParser.Package> packages,
169 PackageManagerService packageManagerService) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700170 return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
171 }
172
173 public static List<PackageParser.Package> getPackagesForDexopt(
174 Collection<PackageParser.Package> packages,
175 PackageManagerService packageManagerService,
176 boolean debug) {
David Brazdil6b4736d2016-02-04 11:54:17 +0000177 ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
178 LinkedList<PackageParser.Package> result = new LinkedList<>();
Andreas Gamped3e07d42016-09-06 18:22:19 -0700179 ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
David Brazdil6b4736d2016-02-04 11:54:17 +0000180
181 // Give priority to core apps.
Andreas Gamped3e07d42016-09-06 18:22:19 -0700182 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp,
183 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000184
185 // Give priority to system apps that listen for pre boot complete.
186 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
Andreas Gamped3e07d42016-09-06 18:22:19 -0700187 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
188 applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs,
189 sortTemp, packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000190
David Brazdil90e26992016-04-18 14:08:52 +0100191 // Give priority to apps used by other apps.
Calin Juravle3b74c412017-08-03 19:48:37 -0700192 DexManager dexManager = packageManagerService.getDexManager();
Calin Juravle07b6eab2017-03-01 19:55:35 -0800193 applyPackageFilter((pkg) ->
Calin Juravle52a452c2017-08-04 01:42:17 -0700194 dexManager.getPackageUseInfoOrDefault(pkg.packageName)
195 .isAnyCodePathUsedByOtherApps(),
196 result, remainingPkgs, sortTemp, packageManagerService);
David Brazdil90e26992016-04-18 14:08:52 +0100197
David Brazdil6b4736d2016-02-04 11:54:17 +0000198 // Filter out packages that aren't recently used, add all remaining apps.
199 // TODO: add a property to control this?
Andreas Gamped3e07d42016-09-06 18:22:19 -0700200 Predicate<PackageParser.Package> remainingPredicate;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100201 if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700202 if (debug) {
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100203 Log.i(TAG, "Looking at historical package use");
204 }
205 // Get the package that was used last.
206 PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
207 Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
208 pkg2.getLatestForegroundPackageUseTimeInMills()));
Andreas Gampe069a91c2018-09-05 16:11:50 -0700209 if (debug) {
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100210 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
211 }
212 long estimatedPreviousSystemUseTime =
213 lastUsed.getLatestForegroundPackageUseTimeInMills();
214 // Be defensive if for some reason package usage has bogus data.
215 if (estimatedPreviousSystemUseTime != 0) {
Andreas Gamped3e07d42016-09-06 18:22:19 -0700216 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
217 remainingPredicate =
218 (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
219 } else {
220 // No meaningful historical info. Take all.
221 remainingPredicate = (pkg) -> true;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100222 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700223 sortPackagesByUsageDate(remainingPkgs, packageManagerService);
224 } else {
225 // No historical info. Take all.
226 remainingPredicate = (pkg) -> true;
David Brazdil6b4736d2016-02-04 11:54:17 +0000227 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700228 applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
229 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000230
Andreas Gampe069a91c2018-09-05 16:11:50 -0700231 if (debug) {
Andreas Gampedab38e02016-09-09 17:50:20 -0700232 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
233 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
David Brazdil6b4736d2016-02-04 11:54:17 +0000234 }
235
236 return result;
237 }
Narayan Kamath6d99f792016-05-16 17:34:48 +0100238
239 /**
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700240 * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
241 * Package is considered active, if:
242 * 1) It was active in foreground.
243 * 2) It was active in background and also used by other apps.
244 *
245 * If it doesn't have sufficient information about the package, it return <code>false</code>.
246 */
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700247 public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700248 long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
249 long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
250
251 if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
252 return false;
253 }
254
255 // If the app was active in foreground during the threshold period.
256 boolean isActiveInForeground = (currentTimeInMillis
257 - latestForegroundPackageUseTimeInMillis)
258 < thresholdTimeinMillis;
259
260 if (isActiveInForeground) {
261 return false;
262 }
263
264 // If the app was active in background during the threshold period and was used
265 // by other packages.
266 boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
267 - latestPackageUseTimeInMillis)
268 < thresholdTimeinMillis)
Calin Juravle52a452c2017-08-04 01:42:17 -0700269 && packageUseInfo.isAnyCodePathUsedByOtherApps();
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700270
271 return !isActiveInBackgroundAndUsedByOtherPackages;
272 }
273
274 /**
Narayan Kamath6d99f792016-05-16 17:34:48 +0100275 * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
276 * semantics.
277 */
278 public static String realpath(File path) throws IOException {
279 try {
Tobias Thierer96aac9b32017-10-17 20:26:20 +0100280 return Os.realpath(path.getAbsolutePath());
Narayan Kamath6d99f792016-05-16 17:34:48 +0100281 } catch (ErrnoException ee) {
282 throw ee.rethrowAsIOException();
283 }
284 }
Andreas Gampedab38e02016-09-09 17:50:20 -0700285
286 public static String packagesToString(Collection<PackageParser.Package> c) {
287 StringBuilder sb = new StringBuilder();
288 for (PackageParser.Package pkg : c) {
289 if (sb.length() > 0) {
290 sb.append(", ");
291 }
292 sb.append(pkg.packageName);
293 }
294 return sb.toString();
295 }
Calin Juravle03181622016-12-01 17:53:07 +0000296
297 /**
298 * Verifies that the given string {@code isa} is a valid supported isa on
299 * the running device.
300 */
301 public static boolean checkISA(String isa) {
302 for (String abi : Build.SUPPORTED_ABIS) {
303 if (VMRuntime.getInstructionSet(abi).equals(isa)) {
304 return true;
305 }
306 }
307 return false;
308 }
Nicolas Geoffray20a894e2017-09-08 13:01:40 +0100309
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800310 public static long getLastModifiedTime(PackageParser.Package pkg) {
311 final File srcFile = new File(pkg.codePath);
312 if (!srcFile.isDirectory()) {
313 return srcFile.lastModified();
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700314 }
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800315 final File baseFile = new File(pkg.baseCodePath);
316 long maxModifiedTime = baseFile.lastModified();
317 if (pkg.splitCodePaths != null) {
318 for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
319 final File splitFile = new File(pkg.splitCodePaths[i]);
320 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
321 }
322 }
323 return maxModifiedTime;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700324 }
325
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700326 private static File getSettingsProblemFile() {
327 File dataDir = Environment.getDataDirectory();
328 File systemDir = new File(dataDir, "system");
329 File fname = new File(systemDir, "uiderrors.txt");
330 return fname;
331 }
332
333 public static void dumpCriticalInfo(ProtoOutputStream proto) {
334 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
335 String line = null;
336 while ((line = in.readLine()) != null) {
337 if (line.contains("ignored: updated version")) continue;
338 proto.write(PackageServiceDumpProto.MESSAGES, line);
339 }
340 } catch (IOException ignored) {
341 }
342 }
343
344 public static void dumpCriticalInfo(PrintWriter pw, String msg) {
345 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
346 String line = null;
347 while ((line = in.readLine()) != null) {
348 if (line.contains("ignored: updated version")) continue;
349 if (msg != null) {
350 pw.print(msg);
351 }
352 pw.println(line);
353 }
354 } catch (IOException ignored) {
355 }
356 }
357
358 public static void logCriticalInfo(int priority, String msg) {
359 Slog.println(priority, TAG, msg);
360 EventLogTags.writePmCriticalInfo(msg);
361 try {
362 File fname = getSettingsProblemFile();
363 FileOutputStream out = new FileOutputStream(fname, true);
364 PrintWriter pw = new FastPrintWriter(out);
365 SimpleDateFormat formatter = new SimpleDateFormat();
366 String dateString = formatter.format(new Date(System.currentTimeMillis()));
367 pw.println(dateString + ": " + msg);
368 pw.close();
369 FileUtils.setPermissions(
370 fname.toString(),
371 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH,
372 -1, -1);
373 } catch (java.io.IOException e) {
374 }
375 }
376
Todd Kennedy0eb97382017-10-03 16:57:22 -0700377 public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
378 if (callingUid == Process.SHELL_UID) {
379 if (userHandle >= 0
380 && PackageManagerService.sUserManager.hasUserRestriction(
381 restriction, userHandle)) {
382 throw new SecurityException("Shell does not have permission to access user "
383 + userHandle);
384 } else if (userHandle < 0) {
385 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
386 + userHandle + "\n\t" + Debug.getCallers(3));
387 }
388 }
389 }
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700390
391 /**
392 * Derive the value of the {@code cpuAbiOverride} based on the provided
393 * value and an optional stored value from the package settings.
394 */
395 public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
396 String cpuAbiOverride = null;
397 if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
398 cpuAbiOverride = null;
399 } else if (abiOverride != null) {
400 cpuAbiOverride = abiOverride;
401 } else if (settings != null) {
402 cpuAbiOverride = settings.cpuAbiOverrideString;
403 }
404 return cpuAbiOverride;
405 }
406
407 /**
408 * Compares two sets of signatures. Returns:
409 * <br />
410 * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
411 * <br />
412 * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
413 * <br />
414 * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
415 * <br />
416 * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
417 * <br />
418 * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
419 */
420 public static int compareSignatures(Signature[] s1, Signature[] s2) {
421 if (s1 == null) {
422 return s2 == null
423 ? PackageManager.SIGNATURE_NEITHER_SIGNED
424 : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
425 }
426
427 if (s2 == null) {
428 return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
429 }
430
431 if (s1.length != s2.length) {
432 return PackageManager.SIGNATURE_NO_MATCH;
433 }
434
435 // Since both signature sets are of size 1, we can compare without HashSets.
436 if (s1.length == 1) {
437 return s1[0].equals(s2[0]) ?
438 PackageManager.SIGNATURE_MATCH :
439 PackageManager.SIGNATURE_NO_MATCH;
440 }
441
442 ArraySet<Signature> set1 = new ArraySet<Signature>();
443 for (Signature sig : s1) {
444 set1.add(sig);
445 }
446 ArraySet<Signature> set2 = new ArraySet<Signature>();
447 for (Signature sig : s2) {
448 set2.add(sig);
449 }
450 // Make sure s2 contains all signatures in s1.
451 if (set1.equals(set2)) {
452 return PackageManager.SIGNATURE_MATCH;
453 }
454 return PackageManager.SIGNATURE_NO_MATCH;
455 }
456
457 /**
458 * Used for backward compatibility to make sure any packages with
459 * certificate chains get upgraded to the new style. {@code existingSigs}
460 * will be in the old format (since they were stored on disk from before the
461 * system upgrade) and {@code scannedSigs} will be in the newer format.
462 */
463 private static boolean matchSignaturesCompat(String packageName,
Patrick Baumann420d58a2017-12-19 10:17:21 -0800464 PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700465 ArraySet<Signature> existingSet = new ArraySet<Signature>();
Daniel Cashman77029c52018-01-18 16:19:29 -0800466 for (Signature sig : packageSignatures.mSigningDetails.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700467 existingSet.add(sig);
468 }
469 ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
Patrick Baumann420d58a2017-12-19 10:17:21 -0800470 for (Signature sig : parsedSignatures.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700471 try {
472 Signature[] chainSignatures = sig.getChainSignatures();
473 for (Signature chainSig : chainSignatures) {
474 scannedCompatSet.add(chainSig);
475 }
476 } catch (CertificateEncodingException e) {
477 scannedCompatSet.add(sig);
478 }
479 }
480 // make sure the expanded scanned set contains all signatures in the existing one
481 if (scannedCompatSet.equals(existingSet)) {
482 // migrate the old signatures to the new scheme
Daniel Cashman77029c52018-01-18 16:19:29 -0800483 packageSignatures.mSigningDetails = parsedSignatures;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700484 return true;
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800485 } else if (parsedSignatures.hasPastSigningCertificates()) {
486
487 // well this sucks: the parsed package has probably rotated signing certificates, but
488 // we don't have enough information to determine if the new signing certificate was
489 // blessed by the old one
490 logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing "
491 + "certificate chain. Unable to install newer version with rotated signing "
492 + "certificate.");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700493 }
494 return false;
495 }
496
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800497 private static boolean matchSignaturesRecover(
498 String packageName,
499 PackageParser.SigningDetails existingSignatures,
500 PackageParser.SigningDetails parsedSignatures,
501 @PackageParser.SigningDetails.CertCapabilities int flags) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700502 String msg = null;
503 try {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800504 if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
505 logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
506 + packageName);
507 return true;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700508 }
509 } catch (CertificateException e) {
510 msg = e.getMessage();
511 }
512 logCriticalInfo(Log.INFO,
513 "Failed to recover certificates for " + packageName + ": " + msg);
514 return false;
515 }
516
517 /**
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700518 * Make sure the updated priv app is signed with the same key as the original APK file on the
519 * /system partition.
520 *
521 * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
522 * and is not tamperproof.
523 */
524 private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
525 PackageSetting disabledPkgSetting) {
526 try {
Victor Hsieh5f761242018-01-20 10:30:12 -0800527 PackageParser.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800528 if (pkgSetting.signatures.mSigningDetails.checkCapability(
529 disabledPkgSetting.signatures.mSigningDetails,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700530 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
531 || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
532 pkgSetting.signatures.mSigningDetails,
533 PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800534 return true;
535 } else {
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700536 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
537 pkgSetting.name);
538 return false;
539 }
540 } catch (PackageParserException e) {
541 logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " +
542 e.getMessage());
543 return false;
544 }
Daniel Cashman5cdda342018-01-19 07:22:52 -0800545 }
546
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700547 /** Default is to not use fs-verity since it depends on kernel support. */
548 private static final int FSVERITY_DISABLED = 0;
549
550 /**
551 * Experimental implementation targeting priv apps, with Android specific kernel patches to
552 * extend fs-verity.
553 */
554 private static final int FSVERITY_LEGACY = 1;
555
556 /** Standard fs-verity. */
557 private static final int FSVERITY_ENABLED = 2;
558
Victor Hsieh0663df42019-01-07 15:28:27 -0800559 /** Returns true if standard APK Verity is enabled. */
Victor Hsieh55f14992018-01-13 14:12:59 -0800560 static boolean isApkVerityEnabled() {
Victor Hsieh0663df42019-01-07 15:28:27 -0800561 return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700562 }
563
Victor Hsieh0663df42019-01-07 15:28:27 -0800564 static boolean isLegacyApkVerityEnabled() {
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700565 return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
Victor Hsieh55f14992018-01-13 14:12:59 -0800566 }
567
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700568 /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
569 static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
Victor Hsieh0663df42019-01-07 15:28:27 -0800570 return disabledPs != null && disabledPs.isPrivileged() && (
571 isApkVerityEnabled() || isLegacyApkVerityEnabled());
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700572 }
573
574 /**
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700575 * Verifies that signatures match.
576 * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
577 * @throws PackageManagerException if the signatures did not match.
578 */
579 public static boolean verifySignatures(PackageSetting pkgSetting,
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700580 PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
581 boolean compareCompat, boolean compareRecover)
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700582 throws PackageManagerException {
583 final String packageName = pkgSetting.name;
584 boolean compatMatch = false;
Daniel Cashman77029c52018-01-18 16:19:29 -0800585 if (pkgSetting.signatures.mSigningDetails.signatures != null) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800586
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700587 // Already existing package. Make sure signatures match
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800588 boolean match = parsedSignatures.checkCapability(
589 pkgSetting.signatures.mSigningDetails,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700590 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
591 || pkgSetting.signatures.mSigningDetails.checkCapability(
592 parsedSignatures,
593 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700594 if (!match && compareCompat) {
Patrick Baumann420d58a2017-12-19 10:17:21 -0800595 match = matchSignaturesCompat(packageName, pkgSetting.signatures,
596 parsedSignatures);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700597 compatMatch = match;
598 }
599 if (!match && compareRecover) {
600 match = matchSignaturesRecover(
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800601 packageName,
602 pkgSetting.signatures.mSigningDetails,
603 parsedSignatures,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700604 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
605 || matchSignaturesRecover(
606 packageName,
607 parsedSignatures,
608 pkgSetting.signatures.mSigningDetails,
609 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700610 }
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700611
612 if (!match && isApkVerificationForced(disabledPkgSetting)) {
613 match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
614 }
615
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700616 if (!match) {
617 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
618 "Package " + packageName +
Todd Kennedy96cb94b2017-11-29 13:17:12 -0800619 " signatures do not match previously installed version; ignoring!");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700620 }
621 }
622 // Check for shared user signatures
Daniel Cashman77029c52018-01-18 16:19:29 -0800623 if (pkgSetting.sharedUser != null
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800624 && pkgSetting.sharedUser.signatures.mSigningDetails
625 != PackageParser.SigningDetails.UNKNOWN) {
626
627 // Already existing package. Make sure signatures match. In case of signing certificate
628 // rotation, the packages with newer certs need to be ok with being sharedUserId with
629 // the older ones. We check to see if either the new package is signed by an older cert
630 // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
631 // with being sharedUser with the existing signing cert.
Daniel Cashman77029c52018-01-18 16:19:29 -0800632 boolean match =
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800633 parsedSignatures.checkCapability(
634 pkgSetting.sharedUser.signatures.mSigningDetails,
635 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
636 || pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(
637 parsedSignatures,
638 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy3e654842017-11-29 13:58:53 -0800639 if (!match && compareCompat) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700640 match = matchSignaturesCompat(
641 packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
642 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800643 if (!match && compareRecover) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800644 match =
645 matchSignaturesRecover(packageName,
646 pkgSetting.sharedUser.signatures.mSigningDetails,
647 parsedSignatures,
648 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
649 || matchSignaturesRecover(packageName,
650 parsedSignatures,
651 pkgSetting.sharedUser.signatures.mSigningDetails,
652 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700653 compatMatch |= match;
654 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800655 if (!match) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700656 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
657 "Package " + packageName
658 + " has no signatures that match those in shared user "
659 + pkgSetting.sharedUser.name + "; ignoring!");
660 }
661 }
662 return compatMatch;
663 }
664
665 public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
666 if (DEBUG_COMPRESSION) {
667 Slog.i(TAG, "Decompress file"
668 + "; src: " + srcFile.getAbsolutePath()
669 + ", dst: " + dstFile.getAbsolutePath());
670 }
671 try (
672 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
673 OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
674 ) {
Jeff Sharkeyb18f8992018-01-31 21:47:09 -0700675 FileUtils.copy(fileIn, fileOut);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700676 Os.chmod(dstFile.getAbsolutePath(), 0644);
677 return PackageManager.INSTALL_SUCCEEDED;
678 } catch (IOException e) {
679 logCriticalInfo(Log.ERROR, "Failed to decompress file"
680 + "; src: " + srcFile.getAbsolutePath()
681 + ", dst: " + dstFile.getAbsolutePath());
682 }
683 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
684 }
685
686 public static File[] getCompressedFiles(String codePath) {
687 final File stubCodePath = new File(codePath);
688 final String stubName = stubCodePath.getName();
689
690 // The layout of a compressed package on a given partition is as follows :
691 //
692 // Compressed artifacts:
693 //
694 // /partition/ModuleName/foo.gz
695 // /partation/ModuleName/bar.gz
696 //
697 // Stub artifact:
698 //
699 // /partition/ModuleName-Stub/ModuleName-Stub.apk
700 //
701 // In other words, stub is on the same partition as the compressed artifacts
702 // and in a directory that's suffixed with "-Stub".
703 int idx = stubName.lastIndexOf(STUB_SUFFIX);
704 if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) {
705 return null;
706 }
707
708 final File stubParentDir = stubCodePath.getParentFile();
709 if (stubParentDir == null) {
710 Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath);
711 return null;
712 }
713
714 final File compressedPath = new File(stubParentDir, stubName.substring(0, idx));
715 final File[] files = compressedPath.listFiles(new FilenameFilter() {
716 @Override
717 public boolean accept(File dir, String name) {
718 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
719 }
720 });
721
722 if (DEBUG_COMPRESSION && files != null && files.length > 0) {
723 Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files));
724 }
725
726 return files;
727 }
728
729 public static boolean compressedFileExists(String codePath) {
730 final File[] compressedFiles = getCompressedFiles(codePath);
731 return compressedFiles != null && compressedFiles.length > 0;
732 }
Sudheer Shankaa05a9942018-08-29 23:28:23 -0700733
734 /**
735 * Parse given package and return minimal details.
736 */
737 public static PackageInfoLite getMinimalPackageInfo(Context context, String packagePath,
738 int flags, String abiOverride) {
739 final PackageInfoLite ret = new PackageInfoLite();
740 if (packagePath == null) {
741 Slog.i(TAG, "Invalid package file " + packagePath);
742 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
743 return ret;
744 }
745
746 final File packageFile = new File(packagePath);
747 final PackageParser.PackageLite pkg;
748 final long sizeBytes;
749 try {
750 pkg = PackageParser.parsePackageLite(packageFile, 0);
751 sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
752 } catch (PackageParserException | IOException e) {
753 Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
754
755 if (!packageFile.exists()) {
756 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
757 } else {
758 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
759 }
760
761 return ret;
762 }
763
764 final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
765 pkg.packageName, pkg.installLocation, sizeBytes, flags);
766
767 ret.packageName = pkg.packageName;
768 ret.splitNames = pkg.splitNames;
769 ret.versionCode = pkg.versionCode;
770 ret.versionCodeMajor = pkg.versionCodeMajor;
771 ret.baseRevisionCode = pkg.baseRevisionCode;
772 ret.splitRevisionCodes = pkg.splitRevisionCodes;
773 ret.installLocation = pkg.installLocation;
774 ret.verifiers = pkg.verifiers;
775 ret.recommendedInstallLocation = recommendedInstallLocation;
776 ret.multiArch = pkg.multiArch;
777
778 return ret;
779 }
780
781 /**
782 * Calculate estimated footprint of given package post-installation.
783 *
784 * @return -1 if there's some error calculating the size, otherwise installed size of the
785 * package.
786 */
787 public static long calculateInstalledSize(String packagePath, String abiOverride) {
788 final File packageFile = new File(packagePath);
789 final PackageParser.PackageLite pkg;
790 try {
791 pkg = PackageParser.parsePackageLite(packageFile, 0);
792 return PackageHelper.calculateInstalledSize(pkg, abiOverride);
793 } catch (PackageParserException | IOException e) {
794 Slog.w(TAG, "Failed to calculate installed size: " + e);
795 return -1;
796 }
797 }
798
799 /**
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000800 * Checks whenever downgrade of an app is permitted.
801 *
802 * @param installFlags flags of the current install.
803 * @param applicationFlags flags of the currently installed version of the app.
804 * @return {@code true} if downgrade is permitted according to the {@code installFlags} and
805 * {@code applicationFlags}.
806 */
807 public static boolean isDowngradePermitted(int installFlags, int applicationFlags) {
808 // If installed, the package will get access to data left on the device by its
Nikita Ioffea1111ff2019-03-04 12:26:14 +0000809 // predecessor. As a security measure, this is permitted only if this is not a
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000810 // version downgrade or if the predecessor package is marked as debuggable and
811 // a downgrade is explicitly requested.
812 //
813 // On debuggable platform builds, downgrades are permitted even for
814 // non-debuggable packages to make testing easier. Debuggable platform builds do
815 // not offer security guarantees and thus it's OK to disable some security
816 // mechanisms to make debugging/testing easier on those builds. However, even on
817 // debuggable builds downgrades of packages are permitted only if requested via
818 // installFlags. This is because we aim to keep the behavior of debuggable
819 // platform builds as close as possible to the behavior of non-debuggable
820 // platform builds.
Nikita Ioffea1111ff2019-03-04 12:26:14 +0000821 //
822 // In case of user builds, downgrade is permitted only for the system server initiated
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000823 // sessions. This is enforced by INSTALL_ALLOW_DOWNGRADE flag parameter.
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000824 final boolean downgradeRequested =
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000825 (installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0;
Nikita Ioffea1111ff2019-03-04 12:26:14 +0000826 if (!downgradeRequested) {
827 return false;
828 }
829 final boolean isDebuggable =
830 Build.IS_DEBUGGABLE || ((applicationFlags
831 & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
832 if (isDebuggable) {
833 return true;
834 }
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000835 return (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000836 }
837
838 /**
Sudheer Shankaa05a9942018-08-29 23:28:23 -0700839 * Copy package to the target location.
840 *
841 * @param packagePath absolute path to the package to be copied. Can be
842 * a single monolithic APK file or a cluster directory
843 * containing one or more APKs.
844 * @return returns status code according to those in
845 * {@link PackageManager}
846 */
847 public static int copyPackage(String packagePath, File targetDir) {
848 if (packagePath == null) {
849 return PackageManager.INSTALL_FAILED_INVALID_URI;
850 }
851
852 try {
853 final File packageFile = new File(packagePath);
854 final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
855 copyFile(pkg.baseCodePath, targetDir, "base.apk");
856 if (!ArrayUtils.isEmpty(pkg.splitNames)) {
857 for (int i = 0; i < pkg.splitNames.length; i++) {
858 copyFile(pkg.splitCodePaths[i], targetDir,
859 "split_" + pkg.splitNames[i] + ".apk");
860 }
861 }
862 return PackageManager.INSTALL_SUCCEEDED;
863 } catch (PackageParserException | IOException | ErrnoException e) {
864 Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
865 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
866 }
867 }
868
869 private static void copyFile(String sourcePath, File targetDir, String targetName)
870 throws ErrnoException, IOException {
871 if (!FileUtils.isValidExtFilename(targetName)) {
872 throw new IllegalArgumentException("Invalid filename: " + targetName);
873 }
874 Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
875
876 final File targetFile = new File(targetDir, targetName);
877 final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
878 O_RDWR | O_CREAT, 0644);
879 Os.chmod(targetFile.getAbsolutePath(), 0644);
880 FileInputStream source = null;
881 try {
882 source = new FileInputStream(sourcePath);
883 FileUtils.copy(source.getFD(), targetFd);
884 } finally {
885 IoUtils.closeQuietly(source);
886 }
887 }
Narayan Kamath6d99f792016-05-16 17:34:48 +0100888}