OtaDexoptService.java revision bf06232f4d440ced8230662a77ca0e8ece6383ca
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.InstallerException; 36 37import java.io.File; 38import java.io.FileDescriptor; 39import java.util.Collection; 40import java.util.List; 41 42/** 43 * A service for A/B OTA dexopting. 44 * 45 * {@hide} 46 */ 47public class OtaDexoptService extends IOtaDexopt.Stub { 48 private final static String TAG = "OTADexopt"; 49 private final static boolean DEBUG_DEXOPT = true; 50 51 private final Context mContext; 52 private final PackageDexOptimizer mPackageDexOptimizer; 53 private final PackageManagerService mPackageManagerService; 54 55 // TODO: Evaluate the need for WeakReferences here. 56 private List<PackageParser.Package> mDexoptPackages; 57 private int completeSize; 58 59 public OtaDexoptService(Context context, PackageManagerService packageManagerService) { 60 this.mContext = context; 61 this.mPackageManagerService = packageManagerService; 62 63 // Use the package manager install and install lock here for the OTA dex optimizer. 64 mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller, 65 packageManagerService.mInstallLock, context); 66 67 // Now it's time to check whether we need to move any A/B artifacts. 68 moveAbArtifacts(packageManagerService.mInstaller); 69 } 70 71 public static OtaDexoptService main(Context context, 72 PackageManagerService packageManagerService) { 73 OtaDexoptService ota = new OtaDexoptService(context, packageManagerService); 74 ServiceManager.addService("otadexopt", ota); 75 76 return ota; 77 } 78 79 @Override 80 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 81 String[] args, ResultReceiver resultReceiver) throws RemoteException { 82 (new OtaDexoptShellCommand(this)).exec( 83 this, in, out, err, args, resultReceiver); 84 } 85 86 @Override 87 public synchronized void prepare() throws RemoteException { 88 if (mDexoptPackages != null) { 89 throw new IllegalStateException("already called prepare()"); 90 } 91 synchronized (mPackageManagerService.mPackages) { 92 mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt( 93 mPackageManagerService.mPackages.values(), mPackageManagerService); 94 } 95 completeSize = mDexoptPackages.size(); 96 } 97 98 @Override 99 public synchronized void cleanup() throws RemoteException { 100 if (DEBUG_DEXOPT) { 101 Log.i(TAG, "Cleaning up OTA Dexopt state."); 102 } 103 mDexoptPackages = null; 104 } 105 106 @Override 107 public synchronized boolean isDone() throws RemoteException { 108 if (mDexoptPackages == null) { 109 throw new IllegalStateException("done() called before prepare()"); 110 } 111 112 return mDexoptPackages.isEmpty(); 113 } 114 115 @Override 116 public synchronized float getProgress() throws RemoteException { 117 if (completeSize == 0) { 118 return 1f; 119 } 120 return (completeSize - mDexoptPackages.size()) / ((float)completeSize); 121 } 122 123 @Override 124 public synchronized void dexoptNextPackage() throws RemoteException { 125 if (mDexoptPackages == null) { 126 throw new IllegalStateException("dexoptNextPackage() called before prepare()"); 127 } 128 if (mDexoptPackages.isEmpty()) { 129 // Tolerate repeated calls. 130 return; 131 } 132 133 PackageParser.Package nextPackage = mDexoptPackages.remove(0); 134 135 if (DEBUG_DEXOPT) { 136 Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt."); 137 } 138 139 // Check for low space. 140 // TODO: If apps are not installed in the internal /data partition, we should compare 141 // against that storage's free capacity. 142 File dataDir = Environment.getDataDirectory(); 143 @SuppressWarnings("deprecation") 144 long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir); 145 if (lowThreshold == 0) { 146 throw new IllegalStateException("Invalid low memory threshold"); 147 } 148 long usableSpace = dataDir.getUsableSpace(); 149 if (usableSpace < lowThreshold) { 150 Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " + 151 usableSpace); 152 return; 153 } 154 155 mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, 156 null /* ISAs */, false /* checkProfiles */, 157 getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA)); 158 } 159 160 private void moveAbArtifacts(Installer installer) { 161 if (mDexoptPackages != null) { 162 throw new IllegalStateException("Should not be ota-dexopting when trying to move."); 163 } 164 165 // Look into all packages. 166 Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages(); 167 for (PackageParser.Package pkg : pkgs) { 168 if (pkg == null) { 169 continue; 170 } 171 172 // Does the package have code? If not, there won't be any artifacts. 173 if (!PackageDexOptimizer.canOptimizePackage(pkg)) { 174 continue; 175 } 176 if (pkg.codePath == null) { 177 Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath"); 178 continue; 179 } 180 181 // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into 182 // /data/ota and moved into the dalvik-cache already. 183 if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) { 184 continue; 185 } 186 187 final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo); 188 final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly(); 189 final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); 190 for (String dexCodeInstructionSet : dexCodeInstructionSets) { 191 for (String path : paths) { 192 String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)). 193 getAbsolutePath(); 194 195 // TODO: Check first whether there is an artifact, to save the roundtrip time. 196 197 try { 198 installer.moveAb(path, dexCodeInstructionSet, oatDir); 199 } catch (InstallerException e) { 200 } 201 } 202 } 203 } 204 } 205 206 private static class OTADexoptPackageDexOptimizer extends 207 PackageDexOptimizer.ForcedUpdatePackageDexOptimizer { 208 209 public OTADexoptPackageDexOptimizer(Installer installer, Object installLock, 210 Context context) { 211 super(installer, installLock, context, "*otadexopt*"); 212 } 213 214 @Override 215 protected int adjustDexoptFlags(int dexoptFlags) { 216 // Add the OTA flag. 217 return dexoptFlags | DEXOPT_OTA; 218 } 219 220 } 221} 222