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