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