OtaDexoptService.java revision a8d4f489974f3ea8f73990cbabbce205343fb926
1/* 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 19import static com.android.server.pm.Installer.DEXOPT_OTA; 20import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; 21import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; 22import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; 23 24import android.content.Context; 25import android.content.pm.IOtaDexopt; 26import android.content.pm.PackageParser; 27import android.os.Environment; 28import android.os.RemoteException; 29import android.os.ResultReceiver; 30import android.os.ServiceManager; 31import android.os.storage.StorageManager; 32import android.util.Log; 33import android.util.Slog; 34 35import com.android.internal.os.InstallerConnection; 36import com.android.internal.os.InstallerConnection.InstallerException; 37 38import java.io.File; 39import java.io.FileDescriptor; 40import java.util.ArrayList; 41import java.util.Collection; 42import java.util.List; 43 44/** 45 * A service for A/B OTA dexopting. 46 * 47 * {@hide} 48 */ 49public class OtaDexoptService extends IOtaDexopt.Stub { 50 private final static String TAG = "OTADexopt"; 51 private final static boolean DEBUG_DEXOPT = true; 52 53 // The synthetic library dependencies denoting "no checks." 54 private final static String[] NO_LIBRARIES = new String[] { "&" }; 55 56 private final Context mContext; 57 private final PackageManagerService mPackageManagerService; 58 59 // TODO: Evaluate the need for WeakReferences here. 60 61 /** 62 * The list of dexopt invocations for all work. 63 */ 64 private List<String> mDexoptCommands; 65 66 private int completeSize; 67 68 public OtaDexoptService(Context context, PackageManagerService packageManagerService) { 69 this.mContext = context; 70 this.mPackageManagerService = packageManagerService; 71 72 // Now it's time to check whether we need to move any A/B artifacts. 73 moveAbArtifacts(packageManagerService.mInstaller); 74 } 75 76 public static OtaDexoptService main(Context context, 77 PackageManagerService packageManagerService) { 78 OtaDexoptService ota = new OtaDexoptService(context, packageManagerService); 79 ServiceManager.addService("otadexopt", ota); 80 81 return ota; 82 } 83 84 @Override 85 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 86 String[] args, ResultReceiver resultReceiver) throws RemoteException { 87 (new OtaDexoptShellCommand(this)).exec( 88 this, in, out, err, args, resultReceiver); 89 } 90 91 @Override 92 public synchronized void prepare() throws RemoteException { 93 if (mDexoptCommands != null) { 94 throw new IllegalStateException("already called prepare()"); 95 } 96 final List<PackageParser.Package> important; 97 final List<PackageParser.Package> others; 98 synchronized (mPackageManagerService.mPackages) { 99 // Important: the packages we need to run with ab-ota compiler-reason. 100 important = PackageManagerServiceUtils.getPackagesForDexopt( 101 mPackageManagerService.mPackages.values(), mPackageManagerService); 102 // Others: we should optimize this with the (first-)boot compiler-reason. 103 others = new ArrayList<>(mPackageManagerService.mPackages.values()); 104 others.removeAll(important); 105 106 // Pre-size the array list by over-allocating by a factor of 1.5. 107 mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2); 108 } 109 110 for (PackageParser.Package p : important) { 111 // Make sure that core apps are optimized according to their own "reason". 112 // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed 113 // (by default is speed-profile) they will be interepreted/JITed. This in itself is 114 // not a problem as we will end up doing profile guided compilation. However, some 115 // core apps may be loaded by system server which doesn't JIT and we need to make 116 // sure we don't interpret-only 117 int compilationReason = p.coreApp 118 ? PackageManagerService.REASON_CORE_APP 119 : PackageManagerService.REASON_AB_OTA; 120 mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason)); 121 } 122 for (PackageParser.Package p : others) { 123 // We assume here that there are no core apps left. 124 if (p.coreApp) { 125 throw new IllegalStateException("Found a core app that's not important"); 126 } 127 mDexoptCommands.addAll( 128 generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT)); 129 } 130 completeSize = mDexoptCommands.size(); 131 } 132 133 @Override 134 public synchronized void cleanup() throws RemoteException { 135 if (DEBUG_DEXOPT) { 136 Log.i(TAG, "Cleaning up OTA Dexopt state."); 137 } 138 mDexoptCommands = null; 139 } 140 141 @Override 142 public synchronized boolean isDone() throws RemoteException { 143 if (mDexoptCommands == null) { 144 throw new IllegalStateException("done() called before prepare()"); 145 } 146 147 return mDexoptCommands.isEmpty(); 148 } 149 150 @Override 151 public synchronized float getProgress() throws RemoteException { 152 // Approximate the progress by the amount of already completed commands. 153 if (completeSize == 0) { 154 return 1f; 155 } 156 int commandsLeft = mDexoptCommands.size(); 157 return (completeSize - commandsLeft) / ((float)completeSize); 158 } 159 160 @Override 161 public synchronized String nextDexoptCommand() throws RemoteException { 162 if (mDexoptCommands == null) { 163 throw new IllegalStateException("dexoptNextPackage() called before prepare()"); 164 } 165 166 if (mDexoptCommands.isEmpty()) { 167 return "(all done)"; 168 } 169 170 String next = mDexoptCommands.remove(0); 171 172 if (IsFreeSpaceAvailable()) { 173 return next; 174 } else { 175 mDexoptCommands.clear(); 176 return "(no free space)"; 177 } 178 } 179 180 /** 181 * Check for low space. Returns true if there's space left. 182 */ 183 private boolean IsFreeSpaceAvailable() { 184 // TODO: If apps are not installed in the internal /data partition, we should compare 185 // against that storage's free capacity. 186 File dataDir = Environment.getDataDirectory(); 187 @SuppressWarnings("deprecation") 188 long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir); 189 if (lowThreshold == 0) { 190 throw new IllegalStateException("Invalid low memory threshold"); 191 } 192 long usableSpace = dataDir.getUsableSpace(); 193 return (usableSpace >= lowThreshold); 194 } 195 196 /** 197 * Generate all dexopt commands for the given package. 198 */ 199 private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg, 200 int compilationReason) { 201 // Use our custom connection that just collects the commands. 202 RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection(); 203 Installer collectingInstaller = new Installer(mContext, collectingConnection); 204 205 // Use the package manager install and install lock here for the OTA dex optimizer. 206 PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( 207 collectingInstaller, mPackageManagerService.mInstallLock, mContext); 208 209 String[] libraryDependencies = pkg.usesLibraryFiles; 210 if (pkg.isSystemApp()) { 211 // For system apps, we want to avoid classpaths checks. 212 libraryDependencies = NO_LIBRARIES; 213 } 214 215 optimizer.performDexOpt(pkg, libraryDependencies, 216 null /* ISAs */, false /* checkProfiles */, 217 getCompilerFilterForReason(compilationReason), 218 null /* CompilerStats.PackageStats */); 219 220 return collectingConnection.commands; 221 } 222 223 @Override 224 public synchronized void dexoptNextPackage() throws RemoteException { 225 throw new UnsupportedOperationException(); 226 } 227 228 private void moveAbArtifacts(Installer installer) { 229 if (mDexoptCommands != null) { 230 throw new IllegalStateException("Should not be ota-dexopting when trying to move."); 231 } 232 233 // Look into all packages. 234 Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages(); 235 for (PackageParser.Package pkg : pkgs) { 236 if (pkg == null) { 237 continue; 238 } 239 240 // Does the package have code? If not, there won't be any artifacts. 241 if (!PackageDexOptimizer.canOptimizePackage(pkg)) { 242 continue; 243 } 244 if (pkg.codePath == null) { 245 Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath"); 246 continue; 247 } 248 249 // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into 250 // /data/ota and moved into the dalvik-cache already. 251 if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) { 252 continue; 253 } 254 255 final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo); 256 final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly(); 257 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); 258 for (String dexCodeInstructionSet : dexCodeInstructionSets) { 259 for (String path : paths) { 260 String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)). 261 getAbsolutePath(); 262 263 // TODO: Check first whether there is an artifact, to save the roundtrip time. 264 265 try { 266 installer.moveAb(path, dexCodeInstructionSet, oatDir); 267 } catch (InstallerException e) { 268 } 269 } 270 } 271 } 272 } 273 274 private static class OTADexoptPackageDexOptimizer extends 275 PackageDexOptimizer.ForcedUpdatePackageDexOptimizer { 276 277 public OTADexoptPackageDexOptimizer(Installer installer, Object installLock, 278 Context context) { 279 super(installer, installLock, context, "*otadexopt*"); 280 } 281 282 @Override 283 protected int adjustDexoptFlags(int dexoptFlags) { 284 // Add the OTA flag. 285 return dexoptFlags | DEXOPT_OTA; 286 } 287 288 } 289 290 private static class RecordingInstallerConnection extends InstallerConnection { 291 public List<String> commands = new ArrayList<String>(1); 292 293 @Override 294 public void setWarnIfHeld(Object warnIfHeld) { 295 throw new IllegalStateException("Should not reach here"); 296 } 297 298 @Override 299 public synchronized String transact(String cmd) { 300 commands.add(cmd); 301 return "0"; 302 } 303 304 @Override 305 public boolean mergeProfiles(int uid, String pkgName) throws InstallerException { 306 throw new IllegalStateException("Should not reach here"); 307 } 308 309 @Override 310 public boolean dumpProfiles(String gid, String packageName, String codePaths) 311 throws InstallerException { 312 throw new IllegalStateException("Should not reach here"); 313 } 314 315 @Override 316 public void disconnect() { 317 throw new IllegalStateException("Should not reach here"); 318 } 319 320 @Override 321 public void waitForConnection() { 322 throw new IllegalStateException("Should not reach here"); 323 } 324 } 325} 326