blob: 689917cd670a7f15cb16ee40ff2cd1834a98424e [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.Installer.DEXOPT_OTA;
20import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
21import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
Andreas Gampebdd30d82016-03-20 11:32:11 -070022import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080023
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;
Andreas Gampea8908752015-11-10 08:58:14 -080032import android.util.Log;
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080033import android.util.Slog;
Andreas Gampe77cc8172016-09-13 11:12:13 -070034import com.android.internal.logging.MetricsLogger;
Andreas Gampecc241a52016-06-23 20:27:12 -070035import com.android.internal.os.InstallerConnection;
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080036import com.android.internal.os.InstallerConnection.InstallerException;
Andreas Gampea8908752015-11-10 08:58:14 -080037
38import java.io.File;
39import java.io.FileDescriptor;
Andreas Gampecc241a52016-06-23 20:27:12 -070040import java.util.ArrayList;
Andreas Gampea8908752015-11-10 08:58:14 -080041import java.util.Collection;
Andreas Gampea8908752015-11-10 08:58:14 -080042import java.util.List;
Andreas Gampe77cc8172016-09-13 11:12:13 -070043import java.util.concurrent.TimeUnit;
Andreas Gampea8908752015-11-10 08:58:14 -080044
45/**
46 * A service for A/B OTA dexopting.
47 *
48 * {@hide}
49 */
50public class OtaDexoptService extends IOtaDexopt.Stub {
51 private final static String TAG = "OTADexopt";
52 private final static boolean DEBUG_DEXOPT = true;
Andreas Gampea8908752015-11-10 08:58:14 -080053
Andreas Gampec7e02c12016-08-01 22:08:26 -070054 // The synthetic library dependencies denoting "no checks."
55 private final static String[] NO_LIBRARIES = new String[] { "&" };
56
Andreas Gampedab38e02016-09-09 17:50:20 -070057 // The amount of "available" (free - low threshold) space necessary at the start of an OTA to
58 // not bulk-delete unused apps' odex files.
59 private final static long BULK_DELETE_THRESHOLD = 1024 * 1024 * 1024; // 1GB.
60
Andreas Gampea8908752015-11-10 08:58:14 -080061 private final Context mContext;
Andreas Gampea8908752015-11-10 08:58:14 -080062 private final PackageManagerService mPackageManagerService;
63
64 // TODO: Evaluate the need for WeakReferences here.
Andreas Gampecc241a52016-06-23 20:27:12 -070065
66 /**
Andreas Gampe115514b2016-07-28 16:54:41 -070067 * The list of dexopt invocations for all work.
Andreas Gampecc241a52016-06-23 20:27:12 -070068 */
Andreas Gampe115514b2016-07-28 16:54:41 -070069 private List<String> mDexoptCommands;
Andreas Gampecc241a52016-06-23 20:27:12 -070070
Andreas Gampebf062322016-06-10 15:21:39 -070071 private int completeSize;
Andreas Gampea8908752015-11-10 08:58:14 -080072
Andreas Gampe77cc8172016-09-13 11:12:13 -070073 // MetricsLogger properties.
74
75 // Space before and after.
76 private long availableSpaceBefore;
77 private long availableSpaceAfterBulkDelete;
78 private long availableSpaceAfterDexopt;
79
80 // Packages.
81 private int importantPackageCount;
82 private int otherPackageCount;
83
84 // Number of dexopt commands. This may be different from the count of packages.
85 private int dexoptCommandCountTotal;
86 private int dexoptCommandCountExecuted;
87
88 // For spent time.
89 private long otaDexoptTimeStart;
90
91
Andreas Gampea8908752015-11-10 08:58:14 -080092 public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
93 this.mContext = context;
94 this.mPackageManagerService = packageManagerService;
95
Andreas Gampeabcbe2f2016-02-26 11:25:36 -080096 // Now it's time to check whether we need to move any A/B artifacts.
97 moveAbArtifacts(packageManagerService.mInstaller);
Andreas Gampea8908752015-11-10 08:58:14 -080098 }
99
100 public static OtaDexoptService main(Context context,
101 PackageManagerService packageManagerService) {
102 OtaDexoptService ota = new OtaDexoptService(context, packageManagerService);
103 ServiceManager.addService("otadexopt", ota);
104
105 return ota;
106 }
107
108 @Override
109 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
110 String[] args, ResultReceiver resultReceiver) throws RemoteException {
111 (new OtaDexoptShellCommand(this)).exec(
112 this, in, out, err, args, resultReceiver);
113 }
114
115 @Override
116 public synchronized void prepare() throws RemoteException {
Andreas Gampe115514b2016-07-28 16:54:41 -0700117 if (mDexoptCommands != null) {
Andreas Gampea8908752015-11-10 08:58:14 -0800118 throw new IllegalStateException("already called prepare()");
119 }
Todd Kennedya8d4f482016-08-18 11:22:52 -0700120 final List<PackageParser.Package> important;
121 final List<PackageParser.Package> others;
Andreas Gampea8908752015-11-10 08:58:14 -0800122 synchronized (mPackageManagerService.mPackages) {
Andreas Gampe115514b2016-07-28 16:54:41 -0700123 // Important: the packages we need to run with ab-ota compiler-reason.
Todd Kennedya8d4f482016-08-18 11:22:52 -0700124 important = PackageManagerServiceUtils.getPackagesForDexopt(
David Brazdil6b4736d2016-02-04 11:54:17 +0000125 mPackageManagerService.mPackages.values(), mPackageManagerService);
Andreas Gampe115514b2016-07-28 16:54:41 -0700126 // Others: we should optimize this with the (first-)boot compiler-reason.
Todd Kennedya8d4f482016-08-18 11:22:52 -0700127 others = new ArrayList<>(mPackageManagerService.mPackages.values());
Andreas Gampe115514b2016-07-28 16:54:41 -0700128 others.removeAll(important);
129
130 // Pre-size the array list by over-allocating by a factor of 1.5.
131 mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
Todd Kennedya8d4f482016-08-18 11:22:52 -0700132 }
Andreas Gampe115514b2016-07-28 16:54:41 -0700133
Todd Kennedya8d4f482016-08-18 11:22:52 -0700134 for (PackageParser.Package p : important) {
135 // Make sure that core apps are optimized according to their own "reason".
136 // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
137 // (by default is speed-profile) they will be interepreted/JITed. This in itself is
138 // not a problem as we will end up doing profile guided compilation. However, some
139 // core apps may be loaded by system server which doesn't JIT and we need to make
140 // sure we don't interpret-only
141 int compilationReason = p.coreApp
142 ? PackageManagerService.REASON_CORE_APP
143 : PackageManagerService.REASON_AB_OTA;
144 mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason));
145 }
146 for (PackageParser.Package p : others) {
147 // We assume here that there are no core apps left.
148 if (p.coreApp) {
149 throw new IllegalStateException("Found a core app that's not important");
Andreas Gampe115514b2016-07-28 16:54:41 -0700150 }
Todd Kennedya8d4f482016-08-18 11:22:52 -0700151 mDexoptCommands.addAll(
152 generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
Andreas Gampea8908752015-11-10 08:58:14 -0800153 }
Andreas Gampe115514b2016-07-28 16:54:41 -0700154 completeSize = mDexoptCommands.size();
Andreas Gampedab38e02016-09-09 17:50:20 -0700155
Andreas Gampe77cc8172016-09-13 11:12:13 -0700156 long spaceAvailable = getAvailableSpace();
157 if (spaceAvailable < BULK_DELETE_THRESHOLD) {
Andreas Gampedab38e02016-09-09 17:50:20 -0700158 Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
159 + PackageManagerServiceUtils.packagesToString(others));
160 for (PackageParser.Package pkg : others) {
161 deleteOatArtifactsOfPackage(pkg);
162 }
163 }
Andreas Gampe77cc8172016-09-13 11:12:13 -0700164 long spaceAvailableNow = getAvailableSpace();
165
166 prepareMetricsLogging(important.size(), others.size(), spaceAvailable, spaceAvailableNow);
Andreas Gampea8908752015-11-10 08:58:14 -0800167 }
168
169 @Override
170 public synchronized void cleanup() throws RemoteException {
171 if (DEBUG_DEXOPT) {
172 Log.i(TAG, "Cleaning up OTA Dexopt state.");
173 }
Andreas Gampe115514b2016-07-28 16:54:41 -0700174 mDexoptCommands = null;
Andreas Gampe8d1d2ab2016-09-19 15:18:46 -0700175 availableSpaceAfterDexopt = getAvailableSpace();
Andreas Gampe77cc8172016-09-13 11:12:13 -0700176
177 performMetricsLogging();
Andreas Gampea8908752015-11-10 08:58:14 -0800178 }
179
180 @Override
181 public synchronized boolean isDone() throws RemoteException {
Andreas Gampe115514b2016-07-28 16:54:41 -0700182 if (mDexoptCommands == null) {
Andreas Gampea8908752015-11-10 08:58:14 -0800183 throw new IllegalStateException("done() called before prepare()");
184 }
185
Andreas Gampe115514b2016-07-28 16:54:41 -0700186 return mDexoptCommands.isEmpty();
Andreas Gampea8908752015-11-10 08:58:14 -0800187 }
188
189 @Override
Andreas Gampebf062322016-06-10 15:21:39 -0700190 public synchronized float getProgress() throws RemoteException {
Andreas Gampe115514b2016-07-28 16:54:41 -0700191 // Approximate the progress by the amount of already completed commands.
Andreas Gampebf062322016-06-10 15:21:39 -0700192 if (completeSize == 0) {
193 return 1f;
194 }
Andreas Gampe115514b2016-07-28 16:54:41 -0700195 int commandsLeft = mDexoptCommands.size();
196 return (completeSize - commandsLeft) / ((float)completeSize);
Andreas Gampecc241a52016-06-23 20:27:12 -0700197 }
198
199 @Override
200 public synchronized String nextDexoptCommand() throws RemoteException {
Andreas Gampe115514b2016-07-28 16:54:41 -0700201 if (mDexoptCommands == null) {
Andreas Gampecc241a52016-06-23 20:27:12 -0700202 throw new IllegalStateException("dexoptNextPackage() called before prepare()");
203 }
204
Andreas Gampe115514b2016-07-28 16:54:41 -0700205 if (mDexoptCommands.isEmpty()) {
206 return "(all done)";
207 }
Andreas Gampecc241a52016-06-23 20:27:12 -0700208
Andreas Gampe115514b2016-07-28 16:54:41 -0700209 String next = mDexoptCommands.remove(0);
Andreas Gampecc241a52016-06-23 20:27:12 -0700210
Andreas Gampedab38e02016-09-09 17:50:20 -0700211 if (getAvailableSpace() > 0) {
Andreas Gampe77cc8172016-09-13 11:12:13 -0700212 dexoptCommandCountExecuted++;
213
Andreas Gampe115514b2016-07-28 16:54:41 -0700214 return next;
215 } else {
Andreas Gampedab38e02016-09-09 17:50:20 -0700216 if (DEBUG_DEXOPT) {
217 Log.w(TAG, "Not enough space for OTA dexopt, stopping with "
218 + (mDexoptCommands.size() + 1) + " commands left.");
219 }
Andreas Gampe115514b2016-07-28 16:54:41 -0700220 mDexoptCommands.clear();
221 return "(no free space)";
Andreas Gampecc241a52016-06-23 20:27:12 -0700222 }
223 }
224
Andreas Gampedab38e02016-09-09 17:50:20 -0700225 private long getMainLowSpaceThreshold() {
Andreas Gampecc241a52016-06-23 20:27:12 -0700226 File dataDir = Environment.getDataDirectory();
227 @SuppressWarnings("deprecation")
228 long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
229 if (lowThreshold == 0) {
230 throw new IllegalStateException("Invalid low memory threshold");
231 }
Andreas Gampedab38e02016-09-09 17:50:20 -0700232 return lowThreshold;
233 }
234
235 /**
236 * Returns the difference of free space to the low-storage-space threshold. Positive values
237 * indicate free bytes.
238 */
239 private long getAvailableSpace() {
240 // TODO: If apps are not installed in the internal /data partition, we should compare
241 // against that storage's free capacity.
242 long lowThreshold = getMainLowSpaceThreshold();
243
244 File dataDir = Environment.getDataDirectory();
Andreas Gampecc241a52016-06-23 20:27:12 -0700245 long usableSpace = dataDir.getUsableSpace();
Andreas Gampedab38e02016-09-09 17:50:20 -0700246
247 return usableSpace - lowThreshold;
248 }
249
250 private static String getOatDir(PackageParser.Package pkg) {
251 if (!pkg.canHaveOatDir()) {
252 return null;
253 }
254 File codePath = new File(pkg.codePath);
255 if (codePath.isDirectory()) {
256 return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
257 }
258 return null;
259 }
260
261 private void deleteOatArtifactsOfPackage(PackageParser.Package pkg) {
262 String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
263 for (String codePath : pkg.getAllCodePaths()) {
264 for (String isa : instructionSets) {
265 try {
266 mPackageManagerService.mInstaller.deleteOdex(codePath, isa, getOatDir(pkg));
267 } catch (InstallerException e) {
268 Log.e(TAG, "Failed deleting oat files for " + codePath, e);
269 }
270 }
271 }
Andreas Gampe115514b2016-07-28 16:54:41 -0700272 }
Andreas Gampecc241a52016-06-23 20:27:12 -0700273
Andreas Gampe115514b2016-07-28 16:54:41 -0700274 /**
275 * Generate all dexopt commands for the given package.
276 */
277 private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
278 int compilationReason) {
Andreas Gampecc241a52016-06-23 20:27:12 -0700279 // Use our custom connection that just collects the commands.
280 RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
281 Installer collectingInstaller = new Installer(mContext, collectingConnection);
282
283 // Use the package manager install and install lock here for the OTA dex optimizer.
284 PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
285 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
Calin Juravle56562132016-07-12 13:56:34 +0100286
Andreas Gampec7e02c12016-08-01 22:08:26 -0700287 String[] libraryDependencies = pkg.usesLibraryFiles;
288 if (pkg.isSystemApp()) {
289 // For system apps, we want to avoid classpaths checks.
290 libraryDependencies = NO_LIBRARIES;
291 }
292
293 optimizer.performDexOpt(pkg, libraryDependencies,
Andreas Gampecc241a52016-06-23 20:27:12 -0700294 null /* ISAs */, false /* checkProfiles */,
Andreas Gampe37e5fdc2016-07-12 22:42:41 -0700295 getCompilerFilterForReason(compilationReason),
296 null /* CompilerStats.PackageStats */);
Andreas Gampecc241a52016-06-23 20:27:12 -0700297
Andreas Gampe115514b2016-07-28 16:54:41 -0700298 return collectingConnection.commands;
Andreas Gampebf062322016-06-10 15:21:39 -0700299 }
300
301 @Override
Andreas Gampea8908752015-11-10 08:58:14 -0800302 public synchronized void dexoptNextPackage() throws RemoteException {
Andreas Gampe115514b2016-07-28 16:54:41 -0700303 throw new UnsupportedOperationException();
Andreas Gampea8908752015-11-10 08:58:14 -0800304 }
305
Andreas Gampeabcbe2f2016-02-26 11:25:36 -0800306 private void moveAbArtifacts(Installer installer) {
Andreas Gampe115514b2016-07-28 16:54:41 -0700307 if (mDexoptCommands != null) {
Andreas Gampeabcbe2f2016-02-26 11:25:36 -0800308 throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
Andreas Gampea8908752015-11-10 08:58:14 -0800309 }
Andreas Gampeabcbe2f2016-02-26 11:25:36 -0800310
311 // Look into all packages.
312 Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages();
313 for (PackageParser.Package pkg : pkgs) {
314 if (pkg == null) {
315 continue;
316 }
317
318 // Does the package have code? If not, there won't be any artifacts.
319 if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
320 continue;
321 }
322 if (pkg.codePath == null) {
323 Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath");
324 continue;
325 }
326
327 // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into
328 // /data/ota and moved into the dalvik-cache already.
329 if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) {
330 continue;
331 }
332
333 final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
334 final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
335 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
336 for (String dexCodeInstructionSet : dexCodeInstructionSets) {
337 for (String path : paths) {
338 String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)).
339 getAbsolutePath();
340
341 // TODO: Check first whether there is an artifact, to save the roundtrip time.
342
343 try {
344 installer.moveAb(path, dexCodeInstructionSet, oatDir);
345 } catch (InstallerException e) {
346 }
347 }
Andreas Gampea8908752015-11-10 08:58:14 -0800348 }
349 }
Andreas Gampea8908752015-11-10 08:58:14 -0800350 }
351
Andreas Gampe77cc8172016-09-13 11:12:13 -0700352 /**
353 * Initialize logging fields.
354 */
355 private void prepareMetricsLogging(int important, int others, long spaceBegin, long spaceBulk) {
356 availableSpaceBefore = spaceBegin;
357 availableSpaceAfterBulkDelete = spaceBulk;
358 availableSpaceAfterDexopt = 0;
359
360 importantPackageCount = important;
361 otherPackageCount = others;
362
363 dexoptCommandCountTotal = mDexoptCommands.size();
364 dexoptCommandCountExecuted = 0;
365
366 otaDexoptTimeStart = System.nanoTime();
367 }
368
369 private static int inMegabytes(long value) {
370 long in_mega_bytes = value / (1024 * 1024);
371 if (in_mega_bytes > Integer.MAX_VALUE) {
372 Log.w(TAG, "Recording " + in_mega_bytes + "MB of free space, overflowing range");
373 return Integer.MAX_VALUE;
374 }
375 return (int)in_mega_bytes;
376 }
377
378 private void performMetricsLogging() {
379 long finalTime = System.nanoTime();
380
381 MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb",
382 inMegabytes(availableSpaceBefore));
383 MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb",
384 inMegabytes(availableSpaceAfterBulkDelete));
385 MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb",
386 inMegabytes(availableSpaceAfterDexopt));
387
388 MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages",
389 importantPackageCount);
390 MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount);
391
392 MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal);
393 MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed",
394 dexoptCommandCountExecuted);
395
396 final int elapsedTimeSeconds =
397 (int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart);
398 MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds);
399 }
400
Andreas Gampea8908752015-11-10 08:58:14 -0800401 private static class OTADexoptPackageDexOptimizer extends
402 PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
403
404 public OTADexoptPackageDexOptimizer(Installer installer, Object installLock,
405 Context context) {
406 super(installer, installLock, context, "*otadexopt*");
407 }
408
409 @Override
410 protected int adjustDexoptFlags(int dexoptFlags) {
411 // Add the OTA flag.
412 return dexoptFlags | DEXOPT_OTA;
413 }
414
Andreas Gampea8908752015-11-10 08:58:14 -0800415 }
Andreas Gampecc241a52016-06-23 20:27:12 -0700416
417 private static class RecordingInstallerConnection extends InstallerConnection {
418 public List<String> commands = new ArrayList<String>(1);
419
420 @Override
421 public void setWarnIfHeld(Object warnIfHeld) {
422 throw new IllegalStateException("Should not reach here");
423 }
424
425 @Override
426 public synchronized String transact(String cmd) {
427 commands.add(cmd);
428 return "0";
429 }
430
431 @Override
432 public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
433 throw new IllegalStateException("Should not reach here");
434 }
435
436 @Override
437 public boolean dumpProfiles(String gid, String packageName, String codePaths)
438 throws InstallerException {
439 throw new IllegalStateException("Should not reach here");
440 }
441
442 @Override
443 public void disconnect() {
444 throw new IllegalStateException("Should not reach here");
445 }
446
447 @Override
448 public void waitForConnection() {
449 throw new IllegalStateException("Should not reach here");
450 }
451 }
Andreas Gampea8908752015-11-10 08:58:14 -0800452}