blob: 60c83b404739e305108aefbe541801515f3fd551 [file] [log] [blame]
Andreas Gampea8908752015-11-10 08:58:14 -08001/*
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
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080019import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
20import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
Andreas Gampebdd30d82016-03-20 11:32:11 -070021import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080022
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -070023import android.annotation.Nullable;
Andreas Gampea8908752015-11-10 08:58:14 -080024import android.content.Context;
Andreas Gampea8908752015-11-10 08:58:14 -080025import android.content.pm.IOtaDexopt;
26import android.content.pm.PackageParser;
Andreas Gampea8908752015-11-10 08:58:14 -080027import android.os.Environment;
28import android.os.RemoteException;
29import android.os.ResultReceiver;
30import android.os.ServiceManager;
Andreas Gampea8908752015-11-10 08:58:14 -080031import android.os.storage.StorageManager;
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -070032import android.text.TextUtils;
Andreas Gampea8908752015-11-10 08:58:14 -080033import android.util.Log;
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080034import android.util.Slog;
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -070035
Andreas Gampe112e0142016-09-13 11:12:13 -070036import com.android.internal.logging.MetricsLogger;
Jeff Sharkey740f5232016-12-09 14:31:26 -070037import com.android.server.pm.Installer.InstallerException;
Andreas Gampea8908752015-11-10 08:58:14 -080038
39import java.io.File;
40import java.io.FileDescriptor;
Andreas Gamped15300c2016-06-23 20:27:12 -070041import java.util.ArrayList;
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -070042import java.util.Arrays;
Andreas Gampea8908752015-11-10 08:58:14 -080043import java.util.Collection;
Andreas Gampea8908752015-11-10 08:58:14 -080044import java.util.List;
Andreas Gampe112e0142016-09-13 11:12:13 -070045import java.util.concurrent.TimeUnit;
Andreas Gampea8908752015-11-10 08:58:14 -080046
47/**
48 * A service for A/B OTA dexopting.
49 *
50 * {@hide}
51 */
52public class OtaDexoptService extends IOtaDexopt.Stub {
53 private final static String TAG = "OTADexopt";
54 private final static boolean DEBUG_DEXOPT = true;
Andreas Gampea8908752015-11-10 08:58:14 -080055
Andreas Gampe3f95d0b2016-08-01 22:08:26 -070056 // The synthetic library dependencies denoting "no checks."
57 private final static String[] NO_LIBRARIES = new String[] { "&" };
58
Andreas Gampe1920d7b42016-09-09 17:50:20 -070059 // The amount of "available" (free - low threshold) space necessary at the start of an OTA to
60 // not bulk-delete unused apps' odex files.
61 private final static long BULK_DELETE_THRESHOLD = 1024 * 1024 * 1024; // 1GB.
62
Andreas Gampea8908752015-11-10 08:58:14 -080063 private final Context mContext;
Andreas Gampea8908752015-11-10 08:58:14 -080064 private final PackageManagerService mPackageManagerService;
65
66 // TODO: Evaluate the need for WeakReferences here.
Andreas Gamped15300c2016-06-23 20:27:12 -070067
68 /**
Andreas Gampe14186732016-07-28 16:54:41 -070069 * The list of dexopt invocations for all work.
Andreas Gamped15300c2016-06-23 20:27:12 -070070 */
Andreas Gampe14186732016-07-28 16:54:41 -070071 private List<String> mDexoptCommands;
Andreas Gamped15300c2016-06-23 20:27:12 -070072
Andreas Gampeff8ab4c2016-06-10 15:21:39 -070073 private int completeSize;
Andreas Gampea8908752015-11-10 08:58:14 -080074
Andreas Gampe112e0142016-09-13 11:12:13 -070075 // MetricsLogger properties.
76
77 // Space before and after.
78 private long availableSpaceBefore;
79 private long availableSpaceAfterBulkDelete;
80 private long availableSpaceAfterDexopt;
81
82 // Packages.
83 private int importantPackageCount;
84 private int otherPackageCount;
85
86 // Number of dexopt commands. This may be different from the count of packages.
87 private int dexoptCommandCountTotal;
88 private int dexoptCommandCountExecuted;
89
90 // For spent time.
91 private long otaDexoptTimeStart;
92
93
Andreas Gampea8908752015-11-10 08:58:14 -080094 public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
95 this.mContext = context;
96 this.mPackageManagerService = packageManagerService;
97
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080098 // Now it's time to check whether we need to move any A/B artifacts.
99 moveAbArtifacts(packageManagerService.mInstaller);
Andreas Gampea8908752015-11-10 08:58:14 -0800100 }
101
102 public static OtaDexoptService main(Context context,
103 PackageManagerService packageManagerService) {
104 OtaDexoptService ota = new OtaDexoptService(context, packageManagerService);
105 ServiceManager.addService("otadexopt", ota);
106
107 return ota;
108 }
109
110 @Override
111 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
112 String[] args, ResultReceiver resultReceiver) throws RemoteException {
113 (new OtaDexoptShellCommand(this)).exec(
114 this, in, out, err, args, resultReceiver);
115 }
116
117 @Override
118 public synchronized void prepare() throws RemoteException {
Andreas Gampe14186732016-07-28 16:54:41 -0700119 if (mDexoptCommands != null) {
Andreas Gampea8908752015-11-10 08:58:14 -0800120 throw new IllegalStateException("already called prepare()");
121 }
Todd Kennedy94f9be22016-08-18 11:22:52 -0700122 final List<PackageParser.Package> important;
123 final List<PackageParser.Package> others;
Andreas Gampea8908752015-11-10 08:58:14 -0800124 synchronized (mPackageManagerService.mPackages) {
Andreas Gampe14186732016-07-28 16:54:41 -0700125 // Important: the packages we need to run with ab-ota compiler-reason.
Todd Kennedy94f9be22016-08-18 11:22:52 -0700126 important = PackageManagerServiceUtils.getPackagesForDexopt(
David Brazdil6b4736d2016-02-04 11:54:17 +0000127 mPackageManagerService.mPackages.values(), mPackageManagerService);
Andreas Gampe14186732016-07-28 16:54:41 -0700128 // Others: we should optimize this with the (first-)boot compiler-reason.
Todd Kennedy94f9be22016-08-18 11:22:52 -0700129 others = new ArrayList<>(mPackageManagerService.mPackages.values());
Andreas Gampe14186732016-07-28 16:54:41 -0700130 others.removeAll(important);
131
132 // Pre-size the array list by over-allocating by a factor of 1.5.
133 mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
Todd Kennedy94f9be22016-08-18 11:22:52 -0700134 }
Andreas Gampe14186732016-07-28 16:54:41 -0700135
Todd Kennedy94f9be22016-08-18 11:22:52 -0700136 for (PackageParser.Package p : important) {
137 // Make sure that core apps are optimized according to their own "reason".
138 // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
139 // (by default is speed-profile) they will be interepreted/JITed. This in itself is
140 // not a problem as we will end up doing profile guided compilation. However, some
141 // core apps may be loaded by system server which doesn't JIT and we need to make
142 // sure we don't interpret-only
143 int compilationReason = p.coreApp
144 ? PackageManagerService.REASON_CORE_APP
145 : PackageManagerService.REASON_AB_OTA;
146 mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason));
147 }
148 for (PackageParser.Package p : others) {
149 // We assume here that there are no core apps left.
150 if (p.coreApp) {
151 throw new IllegalStateException("Found a core app that's not important");
Andreas Gampe14186732016-07-28 16:54:41 -0700152 }
Todd Kennedy94f9be22016-08-18 11:22:52 -0700153 mDexoptCommands.addAll(
154 generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
Andreas Gampea8908752015-11-10 08:58:14 -0800155 }
Andreas Gampe14186732016-07-28 16:54:41 -0700156 completeSize = mDexoptCommands.size();
Andreas Gampe1920d7b42016-09-09 17:50:20 -0700157
Andreas Gampe112e0142016-09-13 11:12:13 -0700158 long spaceAvailable = getAvailableSpace();
159 if (spaceAvailable < BULK_DELETE_THRESHOLD) {
Andreas Gampe1920d7b42016-09-09 17:50:20 -0700160 Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
161 + PackageManagerServiceUtils.packagesToString(others));
162 for (PackageParser.Package pkg : others) {
163 deleteOatArtifactsOfPackage(pkg);
164 }
165 }
Andreas Gampe112e0142016-09-13 11:12:13 -0700166 long spaceAvailableNow = getAvailableSpace();
167
168 prepareMetricsLogging(important.size(), others.size(), spaceAvailable, spaceAvailableNow);
Andreas Gampea8908752015-11-10 08:58:14 -0800169 }
170
171 @Override
172 public synchronized void cleanup() throws RemoteException {
173 if (DEBUG_DEXOPT) {
174 Log.i(TAG, "Cleaning up OTA Dexopt state.");
175 }
Andreas Gampe14186732016-07-28 16:54:41 -0700176 mDexoptCommands = null;
Andreas Gampe13018f32016-09-19 15:18:46 -0700177 availableSpaceAfterDexopt = getAvailableSpace();
Andreas Gampe112e0142016-09-13 11:12:13 -0700178
179 performMetricsLogging();
Andreas Gampea8908752015-11-10 08:58:14 -0800180 }
181
182 @Override
183 public synchronized boolean isDone() throws RemoteException {
Andreas Gampe14186732016-07-28 16:54:41 -0700184 if (mDexoptCommands == null) {
Andreas Gampea8908752015-11-10 08:58:14 -0800185 throw new IllegalStateException("done() called before prepare()");
186 }
187
Andreas Gampe14186732016-07-28 16:54:41 -0700188 return mDexoptCommands.isEmpty();
Andreas Gampea8908752015-11-10 08:58:14 -0800189 }
190
191 @Override
Andreas Gampeff8ab4c2016-06-10 15:21:39 -0700192 public synchronized float getProgress() throws RemoteException {
Andreas Gampe14186732016-07-28 16:54:41 -0700193 // Approximate the progress by the amount of already completed commands.
Andreas Gampeff8ab4c2016-06-10 15:21:39 -0700194 if (completeSize == 0) {
195 return 1f;
196 }
Andreas Gampe14186732016-07-28 16:54:41 -0700197 int commandsLeft = mDexoptCommands.size();
198 return (completeSize - commandsLeft) / ((float)completeSize);
Andreas Gamped15300c2016-06-23 20:27:12 -0700199 }
200
201 @Override
202 public synchronized String nextDexoptCommand() throws RemoteException {
Andreas Gampe14186732016-07-28 16:54:41 -0700203 if (mDexoptCommands == null) {
Andreas Gamped15300c2016-06-23 20:27:12 -0700204 throw new IllegalStateException("dexoptNextPackage() called before prepare()");
205 }
206
Andreas Gampe14186732016-07-28 16:54:41 -0700207 if (mDexoptCommands.isEmpty()) {
208 return "(all done)";
209 }
Andreas Gamped15300c2016-06-23 20:27:12 -0700210
Andreas Gampe14186732016-07-28 16:54:41 -0700211 String next = mDexoptCommands.remove(0);
Andreas Gamped15300c2016-06-23 20:27:12 -0700212
Andreas Gampe1920d7b42016-09-09 17:50:20 -0700213 if (getAvailableSpace() > 0) {
Andreas Gampe112e0142016-09-13 11:12:13 -0700214 dexoptCommandCountExecuted++;
215
Andreas Gampe14186732016-07-28 16:54:41 -0700216 return next;
217 } else {
Andreas Gampe1920d7b42016-09-09 17:50:20 -0700218 if (DEBUG_DEXOPT) {
219 Log.w(TAG, "Not enough space for OTA dexopt, stopping with "
220 + (mDexoptCommands.size() + 1) + " commands left.");
221 }
Andreas Gampe14186732016-07-28 16:54:41 -0700222 mDexoptCommands.clear();
223 return "(no free space)";
Andreas Gamped15300c2016-06-23 20:27:12 -0700224 }
225 }
226
Andreas Gampe1920d7b42016-09-09 17:50:20 -0700227 private long getMainLowSpaceThreshold() {
Andreas Gamped15300c2016-06-23 20:27:12 -0700228 File dataDir = Environment.getDataDirectory();
229 @SuppressWarnings("deprecation")
230 long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
231 if (lowThreshold == 0) {
232 throw new IllegalStateException("Invalid low memory threshold");
233 }
Andreas Gampe1920d7b42016-09-09 17:50:20 -0700234 return lowThreshold;
235 }
236
237 /**
238 * Returns the difference of free space to the low-storage-space threshold. Positive values
239 * indicate free bytes.
240 */
241 private long getAvailableSpace() {
242 // TODO: If apps are not installed in the internal /data partition, we should compare
243 // against that storage's free capacity.
244 long lowThreshold = getMainLowSpaceThreshold();
245
246 File dataDir = Environment.getDataDirectory();
Andreas Gamped15300c2016-06-23 20:27:12 -0700247 long usableSpace = dataDir.getUsableSpace();
Andreas Gampe1920d7b42016-09-09 17:50:20 -0700248
249 return usableSpace - lowThreshold;
250 }
251
252 private static String getOatDir(PackageParser.Package pkg) {
253 if (!pkg.canHaveOatDir()) {
254 return null;
255 }
256 File codePath = new File(pkg.codePath);
257 if (codePath.isDirectory()) {
258 return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
259 }
260 return null;
261 }
262
263 private void deleteOatArtifactsOfPackage(PackageParser.Package pkg) {
264 String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
265 for (String codePath : pkg.getAllCodePaths()) {
266 for (String isa : instructionSets) {
267 try {
268 mPackageManagerService.mInstaller.deleteOdex(codePath, isa, getOatDir(pkg));
269 } catch (InstallerException e) {
270 Log.e(TAG, "Failed deleting oat files for " + codePath, e);
271 }
272 }
273 }
Andreas Gampe14186732016-07-28 16:54:41 -0700274 }
Andreas Gamped15300c2016-06-23 20:27:12 -0700275
Andreas Gampe14186732016-07-28 16:54:41 -0700276 /**
277 * Generate all dexopt commands for the given package.
278 */
279 private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
280 int compilationReason) {
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -0700281 // Intercept and collect dexopt requests
282 final List<String> commands = new ArrayList<String>();
283 final Installer collectingInstaller = new Installer(mContext, true) {
284 @Override
285 public void dexopt(String apkPath, int uid, @Nullable String pkgName,
286 String instructionSet, int dexoptNeeded, @Nullable String outputPath,
287 int dexFlags, String compilerFilter, @Nullable String volumeUuid,
288 @Nullable String sharedLibraries) throws InstallerException {
289 commands.add(buildCommand("dexopt",
290 apkPath,
291 uid,
292 pkgName,
293 instructionSet,
294 dexoptNeeded,
295 outputPath,
296 dexFlags,
297 compilerFilter,
298 volumeUuid,
299 sharedLibraries));
300 }
301 };
Andreas Gamped15300c2016-06-23 20:27:12 -0700302
303 // Use the package manager install and install lock here for the OTA dex optimizer.
304 PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
305 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
Calin Juravle94385282016-07-12 13:56:34 +0100306
Andreas Gampe3f95d0b2016-08-01 22:08:26 -0700307 String[] libraryDependencies = pkg.usesLibraryFiles;
308 if (pkg.isSystemApp()) {
309 // For system apps, we want to avoid classpaths checks.
310 libraryDependencies = NO_LIBRARIES;
311 }
312
313 optimizer.performDexOpt(pkg, libraryDependencies,
Andreas Gamped15300c2016-06-23 20:27:12 -0700314 null /* ISAs */, false /* checkProfiles */,
Andreas Gampe7e619a92016-07-12 22:42:41 -0700315 getCompilerFilterForReason(compilationReason),
316 null /* CompilerStats.PackageStats */);
Andreas Gamped15300c2016-06-23 20:27:12 -0700317
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -0700318 return commands;
Andreas Gampeff8ab4c2016-06-10 15:21:39 -0700319 }
320
321 @Override
Andreas Gampea8908752015-11-10 08:58:14 -0800322 public synchronized void dexoptNextPackage() throws RemoteException {
Andreas Gampe14186732016-07-28 16:54:41 -0700323 throw new UnsupportedOperationException();
Andreas Gampea8908752015-11-10 08:58:14 -0800324 }
325
Andreas Gampeabcbe2f2016-02-26 11:25:36 -0800326 private void moveAbArtifacts(Installer installer) {
Andreas Gampe14186732016-07-28 16:54:41 -0700327 if (mDexoptCommands != null) {
Andreas Gampeabcbe2f2016-02-26 11:25:36 -0800328 throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
Andreas Gampea8908752015-11-10 08:58:14 -0800329 }
Andreas Gampeabcbe2f2016-02-26 11:25:36 -0800330
331 // Look into all packages.
332 Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages();
333 for (PackageParser.Package pkg : pkgs) {
334 if (pkg == null) {
335 continue;
336 }
337
338 // Does the package have code? If not, there won't be any artifacts.
339 if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
340 continue;
341 }
342 if (pkg.codePath == null) {
343 Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath");
344 continue;
345 }
346
347 // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into
348 // /data/ota and moved into the dalvik-cache already.
349 if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) {
350 continue;
351 }
352
353 final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
354 final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
355 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
356 for (String dexCodeInstructionSet : dexCodeInstructionSets) {
357 for (String path : paths) {
358 String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)).
359 getAbsolutePath();
360
361 // TODO: Check first whether there is an artifact, to save the roundtrip time.
362
363 try {
364 installer.moveAb(path, dexCodeInstructionSet, oatDir);
365 } catch (InstallerException e) {
366 }
367 }
Andreas Gampea8908752015-11-10 08:58:14 -0800368 }
369 }
Andreas Gampea8908752015-11-10 08:58:14 -0800370 }
371
Andreas Gampe112e0142016-09-13 11:12:13 -0700372 /**
373 * Initialize logging fields.
374 */
375 private void prepareMetricsLogging(int important, int others, long spaceBegin, long spaceBulk) {
376 availableSpaceBefore = spaceBegin;
377 availableSpaceAfterBulkDelete = spaceBulk;
378 availableSpaceAfterDexopt = 0;
379
380 importantPackageCount = important;
381 otherPackageCount = others;
382
383 dexoptCommandCountTotal = mDexoptCommands.size();
384 dexoptCommandCountExecuted = 0;
385
386 otaDexoptTimeStart = System.nanoTime();
387 }
388
389 private static int inMegabytes(long value) {
390 long in_mega_bytes = value / (1024 * 1024);
391 if (in_mega_bytes > Integer.MAX_VALUE) {
392 Log.w(TAG, "Recording " + in_mega_bytes + "MB of free space, overflowing range");
393 return Integer.MAX_VALUE;
394 }
395 return (int)in_mega_bytes;
396 }
397
398 private void performMetricsLogging() {
399 long finalTime = System.nanoTime();
400
401 MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb",
402 inMegabytes(availableSpaceBefore));
403 MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb",
404 inMegabytes(availableSpaceAfterBulkDelete));
405 MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb",
406 inMegabytes(availableSpaceAfterDexopt));
407
408 MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages",
409 importantPackageCount);
410 MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount);
411
412 MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal);
413 MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed",
414 dexoptCommandCountExecuted);
415
416 final int elapsedTimeSeconds =
417 (int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart);
418 MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds);
419 }
420
Andreas Gampea8908752015-11-10 08:58:14 -0800421 private static class OTADexoptPackageDexOptimizer extends
422 PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
Andreas Gampea8908752015-11-10 08:58:14 -0800423 public OTADexoptPackageDexOptimizer(Installer installer, Object installLock,
424 Context context) {
425 super(installer, installLock, context, "*otadexopt*");
426 }
Andreas Gampea8908752015-11-10 08:58:14 -0800427 }
Andreas Gamped15300c2016-06-23 20:27:12 -0700428
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -0700429 /**
430 * Cook up argument list in the format that {@code installd} expects.
431 */
432 private static String buildCommand(Object... args) {
433 final StringBuilder builder = new StringBuilder();
434 for (Object arg : args) {
435 String escaped;
436 if (arg == null) {
437 escaped = "";
438 } else {
439 escaped = String.valueOf(arg);
440 }
441 if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
442 throw new IllegalArgumentException(
443 "Invalid argument while executing " + Arrays.toString(args));
444 }
445 if (TextUtils.isEmpty(escaped)) {
446 escaped = "!";
447 }
448 builder.append(' ').append(escaped);
Andreas Gamped15300c2016-06-23 20:27:12 -0700449 }
Jeff Sharkeyc98c7bc2016-12-07 14:57:34 -0700450 return builder.toString();
Andreas Gamped15300c2016-06-23 20:27:12 -0700451 }
Andreas Gampea8908752015-11-10 08:58:14 -0800452}