Home | History | Annotate | Download | only in pm
      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 
     17 package com.android.server.pm;
     18 
     19 import static com.android.server.pm.Installer.DEXOPT_OTA;
     20 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
     21 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
     22 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
     23 
     24 import android.content.Context;
     25 import android.content.pm.IOtaDexopt;
     26 import android.content.pm.PackageParser;
     27 import android.os.Environment;
     28 import android.os.RemoteException;
     29 import android.os.ResultReceiver;
     30 import android.os.ServiceManager;
     31 import android.os.storage.StorageManager;
     32 import android.util.Log;
     33 import android.util.Slog;
     34 
     35 import com.android.internal.os.InstallerConnection.InstallerException;
     36 
     37 import java.io.File;
     38 import java.io.FileDescriptor;
     39 import java.util.Collection;
     40 import java.util.List;
     41 
     42 /**
     43  * A service for A/B OTA dexopting.
     44  *
     45  * {@hide}
     46  */
     47 public 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