blob: 5c175a6ef8475616585452f1804b5c49f7681e5c [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;
Winson14ff7172019-10-23 10:42:27 -070037import android.content.pm.PackageManagerInternal;
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;
Patrick Baumann2f2fd712019-07-31 15:18:53 -070050import android.os.UserManagerInternal;
Todd Kennedy75dd0e62020-04-01 10:48:57 -070051import android.os.incremental.IncrementalManager;
52import android.os.incremental.V4Signature;
53import android.os.incremental.V4Signature.HashingInfo;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070054import android.service.pm.PackageServiceDumpProto;
Narayan Kamath6d99f792016-05-16 17:34:48 +010055import android.system.ErrnoException;
Tobias Thierer96aac9b32017-10-17 20:26:20 +010056import android.system.Os;
David Brazdil6b4736d2016-02-04 11:54:17 +000057import android.util.ArraySet;
58import android.util.Log;
Nicolas Geoffray20a894e2017-09-08 13:01:40 +010059import android.util.Slog;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070060import android.util.proto.ProtoOutputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +000061
Sudheer Shankaa05a9942018-08-29 23:28:23 -070062import com.android.internal.content.NativeLibraryHelper;
63import com.android.internal.content.PackageHelper;
64import com.android.internal.util.ArrayUtils;
65import com.android.internal.util.FastPrintWriter;
Todd Kennedy75dd0e62020-04-01 10:48:57 -070066import com.android.internal.util.HexDump;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070067import com.android.server.EventLogTags;
68import com.android.server.pm.dex.DexManager;
69import com.android.server.pm.dex.PackageDexUsage;
Winsone23ae202020-01-24 11:56:44 -080070import com.android.server.pm.parsing.pkg.AndroidPackage;
Todd Kennedy7e3dd3a2019-07-08 10:34:29 -070071import com.android.server.pm.permission.PermissionsState;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070072
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070073import dalvik.system.VMRuntime;
74
75import libcore.io.IoUtils;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070076
77import java.io.BufferedReader;
Narayan Kamath6d99f792016-05-16 17:34:48 +010078import java.io.File;
Sudheer Shankaa05a9942018-08-29 23:28:23 -070079import java.io.FileDescriptor;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070080import java.io.FileInputStream;
81import java.io.FileOutputStream;
82import java.io.FileReader;
83import java.io.FilenameFilter;
Narayan Kamath6d99f792016-05-16 17:34:48 +010084import java.io.IOException;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070085import java.io.InputStream;
86import java.io.OutputStream;
87import java.io.PrintWriter;
Songchun Fanf56f1c52020-01-22 17:10:50 -080088import java.nio.file.Path;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070089import java.security.cert.CertificateEncodingException;
90import java.security.cert.CertificateException;
91import java.text.SimpleDateFormat;
David Brazdil6b4736d2016-02-04 11:54:17 +000092import java.util.ArrayList;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070093import java.util.Arrays;
David Brazdil6b4736d2016-02-04 11:54:17 +000094import java.util.Collection;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +010095import java.util.Collections;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -070096import java.util.Date;
David Brazdil6b4736d2016-02-04 11:54:17 +000097import java.util.LinkedList;
98import java.util.List;
Andreas Gamped3e07d42016-09-06 18:22:19 -070099import java.util.function.Predicate;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700100import java.util.zip.GZIPInputStream;
David Brazdil6b4736d2016-02-04 11:54:17 +0000101
David Brazdil6b4736d2016-02-04 11:54:17 +0000102/**
103 * Class containing helper methods for the PackageManagerService.
104 *
105 * {@hide}
106 */
107public class PackageManagerServiceUtils {
David Brazdilb62d6902016-02-11 13:36:07 +0000108 private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
David Brazdil6b4736d2016-02-04 11:54:17 +0000109
Winsone0756292020-01-31 12:21:54 -0800110 public final static Predicate<PackageSetting> REMOVE_IF_NULL_PKG =
111 pkgSetting -> pkgSetting.pkg == null;
112
David Brazdil6b4736d2016-02-04 11:54:17 +0000113 private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
114 List<ResolveInfo> ris = null;
115 try {
Jeff Sharkeyd5896632016-03-04 16:16:00 -0700116 ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
117 .getList();
David Brazdil6b4736d2016-02-04 11:54:17 +0000118 } catch (RemoteException e) {
119 }
120 ArraySet<String> pkgNames = new ArraySet<String>();
121 if (ris != null) {
122 for (ResolveInfo ri : ris) {
123 pkgNames.add(ri.activityInfo.packageName);
124 }
125 }
126 return pkgNames;
127 }
128
Andreas Gamped3e07d42016-09-06 18:22:19 -0700129 // Sort a list of apps by their last usage, most recently used apps first. The order of
130 // packages without usage data is undefined (but they will be sorted after the packages
131 // that do have usage data).
Winsone0756292020-01-31 12:21:54 -0800132 public static void sortPackagesByUsageDate(List<PackageSetting> pkgSettings,
Andreas Gamped3e07d42016-09-06 18:22:19 -0700133 PackageManagerService packageManagerService) {
134 if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
135 return;
136 }
137
Winsone0756292020-01-31 12:21:54 -0800138 Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
139 Long.compare(
140 pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills(),
141 pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills())
142 );
Andreas Gamped3e07d42016-09-06 18:22:19 -0700143 }
144
145 // Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
146 // package will be removed from {@code packages} and added to {@code result} with its
147 // dependencies. If usage data is available, the positive packages will be sorted by usage
148 // data (with {@code sortTemp} as temporary storage).
Winsone0756292020-01-31 12:21:54 -0800149 private static void applyPackageFilter(
150 Predicate<PackageSetting> filter,
151 Collection<PackageSetting> result,
152 Collection<PackageSetting> packages,
153 @NonNull List<PackageSetting> sortTemp,
Andreas Gamped3e07d42016-09-06 18:22:19 -0700154 PackageManagerService packageManagerService) {
Winsone0756292020-01-31 12:21:54 -0800155 for (PackageSetting pkgSetting : packages) {
156 if (filter.test(pkgSetting)) {
157 sortTemp.add(pkgSetting);
David Brazdil6b4736d2016-02-04 11:54:17 +0000158 }
159 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700160
161 sortPackagesByUsageDate(sortTemp, packageManagerService);
162 packages.removeAll(sortTemp);
163
Winsone0756292020-01-31 12:21:54 -0800164 for (PackageSetting pkgSetting : sortTemp) {
165 result.add(pkgSetting);
Andreas Gamped3e07d42016-09-06 18:22:19 -0700166
Winsone0756292020-01-31 12:21:54 -0800167 List<PackageSetting> deps =
168 packageManagerService.findSharedNonSystemLibraries(pkgSetting);
Andreas Gamped3e07d42016-09-06 18:22:19 -0700169 if (!deps.isEmpty()) {
170 deps.removeAll(result);
171 result.addAll(deps);
172 packages.removeAll(deps);
173 }
David Brazdil6b4736d2016-02-04 11:54:17 +0000174 }
Andreas Gamped3e07d42016-09-06 18:22:19 -0700175
176 sortTemp.clear();
David Brazdil6b4736d2016-02-04 11:54:17 +0000177 }
178
179 // Sort apps by importance for dexopt ordering. Important apps are given
180 // more priority in case the device runs out of space.
Winsone0756292020-01-31 12:21:54 -0800181 public static List<PackageSetting> getPackagesForDexopt(
182 Collection<PackageSetting> packages,
David Brazdil6b4736d2016-02-04 11:54:17 +0000183 PackageManagerService packageManagerService) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700184 return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
185 }
186
Winsone0756292020-01-31 12:21:54 -0800187 public static List<PackageSetting> getPackagesForDexopt(
188 Collection<PackageSetting> pkgSettings,
Andreas Gampe069a91c2018-09-05 16:11:50 -0700189 PackageManagerService packageManagerService,
190 boolean debug) {
Winsone0756292020-01-31 12:21:54 -0800191 List<PackageSetting> result = new LinkedList<>();
192 ArrayList<PackageSetting> remainingPkgSettings = new ArrayList<>(pkgSettings);
193
194 // First, remove all settings without available packages
195 remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
196
197 ArrayList<PackageSetting> sortTemp = new ArrayList<>(remainingPkgSettings.size());
David Brazdil6b4736d2016-02-04 11:54:17 +0000198
199 // Give priority to core apps.
Winsone0756292020-01-31 12:21:54 -0800200 applyPackageFilter(pkgSetting -> pkgSetting.pkg.isCoreApp(), result, remainingPkgSettings, sortTemp,
Andreas Gamped3e07d42016-09-06 18:22:19 -0700201 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000202
203 // Give priority to system apps that listen for pre boot complete.
204 Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
Andreas Gamped3e07d42016-09-06 18:22:19 -0700205 final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
Winsone0756292020-01-31 12:21:54 -0800206 applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.name), result,
207 remainingPkgSettings, sortTemp, packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000208
David Brazdil90e26992016-04-18 14:08:52 +0100209 // Give priority to apps used by other apps.
Calin Juravle3b74c412017-08-03 19:48:37 -0700210 DexManager dexManager = packageManagerService.getDexManager();
Winsone0756292020-01-31 12:21:54 -0800211 applyPackageFilter(pkgSetting ->
212 dexManager.getPackageUseInfoOrDefault(pkgSetting.name)
Calin Juravle52a452c2017-08-04 01:42:17 -0700213 .isAnyCodePathUsedByOtherApps(),
Winsone0756292020-01-31 12:21:54 -0800214 result, remainingPkgSettings, sortTemp, packageManagerService);
David Brazdil90e26992016-04-18 14:08:52 +0100215
David Brazdil6b4736d2016-02-04 11:54:17 +0000216 // Filter out packages that aren't recently used, add all remaining apps.
217 // TODO: add a property to control this?
Winsone0756292020-01-31 12:21:54 -0800218 Predicate<PackageSetting> remainingPredicate;
219 if (!remainingPkgSettings.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
Andreas Gampe069a91c2018-09-05 16:11:50 -0700220 if (debug) {
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100221 Log.i(TAG, "Looking at historical package use");
222 }
223 // Get the package that was used last.
Winsone0756292020-01-31 12:21:54 -0800224 PackageSetting lastUsed = Collections.max(remainingPkgSettings,
225 (pkgSetting1, pkgSetting2) -> Long.compare(
226 pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills(),
227 pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills()));
Andreas Gampe069a91c2018-09-05 16:11:50 -0700228 if (debug) {
Winsone0756292020-01-31 12:21:54 -0800229 Log.i(TAG, "Taking package " + lastUsed.name
Winson14ff7172019-10-23 10:42:27 -0700230 + " as reference in time use");
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100231 }
Winsone0756292020-01-31 12:21:54 -0800232 long estimatedPreviousSystemUseTime = lastUsed.getPkgState()
233 .getLatestForegroundPackageUseTimeInMills();
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100234 // Be defensive if for some reason package usage has bogus data.
235 if (estimatedPreviousSystemUseTime != 0) {
Andreas Gamped3e07d42016-09-06 18:22:19 -0700236 final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
Winsone0756292020-01-31 12:21:54 -0800237 remainingPredicate = pkgSetting -> pkgSetting.getPkgState()
238 .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
Andreas Gamped3e07d42016-09-06 18:22:19 -0700239 } else {
240 // No meaningful historical info. Take all.
Winsone0756292020-01-31 12:21:54 -0800241 remainingPredicate = pkgSetting -> true;
Nicolas Geoffrayfa78b212016-05-26 14:20:49 +0100242 }
Winsone0756292020-01-31 12:21:54 -0800243 sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
Andreas Gamped3e07d42016-09-06 18:22:19 -0700244 } else {
245 // No historical info. Take all.
Winsone0756292020-01-31 12:21:54 -0800246 remainingPredicate = pkgSetting -> true;
David Brazdil6b4736d2016-02-04 11:54:17 +0000247 }
Winsone0756292020-01-31 12:21:54 -0800248 applyPackageFilter(remainingPredicate, result, remainingPkgSettings, sortTemp,
Andreas Gamped3e07d42016-09-06 18:22:19 -0700249 packageManagerService);
David Brazdil6b4736d2016-02-04 11:54:17 +0000250
Andreas Gampe069a91c2018-09-05 16:11:50 -0700251 if (debug) {
Andreas Gampedab38e02016-09-09 17:50:20 -0700252 Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
Winsone0756292020-01-31 12:21:54 -0800253 Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
David Brazdil6b4736d2016-02-04 11:54:17 +0000254 }
255
256 return result;
257 }
Narayan Kamath6d99f792016-05-16 17:34:48 +0100258
259 /**
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700260 * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
261 * Package is considered active, if:
262 * 1) It was active in foreground.
263 * 2) It was active in background and also used by other apps.
264 *
265 * If it doesn't have sufficient information about the package, it return <code>false</code>.
266 */
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700267 public static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700268 long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
269 long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
270
271 if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
272 return false;
273 }
274
275 // If the app was active in foreground during the threshold period.
276 boolean isActiveInForeground = (currentTimeInMillis
277 - latestForegroundPackageUseTimeInMillis)
278 < thresholdTimeinMillis;
279
280 if (isActiveInForeground) {
281 return false;
282 }
283
284 // If the app was active in background during the threshold period and was used
285 // by other packages.
286 boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
287 - latestPackageUseTimeInMillis)
288 < thresholdTimeinMillis)
Calin Juravle52a452c2017-08-04 01:42:17 -0700289 && packageUseInfo.isAnyCodePathUsedByOtherApps();
Shubham Ajmera246dccf2017-05-24 17:46:36 -0700290
291 return !isActiveInBackgroundAndUsedByOtherPackages;
292 }
293
294 /**
Narayan Kamath6d99f792016-05-16 17:34:48 +0100295 * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
296 * semantics.
297 */
298 public static String realpath(File path) throws IOException {
299 try {
Tobias Thierer96aac9b32017-10-17 20:26:20 +0100300 return Os.realpath(path.getAbsolutePath());
Narayan Kamath6d99f792016-05-16 17:34:48 +0100301 } catch (ErrnoException ee) {
302 throw ee.rethrowAsIOException();
303 }
304 }
Andreas Gampedab38e02016-09-09 17:50:20 -0700305
Winsone0756292020-01-31 12:21:54 -0800306 public static String packagesToString(List<PackageSetting> pkgSettings) {
Andreas Gampedab38e02016-09-09 17:50:20 -0700307 StringBuilder sb = new StringBuilder();
Winsone0756292020-01-31 12:21:54 -0800308 for (int index = 0; index < pkgSettings.size(); index++) {
Andreas Gampedab38e02016-09-09 17:50:20 -0700309 if (sb.length() > 0) {
310 sb.append(", ");
311 }
Winsone0756292020-01-31 12:21:54 -0800312 sb.append(pkgSettings.get(index).name);
Andreas Gampedab38e02016-09-09 17:50:20 -0700313 }
314 return sb.toString();
315 }
Calin Juravle03181622016-12-01 17:53:07 +0000316
317 /**
318 * Verifies that the given string {@code isa} is a valid supported isa on
319 * the running device.
320 */
321 public static boolean checkISA(String isa) {
322 for (String abi : Build.SUPPORTED_ABIS) {
323 if (VMRuntime.getInstructionSet(abi).equals(isa)) {
324 return true;
325 }
326 }
327 return false;
328 }
Nicolas Geoffray20a894e2017-09-08 13:01:40 +0100329
Winson14ff7172019-10-23 10:42:27 -0700330 public static long getLastModifiedTime(AndroidPackage pkg) {
331 final File srcFile = new File(pkg.getCodePath());
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800332 if (!srcFile.isDirectory()) {
333 return srcFile.lastModified();
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700334 }
Winson14ff7172019-10-23 10:42:27 -0700335 final File baseFile = new File(pkg.getBaseCodePath());
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800336 long maxModifiedTime = baseFile.lastModified();
Winson14ff7172019-10-23 10:42:27 -0700337 if (pkg.getSplitCodePaths() != null) {
338 for (int i = pkg.getSplitCodePaths().length - 1; i >=0; --i) {
339 final File splitFile = new File(pkg.getSplitCodePaths()[i]);
Todd Kennedyddaaf4c2017-11-07 10:01:25 -0800340 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
341 }
342 }
343 return maxModifiedTime;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700344 }
345
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700346 private static File getSettingsProblemFile() {
347 File dataDir = Environment.getDataDirectory();
348 File systemDir = new File(dataDir, "system");
349 File fname = new File(systemDir, "uiderrors.txt");
350 return fname;
351 }
352
353 public static void dumpCriticalInfo(ProtoOutputStream proto) {
354 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
355 String line = null;
356 while ((line = in.readLine()) != null) {
357 if (line.contains("ignored: updated version")) continue;
358 proto.write(PackageServiceDumpProto.MESSAGES, line);
359 }
360 } catch (IOException ignored) {
361 }
362 }
363
364 public static void dumpCriticalInfo(PrintWriter pw, String msg) {
365 try (BufferedReader in = new BufferedReader(new FileReader(getSettingsProblemFile()))) {
366 String line = null;
367 while ((line = in.readLine()) != null) {
368 if (line.contains("ignored: updated version")) continue;
369 if (msg != null) {
370 pw.print(msg);
371 }
372 pw.println(line);
373 }
374 } catch (IOException ignored) {
375 }
376 }
377
378 public static void logCriticalInfo(int priority, String msg) {
379 Slog.println(priority, TAG, msg);
380 EventLogTags.writePmCriticalInfo(msg);
381 try {
382 File fname = getSettingsProblemFile();
383 FileOutputStream out = new FileOutputStream(fname, true);
384 PrintWriter pw = new FastPrintWriter(out);
385 SimpleDateFormat formatter = new SimpleDateFormat();
386 String dateString = formatter.format(new Date(System.currentTimeMillis()));
387 pw.println(dateString + ": " + msg);
388 pw.close();
389 FileUtils.setPermissions(
390 fname.toString(),
391 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH,
392 -1, -1);
393 } catch (java.io.IOException e) {
394 }
395 }
396
Patrick Baumann2f2fd712019-07-31 15:18:53 -0700397 /** Enforces that if the caller is shell, it does not have the provided user restriction. */
398 public static void enforceShellRestriction(
399 UserManagerInternal userManager, String restriction, int callingUid, int userHandle) {
Todd Kennedy0eb97382017-10-03 16:57:22 -0700400 if (callingUid == Process.SHELL_UID) {
401 if (userHandle >= 0
Patrick Baumann2f2fd712019-07-31 15:18:53 -0700402 && userManager.hasUserRestriction(
Todd Kennedy0eb97382017-10-03 16:57:22 -0700403 restriction, userHandle)) {
404 throw new SecurityException("Shell does not have permission to access user "
405 + userHandle);
406 } else if (userHandle < 0) {
407 Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
408 + userHandle + "\n\t" + Debug.getCallers(3));
409 }
410 }
411 }
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700412
413 /**
Todd Kennedy583378d2019-07-12 06:50:30 -0700414 * Enforces that the caller must be either the system process or the phone process.
415 * If not, throws a {@link SecurityException}.
416 */
417 public static void enforceSystemOrPhoneCaller(String methodName, int callingUid) {
418 if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
419 throw new SecurityException(
420 "Cannot call " + methodName + " from UID " + callingUid);
421 }
422 }
423
424 /**
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700425 * Derive the value of the {@code cpuAbiOverride} based on the provided
426 * value and an optional stored value from the package settings.
427 */
428 public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
429 String cpuAbiOverride = null;
430 if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
431 cpuAbiOverride = null;
432 } else if (abiOverride != null) {
433 cpuAbiOverride = abiOverride;
434 } else if (settings != null) {
435 cpuAbiOverride = settings.cpuAbiOverrideString;
436 }
437 return cpuAbiOverride;
438 }
439
440 /**
441 * Compares two sets of signatures. Returns:
442 * <br />
443 * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
444 * <br />
445 * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
446 * <br />
447 * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
448 * <br />
449 * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
450 * <br />
451 * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
452 */
453 public static int compareSignatures(Signature[] s1, Signature[] s2) {
454 if (s1 == null) {
455 return s2 == null
456 ? PackageManager.SIGNATURE_NEITHER_SIGNED
457 : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
458 }
459
460 if (s2 == null) {
461 return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
462 }
463
464 if (s1.length != s2.length) {
465 return PackageManager.SIGNATURE_NO_MATCH;
466 }
467
468 // Since both signature sets are of size 1, we can compare without HashSets.
469 if (s1.length == 1) {
470 return s1[0].equals(s2[0]) ?
471 PackageManager.SIGNATURE_MATCH :
472 PackageManager.SIGNATURE_NO_MATCH;
473 }
474
475 ArraySet<Signature> set1 = new ArraySet<Signature>();
476 for (Signature sig : s1) {
477 set1.add(sig);
478 }
479 ArraySet<Signature> set2 = new ArraySet<Signature>();
480 for (Signature sig : s2) {
481 set2.add(sig);
482 }
483 // Make sure s2 contains all signatures in s1.
484 if (set1.equals(set2)) {
485 return PackageManager.SIGNATURE_MATCH;
486 }
487 return PackageManager.SIGNATURE_NO_MATCH;
488 }
489
490 /**
491 * Used for backward compatibility to make sure any packages with
492 * certificate chains get upgraded to the new style. {@code existingSigs}
493 * will be in the old format (since they were stored on disk from before the
494 * system upgrade) and {@code scannedSigs} will be in the newer format.
495 */
496 private static boolean matchSignaturesCompat(String packageName,
Patrick Baumann420d58a2017-12-19 10:17:21 -0800497 PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700498 ArraySet<Signature> existingSet = new ArraySet<Signature>();
Daniel Cashman77029c52018-01-18 16:19:29 -0800499 for (Signature sig : packageSignatures.mSigningDetails.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700500 existingSet.add(sig);
501 }
502 ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
Patrick Baumann420d58a2017-12-19 10:17:21 -0800503 for (Signature sig : parsedSignatures.signatures) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700504 try {
505 Signature[] chainSignatures = sig.getChainSignatures();
506 for (Signature chainSig : chainSignatures) {
507 scannedCompatSet.add(chainSig);
508 }
509 } catch (CertificateEncodingException e) {
510 scannedCompatSet.add(sig);
511 }
512 }
513 // make sure the expanded scanned set contains all signatures in the existing one
514 if (scannedCompatSet.equals(existingSet)) {
515 // migrate the old signatures to the new scheme
Daniel Cashman77029c52018-01-18 16:19:29 -0800516 packageSignatures.mSigningDetails = parsedSignatures;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700517 return true;
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800518 } else if (parsedSignatures.hasPastSigningCertificates()) {
519
520 // well this sucks: the parsed package has probably rotated signing certificates, but
521 // we don't have enough information to determine if the new signing certificate was
522 // blessed by the old one
523 logCriticalInfo(Log.INFO, "Existing package " + packageName + " has flattened signing "
524 + "certificate chain. Unable to install newer version with rotated signing "
525 + "certificate.");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700526 }
527 return false;
528 }
529
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800530 private static boolean matchSignaturesRecover(
531 String packageName,
532 PackageParser.SigningDetails existingSignatures,
533 PackageParser.SigningDetails parsedSignatures,
534 @PackageParser.SigningDetails.CertCapabilities int flags) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700535 String msg = null;
536 try {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800537 if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
538 logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
539 + packageName);
540 return true;
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700541 }
542 } catch (CertificateException e) {
543 msg = e.getMessage();
544 }
545 logCriticalInfo(Log.INFO,
546 "Failed to recover certificates for " + packageName + ": " + msg);
547 return false;
548 }
549
550 /**
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700551 * Make sure the updated priv app is signed with the same key as the original APK file on the
552 * /system partition.
553 *
554 * <p>The rationale is that {@code disabledPkg} is a PackageSetting backed by xml files in /data
555 * and is not tamperproof.
556 */
557 private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
558 PackageSetting disabledPkgSetting) {
Winsone0756292020-01-31 12:21:54 -0800559 if (pkgSetting.signatures.mSigningDetails.checkCapability(
560 disabledPkgSetting.signatures.mSigningDetails,
561 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
562 || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
563 pkgSetting.signatures.mSigningDetails,
564 PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
565 return true;
566 } else {
567 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
568 pkgSetting.name);
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700569 return false;
570 }
Daniel Cashman5cdda342018-01-19 07:22:52 -0800571 }
572
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700573 /** Default is to not use fs-verity since it depends on kernel support. */
574 private static final int FSVERITY_DISABLED = 0;
575
576 /**
577 * Experimental implementation targeting priv apps, with Android specific kernel patches to
578 * extend fs-verity.
579 */
580 private static final int FSVERITY_LEGACY = 1;
581
582 /** Standard fs-verity. */
583 private static final int FSVERITY_ENABLED = 2;
584
Victor Hsieh0663df42019-01-07 15:28:27 -0800585 /** Returns true if standard APK Verity is enabled. */
Victor Hsieh55f14992018-01-13 14:12:59 -0800586 static boolean isApkVerityEnabled() {
Victor Hsieh946e06d72020-02-12 12:11:38 -0800587 return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
588 || SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED)
589 == FSVERITY_ENABLED;
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700590 }
591
Victor Hsieh0663df42019-01-07 15:28:27 -0800592 static boolean isLegacyApkVerityEnabled() {
Victor Hsiehc0cd7482018-10-04 10:10:54 -0700593 return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
Victor Hsieh55f14992018-01-13 14:12:59 -0800594 }
595
Victor Hsiehc243fe22019-04-18 11:26:49 -0700596 /** Returns true to force apk verification if the package is considered privileged. */
597 static boolean isApkVerificationForced(@Nullable PackageSetting ps) {
598 return ps != null && ps.isPrivileged() && (
Victor Hsieh0663df42019-01-07 15:28:27 -0800599 isApkVerityEnabled() || isLegacyApkVerityEnabled());
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700600 }
601
602 /**
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700603 * Verifies that signatures match.
604 * @returns {@code true} if the compat signatures were matched; otherwise, {@code false}.
605 * @throws PackageManagerException if the signatures did not match.
606 */
607 public static boolean verifySignatures(PackageSetting pkgSetting,
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700608 PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
609 boolean compareCompat, boolean compareRecover)
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700610 throws PackageManagerException {
611 final String packageName = pkgSetting.name;
612 boolean compatMatch = false;
Daniel Cashman77029c52018-01-18 16:19:29 -0800613 if (pkgSetting.signatures.mSigningDetails.signatures != null) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800614
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700615 // Already existing package. Make sure signatures match
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800616 boolean match = parsedSignatures.checkCapability(
617 pkgSetting.signatures.mSigningDetails,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700618 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
619 || pkgSetting.signatures.mSigningDetails.checkCapability(
620 parsedSignatures,
621 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700622 if (!match && compareCompat) {
Patrick Baumann420d58a2017-12-19 10:17:21 -0800623 match = matchSignaturesCompat(packageName, pkgSetting.signatures,
624 parsedSignatures);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700625 compatMatch = match;
626 }
627 if (!match && compareRecover) {
628 match = matchSignaturesRecover(
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800629 packageName,
630 pkgSetting.signatures.mSigningDetails,
631 parsedSignatures,
Daniel Cashman629a5f92018-03-30 18:54:14 -0700632 PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
633 || matchSignaturesRecover(
634 packageName,
635 parsedSignatures,
636 pkgSetting.signatures.mSigningDetails,
637 PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700638 }
Victor Hsieh0c8f2e02017-10-06 14:44:52 -0700639
640 if (!match && isApkVerificationForced(disabledPkgSetting)) {
641 match = matchSignatureInSystem(pkgSetting, disabledPkgSetting);
642 }
643
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700644 if (!match) {
645 throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
646 "Package " + packageName +
Todd Kennedy96cb94b2017-11-29 13:17:12 -0800647 " signatures do not match previously installed version; ignoring!");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700648 }
649 }
650 // Check for shared user signatures
Winsone0756292020-01-31 12:21:54 -0800651 if (pkgSetting.getSharedUser() != null
652 && pkgSetting.getSharedUser().signatures.mSigningDetails
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800653 != PackageParser.SigningDetails.UNKNOWN) {
654
655 // Already existing package. Make sure signatures match. In case of signing certificate
656 // rotation, the packages with newer certs need to be ok with being sharedUserId with
657 // the older ones. We check to see if either the new package is signed by an older cert
658 // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
659 // with being sharedUser with the existing signing cert.
Daniel Cashman77029c52018-01-18 16:19:29 -0800660 boolean match =
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800661 parsedSignatures.checkCapability(
Winsone0756292020-01-31 12:21:54 -0800662 pkgSetting.getSharedUser().signatures.mSigningDetails,
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800663 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
Winsone0756292020-01-31 12:21:54 -0800664 || pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800665 parsedSignatures,
666 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy3e654842017-11-29 13:58:53 -0800667 if (!match && compareCompat) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700668 match = matchSignaturesCompat(
Winsone0756292020-01-31 12:21:54 -0800669 packageName, pkgSetting.getSharedUser().signatures, parsedSignatures);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700670 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800671 if (!match && compareRecover) {
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800672 match =
673 matchSignaturesRecover(packageName,
Winsone0756292020-01-31 12:21:54 -0800674 pkgSetting.getSharedUser().signatures.mSigningDetails,
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800675 parsedSignatures,
676 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
677 || matchSignaturesRecover(packageName,
678 parsedSignatures,
Winsone0756292020-01-31 12:21:54 -0800679 pkgSetting.getSharedUser().signatures.mSigningDetails,
Dan Cashman1dbe6d02018-01-23 11:18:28 -0800680 PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700681 compatMatch |= match;
682 }
Todd Kennedy3e654842017-11-29 13:58:53 -0800683 if (!match) {
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700684 throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
685 "Package " + packageName
686 + " has no signatures that match those in shared user "
Winsone0756292020-01-31 12:21:54 -0800687 + pkgSetting.getSharedUser().name + "; ignoring!");
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700688 }
689 }
690 return compatMatch;
691 }
692
693 public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
694 if (DEBUG_COMPRESSION) {
695 Slog.i(TAG, "Decompress file"
696 + "; src: " + srcFile.getAbsolutePath()
697 + ", dst: " + dstFile.getAbsolutePath());
698 }
699 try (
700 InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
701 OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
702 ) {
Jeff Sharkeyb18f8992018-01-31 21:47:09 -0700703 FileUtils.copy(fileIn, fileOut);
Todd Kennedy7c4c55d2017-11-02 10:01:39 -0700704 Os.chmod(dstFile.getAbsolutePath(), 0644);
705 return PackageManager.INSTALL_SUCCEEDED;
706 } catch (IOException e) {
707 logCriticalInfo(Log.ERROR, "Failed to decompress file"
708 + "; src: " + srcFile.getAbsolutePath()
709 + ", dst: " + dstFile.getAbsolutePath());
710 }
711 return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
712 }
713
714 public static File[] getCompressedFiles(String codePath) {
715 final File stubCodePath = new File(codePath);
716 final String stubName = stubCodePath.getName();
717
718 // The layout of a compressed package on a given partition is as follows :
719 //
720 // Compressed artifacts:
721 //
722 // /partition/ModuleName/foo.gz
723 // /partation/ModuleName/bar.gz
724 //
725 // Stub artifact:
726 //
727 // /partition/ModuleName-Stub/ModuleName-Stub.apk
728 //
729 // In other words, stub is on the same partition as the compressed artifacts
730 // and in a directory that's suffixed with "-Stub".
731 int idx = stubName.lastIndexOf(STUB_SUFFIX);
732 if (idx < 0 || (stubName.length() != (idx + STUB_SUFFIX.length()))) {
733 return null;
734 }
735
736 final File stubParentDir = stubCodePath.getParentFile();
737 if (stubParentDir == null) {
738 Slog.e(TAG, "Unable to determine stub parent dir for codePath: " + codePath);
739 return null;
740 }
741
742 final File compressedPath = new File(stubParentDir, stubName.substring(0, idx));
743 final File[] files = compressedPath.listFiles(new FilenameFilter() {
744 @Override
745 public boolean accept(File dir, String name) {
746 return name.toLowerCase().endsWith(COMPRESSED_EXTENSION);
747 }
748 });
749
750 if (DEBUG_COMPRESSION && files != null && files.length > 0) {
751 Slog.i(TAG, "getCompressedFiles[" + codePath + "]: " + Arrays.toString(files));
752 }
753
754 return files;
755 }
756
757 public static boolean compressedFileExists(String codePath) {
758 final File[] compressedFiles = getCompressedFiles(codePath);
759 return compressedFiles != null && compressedFiles.length > 0;
760 }
Sudheer Shankaa05a9942018-08-29 23:28:23 -0700761
762 /**
763 * Parse given package and return minimal details.
764 */
765 public static PackageInfoLite getMinimalPackageInfo(Context context, String packagePath,
766 int flags, String abiOverride) {
767 final PackageInfoLite ret = new PackageInfoLite();
768 if (packagePath == null) {
769 Slog.i(TAG, "Invalid package file " + packagePath);
770 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
771 return ret;
772 }
773
774 final File packageFile = new File(packagePath);
775 final PackageParser.PackageLite pkg;
776 final long sizeBytes;
777 try {
778 pkg = PackageParser.parsePackageLite(packageFile, 0);
779 sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
780 } catch (PackageParserException | IOException e) {
781 Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
782
783 if (!packageFile.exists()) {
784 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
785 } else {
786 ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
787 }
788
789 return ret;
790 }
791
792 final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
793 pkg.packageName, pkg.installLocation, sizeBytes, flags);
794
795 ret.packageName = pkg.packageName;
796 ret.splitNames = pkg.splitNames;
797 ret.versionCode = pkg.versionCode;
798 ret.versionCodeMajor = pkg.versionCodeMajor;
799 ret.baseRevisionCode = pkg.baseRevisionCode;
800 ret.splitRevisionCodes = pkg.splitRevisionCodes;
801 ret.installLocation = pkg.installLocation;
802 ret.verifiers = pkg.verifiers;
803 ret.recommendedInstallLocation = recommendedInstallLocation;
804 ret.multiArch = pkg.multiArch;
Chester Hsieh750b5ed2020-01-08 09:32:17 -0800805 ret.debuggable = pkg.debuggable;
Sudheer Shankaa05a9942018-08-29 23:28:23 -0700806
807 return ret;
808 }
809
810 /**
811 * Calculate estimated footprint of given package post-installation.
812 *
813 * @return -1 if there's some error calculating the size, otherwise installed size of the
814 * package.
815 */
816 public static long calculateInstalledSize(String packagePath, String abiOverride) {
817 final File packageFile = new File(packagePath);
818 final PackageParser.PackageLite pkg;
819 try {
820 pkg = PackageParser.parsePackageLite(packageFile, 0);
821 return PackageHelper.calculateInstalledSize(pkg, abiOverride);
822 } catch (PackageParserException | IOException e) {
823 Slog.w(TAG, "Failed to calculate installed size: " + e);
824 return -1;
825 }
826 }
827
828 /**
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000829 * Checks whenever downgrade of an app is permitted.
830 *
831 * @param installFlags flags of the current install.
Winsonf00c7552020-01-28 12:52:01 -0800832 * @param isAppDebuggable if the currently installed version of the app is debuggable.
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000833 * @return {@code true} if downgrade is permitted according to the {@code installFlags} and
834 * {@code applicationFlags}.
835 */
Winsonf00c7552020-01-28 12:52:01 -0800836 public static boolean isDowngradePermitted(int installFlags, boolean isAppDebuggable) {
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000837 // If installed, the package will get access to data left on the device by its
Nikita Ioffea1111ff2019-03-04 12:26:14 +0000838 // predecessor. As a security measure, this is permitted only if this is not a
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000839 // version downgrade or if the predecessor package is marked as debuggable and
840 // a downgrade is explicitly requested.
841 //
842 // On debuggable platform builds, downgrades are permitted even for
843 // non-debuggable packages to make testing easier. Debuggable platform builds do
844 // not offer security guarantees and thus it's OK to disable some security
845 // mechanisms to make debugging/testing easier on those builds. However, even on
846 // debuggable builds downgrades of packages are permitted only if requested via
847 // installFlags. This is because we aim to keep the behavior of debuggable
848 // platform builds as close as possible to the behavior of non-debuggable
849 // platform builds.
Nikita Ioffea1111ff2019-03-04 12:26:14 +0000850 //
851 // In case of user builds, downgrade is permitted only for the system server initiated
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000852 // sessions. This is enforced by INSTALL_ALLOW_DOWNGRADE flag parameter.
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000853 final boolean downgradeRequested =
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000854 (installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0;
Nikita Ioffea1111ff2019-03-04 12:26:14 +0000855 if (!downgradeRequested) {
856 return false;
857 }
Winsonf00c7552020-01-28 12:52:01 -0800858 final boolean isDebuggable = Build.IS_DEBUGGABLE || isAppDebuggable;
Nikita Ioffea1111ff2019-03-04 12:26:14 +0000859 if (isDebuggable) {
860 return true;
861 }
Nikita Ioffeb1d60f12019-03-06 18:56:49 +0000862 return (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
Nikita Ioffe8bbb8152019-02-21 15:54:54 +0000863 }
864
865 /**
Sudheer Shankaa05a9942018-08-29 23:28:23 -0700866 * Copy package to the target location.
867 *
868 * @param packagePath absolute path to the package to be copied. Can be
869 * a single monolithic APK file or a cluster directory
870 * containing one or more APKs.
871 * @return returns status code according to those in
872 * {@link PackageManager}
873 */
874 public static int copyPackage(String packagePath, File targetDir) {
875 if (packagePath == null) {
876 return PackageManager.INSTALL_FAILED_INVALID_URI;
877 }
878
879 try {
880 final File packageFile = new File(packagePath);
881 final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
882 copyFile(pkg.baseCodePath, targetDir, "base.apk");
883 if (!ArrayUtils.isEmpty(pkg.splitNames)) {
884 for (int i = 0; i < pkg.splitNames.length; i++) {
885 copyFile(pkg.splitCodePaths[i], targetDir,
886 "split_" + pkg.splitNames[i] + ".apk");
887 }
888 }
889 return PackageManager.INSTALL_SUCCEEDED;
890 } catch (PackageParserException | IOException | ErrnoException e) {
891 Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
892 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
893 }
894 }
895
896 private static void copyFile(String sourcePath, File targetDir, String targetName)
897 throws ErrnoException, IOException {
898 if (!FileUtils.isValidExtFilename(targetName)) {
899 throw new IllegalArgumentException("Invalid filename: " + targetName);
900 }
901 Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
902
903 final File targetFile = new File(targetDir, targetName);
904 final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
905 O_RDWR | O_CREAT, 0644);
906 Os.chmod(targetFile.getAbsolutePath(), 0644);
907 FileInputStream source = null;
908 try {
909 source = new FileInputStream(sourcePath);
910 FileUtils.copy(source.getFD(), targetFd);
911 } finally {
912 IoUtils.closeQuietly(source);
913 }
914 }
Todd Kennedy7e3dd3a2019-07-08 10:34:29 -0700915
916 /**
917 * Returns the {@link PermissionsState} for the given package. If the {@link PermissionsState}
918 * could not be found, {@code null} will be returned.
919 */
Winson14ff7172019-10-23 10:42:27 -0700920 public static PermissionsState getPermissionsState(
921 PackageManagerInternal packageManagerInternal, AndroidPackage pkg) {
Winsone0756292020-01-31 12:21:54 -0800922 final PackageSetting packageSetting = packageManagerInternal.getPackageSetting(
923 pkg.getPackageName());
Todd Kennedy7e3dd3a2019-07-08 10:34:29 -0700924 if (packageSetting == null) {
925 return null;
926 }
927 return packageSetting.getPermissionsState();
928 }
Songchun Fanf56f1c52020-01-22 17:10:50 -0800929
930 /**
931 * Recursively create target directory
932 */
933 public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException {
934 final Path targetDirPath = targetDir.toPath();
935 final int directoriesCount = targetDirPath.getNameCount();
936 File currentDir;
937 for (int i = 1; i <= directoriesCount; i++) {
938 currentDir = targetDirPath.subpath(0, i).toFile();
939 if (currentDir.exists()) {
940 continue;
941 }
942 Os.mkdir(currentDir.getAbsolutePath(), mode);
943 Os.chmod(currentDir.getAbsolutePath(), mode);
944 }
945 }
Todd Kennedy75dd0e62020-04-01 10:48:57 -0700946
947 /**
948 * Returns a string that's compatible with the verification root hash extra.
949 * @see PackageManager#EXTRA_VERIFICATION_ROOT_HASH
950 */
951 @NonNull
952 public static String buildVerificationRootHashString(@NonNull String baseFilename,
953 @Nullable String[] splitFilenameArray) {
954 final StringBuilder sb = new StringBuilder();
955 final String baseFilePath =
956 baseFilename.substring(baseFilename.lastIndexOf(File.separator) + 1);
957 sb.append(baseFilePath).append(":");
958 final byte[] baseRootHash = getRootHash(baseFilename);
959 if (baseRootHash == null) {
960 sb.append("0");
961 } else {
962 sb.append(HexDump.toHexString(baseRootHash));
963 }
964 if (splitFilenameArray == null || splitFilenameArray.length == 0) {
965 return sb.toString();
966 }
967
968 for (int i = splitFilenameArray.length - 1; i >= 0; i--) {
969 final String splitFilename = splitFilenameArray[i];
970 final String splitFilePath =
971 splitFilename.substring(splitFilename.lastIndexOf(File.separator) + 1);
972 final byte[] splitRootHash = getRootHash(splitFilename);
973 sb.append(";").append(splitFilePath).append(":");
974 if (splitRootHash == null) {
975 sb.append("0");
976 } else {
977 sb.append(HexDump.toHexString(splitRootHash));
978 }
979 }
980 return sb.toString();
981 }
982
983 /**
984 * Returns the root has for the given file.
985 * <p>Otherwise, returns {@code null} if the root hash could not be found or calculated.
986 * <p>NOTE: This currently only works on files stored on the incremental file system. The
987 * eventual goal is that this hash [among others] can be retrieved for any file.
988 */
989 @Nullable
990 private static byte[] getRootHash(String filename) {
991 try {
992 final byte[] baseFileSignature =
993 IncrementalManager.unsafeGetFileSignature(filename);
994 if (baseFileSignature == null) {
995 throw new IOException("File signature not present");
996 }
997 final V4Signature signature =
998 V4Signature.readFrom(baseFileSignature);
999 if (signature.hashingInfo == null) {
1000 throw new IOException("Hashing info not present");
1001 }
1002 final HashingInfo hashInfo =
1003 HashingInfo.fromByteArray(signature.hashingInfo);
1004 if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) {
1005 throw new IOException("Root has not present");
1006 }
1007 return hashInfo.rawRootHash;
1008 } catch (IOException ignore) {
1009 Slog.e(TAG, "ERROR: could not load root hash from incremental install");
1010 }
1011 return null;
1012 }
Narayan Kamath6d99f792016-05-16 17:34:48 +01001013}