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