blob: 1fcb37f7aecc7298d6c86ea34615c9de6a905af3 [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;
21import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
22import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
23import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
24import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
25import static com.android.server.pm.PackageManagerService.TAG;
26import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
27
28import com.android.internal.content.NativeLibraryHelper;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070029import com.android.internal.util.FastPrintWriter;
30import com.android.server.EventLogTags;
Shubham Ajmera246dccf2017-05-24 17:46:36 -070031import com.android.server.pm.dex.DexManager;
32import com.android.server.pm.dex.PackageDexUsage;
33
Andreas Gamped3e07d42016-09-06 18:22:19 -070034import android.annotation.NonNull;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070035import android.annotation.Nullable;
David Brazdil6b4736d2016-02-04 11:54:17 +000036import android.app.AppGlobals;
37import android.content.Intent;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070038import android.content.pm.PackageManager;
David Brazdil6b4736d2016-02-04 11:54:17 +000039import android.content.pm.PackageParser;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070040import android.content.pm.PackageParser.PackageParserException;
David Brazdil6b4736d2016-02-04 11:54:17 +000041import android.content.pm.ResolveInfo;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070042import android.content.pm.Signature;
Calin Juravle03181622016-12-01 17:53:07 +000043import android.os.Build;
Todd Kennedy0eb97382017-10-03 16:57:22 -070044import android.os.Debug;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070045import android.os.Environment;
46import android.os.FileUtils;
Todd Kennedy0eb97382017-10-03 16:57:22 -070047import android.os.Process;
David Brazdil6b4736d2016-02-04 11:54:17 +000048import android.os.RemoteException;
Victor Hsieh0c8f2e02017-10-06 14:44:52 -070049import android.os.SystemProperties;
Jeff Sharkeyd5896632016-03-04 16:16:00 -070050import android.os.UserHandle;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070051import android.service.pm.PackageServiceDumpProto;
Narayan Kamath6d99f792016-05-16 17:34:48 +010052import android.system.ErrnoException;
Tobias Thierer96aac9b32017-10-17 20:26:20 +010053import android.system.Os;
David Brazdil6b4736d2016-02-04 11:54:17 +000054import android.util.ArraySet;
55import android.util.Log;
Daniel Cashman5cdda342018-01-19 07:22:52 -080056import android.util.PackageUtils;
Nicolas Geoffray20a894e2017-09-08 13:01:40 +010057import android.util.Slog;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070058import android.util.proto.ProtoOutputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +000059
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070060import dalvik.system.VMRuntime;
61
62import libcore.io.IoUtils;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070063
64import java.io.BufferedReader;
Narayan Kamath6d99f792016-05-16 17:34:48 +010065import java.io.File;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070066import java.io.FileInputStream;
67import java.io.FileOutputStream;
68import java.io.FileReader;
69import java.io.FilenameFilter;
Narayan Kamath6d99f792016-05-16 17:34:48 +010070import java.io.IOException;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070071import java.io.InputStream;
72import java.io.OutputStream;
73import java.io.PrintWriter;
Daniel Cashman5cdda342018-01-19 07:22:52 -080074import java.security.MessageDigest;
75import java.security.NoSuchAlgorithmException;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070076import java.security.cert.CertificateEncodingException;
77import java.security.cert.CertificateException;
78import java.text.SimpleDateFormat;
David Brazdil6b4736d2016-02-04 11:54:17 +000079import java.util.ArrayList;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070080import java.util.Arrays;
David Brazdil6b4736d2016-02-04 11:54:17 +000081import java.util.Collection;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +010082import java.util.Collections;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070083import java.util.Date;
David Brazdil6b4736d2016-02-04 11:54:17 +000084import java.util.LinkedList;
85import java.util.List;
Andreas Gamped3e07d42016-09-06 18:22:19 -070086import java.util.function.Predicate;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070087import java.util.zip.GZIPInputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +000088
David Brazdil6b4736d2016-02-04 11:54:17 +000089/**
90 * Class containing helper methods for the PackageManagerService.
91 *
92 * {@hide}
93 */
94public class PackageManagerServiceUtils {
David Brazdilb62d6902016-02-11 13:36:07 +000095 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
David Brazdil6b4736d2016-02-04 11:54:17 +000096
97 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
98 List<ResolveInfo> ris = null;
99 try {
Jeff Sharkeyd5896632016-03-04 16:16:00 -0700100 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
101 .getList();
David Brazdil6b4736d2016-02-04 11:54:17 +0000102 } catch (RemoteException e) {
103 }
104 ArraySet<String> pkgNames = new ArraySet<String>();
105 if (ris != null) {
106 for (ResolveInfo ri : ris) {
107 pkgNames.add(ri.activityInfo.packageName);
108 }
109 }
110 return pkgNames;
111 }
112
Andreas Gamped3e07d42016-09-06 18:22:19 -0700113 // Sort a list of apps by their last usage, most recently used apps first. The order of
114 // packages without usage data is undefined (but they will be sorted after the packages
115 // that do have usage data).
116 public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs,
117 PackageManagerService packageManagerService) {
118 if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
119 return;
120 }
121
122 Collections.sort(pkgs, (pkg1, pkg2) ->
123 Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(),
124 pkg1.getLatestForegroundPackageUseTimeInMills()));
125 }
126
127 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
128 // package will be removed from {@code packages} and added to {@code result} with its
129 // dependencies. If usage data is available, the positive packages will be sorted by usage
130 // data (with {@code sortTemp} as temporary storage).
131 private static void applyPackageFilter(Predicate<PackageParser.Package> filter,
132 Collection<PackageParser.Package> result,
133 Collection<PackageParser.Package> packages,
134 @NonNull List<PackageParser.Package> sortTemp,
135 PackageManagerService packageManagerService) {
136 for (PackageParser.Package pkg : packages) {
137 if (filter.test(pkg)) {
138 sortTemp.add(pkg);
David Brazdil6b4736d2016-02-04 11:54:17 +0000139 }
140 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700141
142 sortPackagesByUsageDate(sortTemp, packageManagerService);
143 packages.removeAll(sortTemp);
144
145 for (PackageParser.Package pkg : sortTemp) {
146 result.add(pkg);
147
148 Collection<PackageParser.Package> deps =
149 packageManagerService.findSharedNonSystemLibraries(pkg);
150 if (!deps.isEmpty()) {
151 deps.removeAll(result);
152 result.addAll(deps);
153 packages.removeAll(deps);
154 }
David Brazdil6b4736d2016-02-04 11:54:17 +0000155 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700156
157 sortTemp.clear();
David Brazdil6b4736d2016-02-04 11:54:17 +0000158 }
159
160 // Sort apps by importance for dexopt ordering. Important apps are given
161 // more priority in case the device runs out of space.
162 public static List<PackageParser.Package> getPackagesForDexopt(
163 Collection<PackageParser.Package> packages,
164 PackageManagerService packageManagerService) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700165 return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
166 }
167
168 public static List<PackageParser.Package> getPackagesForDexopt(
169 Collection<PackageParser.Package> packages,
170 PackageManagerService packageManagerService,
171 boolean debug) {
David Brazdil6b4736d2016-02-04 11:54:17 +0000172 ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
173 LinkedList<PackageParser.Package> result = new LinkedList<>();
Andreas Gamped3e07d42016-09-06 18:22:19 -0700174 ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
David Brazdil6b4736d2016-02-04 11:54:17 +0000175
176 // Give priority to core apps.
Andreas Gamped3e07d42016-09-06 18:22:19 -0700177 applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp,
178 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000179
180 // Give priority to system apps that listen for pre boot complete.
181 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
Andreas Gamped3e07d42016-09-06 18:22:19 -0700182 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
183 applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs,
184 sortTemp, packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000185
David Brazdil90e26992016-04-18 14:08:52 +0100186 // Give priority to apps used by other apps.
Calin Juravle3b74c412017-08-03 19:48:37 -0700187 DexManager dexManager = packageManagerService.getDexManager();
Calin Juravle07b6eab2017-03-01 19:55:35 -0800188 applyPackageFilter((pkg) ->
Calin Juravle52a452c2017-08-04 01:42:17 -0700189 dexManager.getPackageUseInfoOrDefault(pkg.packageName)
190 .isAnyCodePathUsedByOtherApps(),
191 result, remainingPkgs, sortTemp, packageManagerService);
David Brazdil90e26992016-04-18 14:08:52 +0100192
David Brazdil6b4736d2016-02-04 11:54:17 +0000193 // Filter out packages that aren't recently used, add all remaining apps.
194 // TODO: add a property to control this?
Andreas Gamped3e07d42016-09-06 18:22:19 -0700195 Predicate<PackageParser.Package> remainingPredicate;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100196 if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700197 if (debug) {
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100198 Log.i(TAG, "Looking at historical package use");
199 }
200 // Get the package that was used last.
201 PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
202 Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
203 pkg2.getLatestForegroundPackageUseTimeInMills()));
Andreas Gampe069a91c2018-09-05 16:11:50 -0700204 if (debug) {
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100205 Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
206 }
207 long estimatedPreviousSystemUseTime =
208 lastUsed.getLatestForegroundPackageUseTimeInMills();
209 // Be defensive if for some reason package usage has bogus data.
210 if (estimatedPreviousSystemUseTime != 0) {
Andreas Gamped3e07d42016-09-06 18:22:19 -0700211 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
212 remainingPredicate =
213 (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
214 } else {
215 // No meaningful historical info. Take all.
216 remainingPredicate = (pkg) -> true;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100217 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700218 sortPackagesByUsageDate(remainingPkgs, packageManagerService);
219 } else {
220 // No historical info. Take all.
221 remainingPredicate = (pkg) -> true;
David Brazdil6b4736d2016-02-04 11:54:17 +0000222 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700223 applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
224 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000225
Andreas Gampe069a91c2018-09-05 16:11:50 -0700226 if (debug) {
Andreas Gampedab38e02016-09-09 17:50:20 -0700227 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
228 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
David Brazdil6b4736d2016-02-04 11:54:17 +0000229 }
230
231 return result;
232 }
Narayan Kamath6d99f792016-05-16 17:34:48 +0100233
234 /**
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700235 * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
236 * Package is considered active, if:
237 * 1) It was active in foreground.
238 * 2) It was active in background and also used by other apps.
239 *
240 * If it doesn't have sufficient information about the package, it return <code>false</code>.
241 */
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700242 public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700243 long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
244 long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
245
246 if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
247 return false;
248 }
249
250 // If the app was active in foreground during the threshold period.
251 boolean isActiveInForeground = (currentTimeInMillis
252 - latestForegroundPackageUseTimeInMillis)
253 < thresholdTimeinMillis;
254
255 if (isActiveInForeground) {
256 return false;
257 }
258
259 // If the app was active in background during the threshold period and was used
260 // by other packages.
261 boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
262 - latestPackageUseTimeInMillis)
263 < thresholdTimeinMillis)
Calin Juravle52a452c2017-08-04 01:42:17 -0700264 && packageUseInfo.isAnyCodePathUsedByOtherApps();
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700265
266 return !isActiveInBackgroundAndUsedByOtherPackages;
267 }
268
269 /**
Narayan Kamath6d99f792016-05-16 17:34:48 +0100270 * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
271 * semantics.
272 */
273 public static String realpath(File path) throws IOException {
274 try {
Tobias Thierer96aac9b32017-10-17 20:26:20 +0100275 return Os.realpath(path.getAbsolutePath());
Narayan Kamath6d99f792016-05-16 17:34:48 +0100276 } catch (ErrnoException ee) {
277 throw ee.rethrowAsIOException();
278 }
279 }
Andreas Gampedab38e02016-09-09 17:50:20 -0700280
281 public static String packagesToString(Collection<PackageParser.Package> c) {
282 StringBuilder sb = new StringBuilder();
283 for (PackageParser.Package pkg : c) {
284 if (sb.length() > 0) {
285 sb.append(", ");
286 }
287 sb.append(pkg.packageName);
288 }
289 return sb.toString();
290 }
Calin Juravle03181622016-12-01 17:53:07 +0000291
292 /**
293 * Verifies that the given string {@code isa} is a valid supported isa on
294 * the running device.
295 */
296 public static boolean checkISA(String isa) {
297 for (String abi : Build.SUPPORTED_ABIS) {
298 if (VMRuntime.getInstructionSet(abi).equals(isa)) {
299 return true;
300 }
301 }
302 return false;
303 }
Nicolas Geoffray20a894e2017-09-08 13:01:40 +0100304
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800305 public static long getLastModifiedTime(PackageParser.Package pkg) {
306 final File srcFile = new File(pkg.codePath);
307 if (!srcFile.isDirectory()) {
308 return srcFile.lastModified();
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700309 }
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800310 final File baseFile = new File(pkg.baseCodePath);
311 long maxModifiedTime = baseFile.lastModified();
312 if (pkg.splitCodePaths != null) {
313 for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
314 final File splitFile = new File(pkg.splitCodePaths[i]);
315 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
316 }
317 }
318 return maxModifiedTime;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700319 }
320
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700321 private static File getSettingsProblemFile() {
322 File dataDir = Environment.getDataDirectory();
323 File systemDir = new File(dataDir, "system");
324 File fname = new File(systemDir, "uiderrors.txt");
325 return fname;
326 }
327
328 public static void dumpCriticalInfo(ProtoOutputStream proto) {
329 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
330 String line = null;
331 while ((line = in.readLine()) != null) {
332 if (line.contains("ignored: updated version")) continue;
333 proto.write(PackageServiceDumpProto.MESSAGES, line);
334 }
335 } catch (IOException ignored) {
336 }
337 }
338
339 public static void dumpCriticalInfo(PrintWriter pw, String msg) {
340 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
341 String line = null;
342 while ((line = in.readLine()) != null) {
343 if (line.contains("ignored: updated version")) continue;
344 if (msg != null) {
345 pw.print(msg);
346 }
347 pw.println(line);
348 }
349 } catch (IOException ignored) {
350 }
351 }
352
353 public static void logCriticalInfo(int priority, String msg) {
354 Slog.println(priority, TAG, msg);
355 EventLogTags.writePmCriticalInfo(msg);
356 try {
357 File fname = getSettingsProblemFile();
358 FileOutputStream out = new FileOutputStream(fname, true);
359 PrintWriter pw = new FastPrintWriter(out);
360 SimpleDateFormat formatter = new SimpleDateFormat();
361 String dateString = formatter.format(new Date(System.currentTimeMillis()));
362 pw.println(dateString + ": " + msg);
363 pw.close();
364 FileUtils.setPermissions(
365 fname.toString(),
366 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH,
367 -1, -1);
368 } catch (java.io.IOException e) {
369 }
370 }
371
Todd Kennedy0eb97382017-10-03 16:57:22 -0700372 public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
373 if (callingUid == Process.SHELL_UID) {
374 if (userHandle >= 0
375 && PackageManagerService.sUserManager.hasUserRestriction(
376 restriction, userHandle)) {
377 throw new SecurityException("Shell does not have permission to access user "
378 + userHandle);
379 } else if (userHandle < 0) {
380 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
381 + userHandle + "\n\t" + Debug.getCallers(3));
382 }
383 }
384 }
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700385
386 /**
387 * Derive the value of the {@code cpuAbiOverride} based on the provided
388 * value and an optional stored value from the package settings.
389 */
390 public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
391 String cpuAbiOverride = null;
392 if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
393 cpuAbiOverride = null;
394 } else if (abiOverride != null) {
395 cpuAbiOverride = abiOverride;
396 } else if (settings != null) {
397 cpuAbiOverride = settings.cpuAbiOverrideString;
398 }
399 return cpuAbiOverride;
400 }
401
402 /**
403 * Compares two sets of signatures. Returns:
404 * <br />
405 * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
406 * <br />
407 * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
408 * <br />
409 * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
410 * <br />
411 * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
412 * <br />
413 * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
414 */
415 public static int compareSignatures(Signature[] s1, Signature[] s2) {
416 if (s1 == null) {
417 return s2 == null
418 ? PackageManager.SIGNATURE_NEITHER_SIGNED
419 : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
420 }
421
422 if (s2 == null) {
423 return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
424 }
425
426 if (s1.length != s2.length) {
427 return PackageManager.SIGNATURE_NO_MATCH;
428 }
429
430 // Since both signature sets are of size 1, we can compare without HashSets.
431 if (s1.length == 1) {
432 return s1[0].equals(s2[0]) ?
433 PackageManager.SIGNATURE_MATCH :
434 PackageManager.SIGNATURE_NO_MATCH;
435 }
436
437 ArraySet<Signature> set1 = new ArraySet<Signature>();
438 for (Signature sig : s1) {
439 set1.add(sig);
440 }
441 ArraySet<Signature> set2 = new ArraySet<Signature>();
442 for (Signature sig : s2) {
443 set2.add(sig);
444 }
445 // Make sure s2 contains all signatures in s1.
446 if (set1.equals(set2)) {
447 return PackageManager.SIGNATURE_MATCH;
448 }
449 return PackageManager.SIGNATURE_NO_MATCH;
450 }
451
452 /**
453 * Used for backward compatibility to make sure any packages with
454 * certificate chains get upgraded to the new style. {@code existingSigs}
455 * will be in the old format (since they were stored on disk from before the
456 * system upgrade) and {@code scannedSigs} will be in the newer format.
457 */
458 private static boolean matchSignaturesCompat(String packageName,
Patrick Baumann420d58a2017-12-19 10:17:21 -0800459 PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700460 ArraySet<Signature> existingSet = new ArraySet<Signature>();
Daniel Cashman77029c52018-01-18 16:19:29 -0800461 for (Signature sig : packageSignatures.mSigningDetails.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700462 existingSet.add(sig);
463 }
464 ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
Patrick Baumann420d58a2017-12-19 10:17:21 -0800465 for (Signature sig : parsedSignatures.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700466 try {
467 Signature[] chainSignatures = sig.getChainSignatures();
468 for (Signature chainSig : chainSignatures) {
469 scannedCompatSet.add(chainSig);
470 }
471 } catch (CertificateEncodingException e) {
472 scannedCompatSet.add(sig);
473 }
474 }
475 // make sure the expanded scanned set contains all signatures in the existing one
476 if (scannedCompatSet.equals(existingSet)) {
477 // migrate the old signatures to the new scheme
Daniel Cashman77029c52018-01-18 16:19:29 -0800478 packageSignatures.mSigningDetails = parsedSignatures;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700479 return true;
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800480 } else if (parsedSignatures.hasPastSigningCertificates()) {
481
482 // well this sucks: the parsed package has probably rotated signing certificates, but
483 // we don't have enough information to determine if the new signing certificate was
484 // blessed by the old one
485 logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing "
486 + "certificate chain. Unable to install newer version with rotated signing "
487 + "certificate.");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700488 }
489 return false;
490 }
491
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800492 private static boolean matchSignaturesRecover(
493 String packageName,
494 PackageParser.SigningDetails existingSignatures,
495 PackageParser.SigningDetails parsedSignatures,
496 @PackageParser.SigningDetails.CertCapabilities int flags) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700497 String msg = null;
498 try {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800499 if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
500 logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
501 + packageName);
502 return true;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700503 }
504 } catch (CertificateException e) {
505 msg = e.getMessage();
506 }
507 logCriticalInfo(Log.INFO,
508 "Failed to recover certificates for " + packageName + ": " + msg);
509 return false;
510 }
511
512 /**
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700513 * Make sure the updated priv app is signed with the same key as the original APK file on the
514 * /system partition.
515 *
516 * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
517 * and is not tamperproof.
518 */
519 private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
520 PackageSetting disabledPkgSetting) {
521 try {
Victor Hsieh5f761242018-01-20 10:30:12 -0800522 PackageParser.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800523 if (pkgSetting.signatures.mSigningDetails.checkCapability(
524 disabledPkgSetting.signatures.mSigningDetails,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700525 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
526 || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
527 pkgSetting.signatures.mSigningDetails,
528 PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800529 return true;
530 } else {
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700531 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
532 pkgSetting.name);
533 return false;
534 }
535 } catch (PackageParserException e) {
536 logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " +
537 e.getMessage());
538 return false;
539 }
Daniel Cashman5cdda342018-01-19 07:22:52 -0800540 }
541
Victor Hsieh55f14992018-01-13 14:12:59 -0800542 /** Returns true if APK Verity is enabled. */
543 static boolean isApkVerityEnabled() {
544 return SystemProperties.getInt("ro.apk_verity.mode", 0) != 0;
545 }
546
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700547 /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
548 static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
Victor Hsieh55f14992018-01-13 14:12:59 -0800549 return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled();
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700550 }
551
552 /**
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700553 * Verifies that signatures match.
554 * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
555 * @throws PackageManagerException if the signatures did not match.
556 */
557 public static boolean verifySignatures(PackageSetting pkgSetting,
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700558 PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
559 boolean compareCompat, boolean compareRecover)
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700560 throws PackageManagerException {
561 final String packageName = pkgSetting.name;
562 boolean compatMatch = false;
Daniel Cashman77029c52018-01-18 16:19:29 -0800563 if (pkgSetting.signatures.mSigningDetails.signatures != null) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800564
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700565 // Already existing package. Make sure signatures match
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800566 boolean match = parsedSignatures.checkCapability(
567 pkgSetting.signatures.mSigningDetails,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700568 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
569 || pkgSetting.signatures.mSigningDetails.checkCapability(
570 parsedSignatures,
571 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700572 if (!match && compareCompat) {
Patrick Baumann420d58a2017-12-19 10:17:21 -0800573 match = matchSignaturesCompat(packageName, pkgSetting.signatures,
574 parsedSignatures);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700575 compatMatch = match;
576 }
577 if (!match && compareRecover) {
578 match = matchSignaturesRecover(
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800579 packageName,
580 pkgSetting.signatures.mSigningDetails,
581 parsedSignatures,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700582 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
583 || matchSignaturesRecover(
584 packageName,
585 parsedSignatures,
586 pkgSetting.signatures.mSigningDetails,
587 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700588 }
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700589
590 if (!match && isApkVerificationForced(disabledPkgSetting)) {
591 match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
592 }
593
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700594 if (!match) {
595 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
596 "Package " + packageName +
Todd Kennedy96cb94b2017-11-29 13:17:12 -0800597 " signatures do not match previously installed version; ignoring!");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700598 }
599 }
600 // Check for shared user signatures
Daniel Cashman77029c52018-01-18 16:19:29 -0800601 if (pkgSetting.sharedUser != null
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800602 && pkgSetting.sharedUser.signatures.mSigningDetails
603 != PackageParser.SigningDetails.UNKNOWN) {
604
605 // Already existing package. Make sure signatures match. In case of signing certificate
606 // rotation, the packages with newer certs need to be ok with being sharedUserId with
607 // the older ones. We check to see if either the new package is signed by an older cert
608 // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
609 // with being sharedUser with the existing signing cert.
Daniel Cashman77029c52018-01-18 16:19:29 -0800610 boolean match =
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800611 parsedSignatures.checkCapability(
612 pkgSetting.sharedUser.signatures.mSigningDetails,
613 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
614 || pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(
615 parsedSignatures,
616 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy3e654842017-11-29 13:58:53 -0800617 if (!match && compareCompat) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700618 match = matchSignaturesCompat(
619 packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
620 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800621 if (!match && compareRecover) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800622 match =
623 matchSignaturesRecover(packageName,
624 pkgSetting.sharedUser.signatures.mSigningDetails,
625 parsedSignatures,
626 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
627 || matchSignaturesRecover(packageName,
628 parsedSignatures,
629 pkgSetting.sharedUser.signatures.mSigningDetails,
630 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700631 compatMatch |= match;
632 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800633 if (!match) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700634 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
635 "Package " + packageName
636 + " has no signatures that match those in shared user "
637 + pkgSetting.sharedUser.name + "; ignoring!");
638 }
639 }
640 return compatMatch;
641 }
642
643 public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
644 if (DEBUG_COMPRESSION) {
645 Slog.i(TAG, "Decompress file"
646 + "; src: " + srcFile.getAbsolutePath()
647 + ", dst: " + dstFile.getAbsolutePath());
648 }
649 try (
650 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
651 OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
652 ) {
Jeff Sharkeyb18f8992018-01-31 21:47:09 -0700653 FileUtils.copy(fileIn, fileOut);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700654 Os.chmod(dstFile.getAbsolutePath(), 0644);
655 return PackageManager.INSTALL_SUCCEEDED;
656 } catch (IOException e) {
657 logCriticalInfo(Log.ERROR, "Failed to decompress file"
658 + "; src: " + srcFile.getAbsolutePath()
659 + ", dst: " + dstFile.getAbsolutePath());
660 }
661 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
662 }
663
664 public static File[] getCompressedFiles(String codePath) {
665 final File stubCodePath = new File(codePath);
666 final String stubName = stubCodePath.getName();
667
668 // The layout of a compressed package on a given partition is as follows :
669 //
670 // Compressed artifacts:
671 //
672 // /partition/ModuleName/foo.gz
673 // /partation/ModuleName/bar.gz
674 //
675 // Stub artifact:
676 //
677 // /partition/ModuleName-Stub/ModuleName-Stub.apk
678 //
679 // In other words, stub is on the same partition as the compressed artifacts
680 // and in a directory that's suffixed with "-Stub".
681 int idx = stubName.lastIndexOf(STUB_SUFFIX);
682 if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) {
683 return null;
684 }
685
686 final File stubParentDir = stubCodePath.getParentFile();
687 if (stubParentDir == null) {
688 Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath);
689 return null;
690 }
691
692 final File compressedPath = new File(stubParentDir, stubName.substring(0, idx));
693 final File[] files = compressedPath.listFiles(new FilenameFilter() {
694 @Override
695 public boolean accept(File dir, String name) {
696 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
697 }
698 });
699
700 if (DEBUG_COMPRESSION && files != null && files.length > 0) {
701 Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files));
702 }
703
704 return files;
705 }
706
707 public static boolean compressedFileExists(String codePath) {
708 final File[] compressedFiles = getCompressedFiles(codePath);
709 return compressedFiles != null && compressedFiles.length > 0;
710 }
Narayan Kamath6d99f792016-05-16 17:34:48 +0100711}