PackageDexOptimizer.java revision 27c073796978106746e4a51f2100b29068ab37f6
1/*
2 * Copyright (C) 2015 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 android.annotation.Nullable;
20import android.content.Context;
21import android.content.pm.ApplicationInfo;
22import android.content.pm.PackageParser;
23import android.os.PowerManager;
24import android.os.UserHandle;
25import android.os.WorkSource;
26import android.util.ArraySet;
27import android.util.Log;
28import android.util.Slog;
29
30import java.io.File;
31import java.io.IOException;
32import java.util.ArrayList;
33import java.util.List;
34
35import dalvik.system.DexFile;
36
37import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
38import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
39import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
40import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
41import static com.android.server.pm.Installer.DEXOPT_USEJIT;
42import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
43import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
44
45/**
46 * Helper class for running dexopt command on packages.
47 */
48final class PackageDexOptimizer {
49    private static final String TAG = "PackageManager.DexOptimizer";
50    static final String OAT_DIR_NAME = "oat";
51    // TODO b/19550105 Remove error codes and use exceptions
52    static final int DEX_OPT_SKIPPED = 0;
53    static final int DEX_OPT_PERFORMED = 1;
54    static final int DEX_OPT_DEFERRED = 2;
55    static final int DEX_OPT_FAILED = -1;
56
57    private final PackageManagerService mPackageManagerService;
58
59    private final PowerManager.WakeLock mDexoptWakeLock;
60    private volatile boolean mSystemReady;
61
62    PackageDexOptimizer(PackageManagerService packageManagerService) {
63        this.mPackageManagerService = packageManagerService;
64        PowerManager powerManager = (PowerManager)packageManagerService.mContext.getSystemService(
65                Context.POWER_SERVICE);
66        mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*dexopt*");
67    }
68
69    /**
70     * Performs dexopt on all code paths and libraries of the specified package for specified
71     * instruction sets.
72     *
73     * <p>Calls to {@link com.android.server.pm.Installer#dexopt} are synchronized on
74     * {@link PackageManagerService#mInstallLock}.
75     */
76    int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
77            boolean inclDependencies) {
78        ArraySet<String> done;
79        if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
80            done = new ArraySet<String>();
81            done.add(pkg.packageName);
82        } else {
83            done = null;
84        }
85        synchronized (mPackageManagerService.mInstallLock) {
86            final boolean useLock = mSystemReady;
87            if (useLock) {
88                mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
89                mDexoptWakeLock.acquire();
90            }
91            try {
92                return performDexOptLI(pkg, instructionSets, done);
93            } finally {
94                if (useLock) {
95                    mDexoptWakeLock.release();
96                }
97            }
98        }
99    }
100
101    private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
102            ArraySet<String> done) {
103        final String[] instructionSets = targetInstructionSets != null ?
104                targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
105
106        if (done != null) {
107            done.add(pkg.packageName);
108            if (pkg.usesLibraries != null) {
109                performDexOptLibsLI(pkg.usesLibraries, instructionSets, done);
110            }
111            if (pkg.usesOptionalLibraries != null) {
112                performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, done);
113            }
114        }
115
116        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
117            return DEX_OPT_SKIPPED;
118        }
119
120        final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
121        final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
122
123        final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
124        boolean performedDexOpt = false;
125        // There are three basic cases here:
126        // 1.) we need to dexopt, either because we are forced or it is needed
127        // 2.) we are deferring a needed dexopt
128        // 3.) we are skipping an unneeded dexopt
129        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
130        for (String dexCodeInstructionSet : dexCodeInstructionSets) {
131            if (pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
132                continue;
133            }
134
135            for (String path : paths) {
136                final int dexoptNeeded;
137                try {
138                    dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
139                            dexCodeInstructionSet, /* defer */false);
140                } catch (IOException ioe) {
141                    Slog.w(TAG, "IOException reading apk: " + path, ioe);
142                    return DEX_OPT_FAILED;
143                }
144
145                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
146                    final String dexoptType;
147                    String oatDir = null;
148                    if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
149                        dexoptType = "dex2oat";
150                        oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
151                    } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
152                        dexoptType = "patchoat";
153                    } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
154                        dexoptType = "self patchoat";
155                    } else {
156                        throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);
157                    }
158
159                    Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
160                            + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
161                            + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
162                            + " oatDir = " + oatDir);
163                    final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
164                    final int dexFlags =
165                            (!pkg.isForwardLocked() ? DEXOPT_PUBLIC : 0)
166                            | (vmSafeMode ? DEXOPT_SAFEMODE : 0)
167                            | (debuggable ? DEXOPT_DEBUGGABLE : 0)
168                            | DEXOPT_BOOTCOMPLETE;
169                    final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
170                            pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
171
172                    // Dex2oat might fail due to compiler / verifier errors.
173                    if (ret == 0) {
174                        performedDexOpt = true;
175                    }
176                }
177            }
178
179            // At this point we haven't failed dexopt and we haven't deferred dexopt. We must
180            // either have either succeeded dexopt, or have had getDexOptNeeded tell us
181            // it isn't required. We therefore mark that this package doesn't need dexopt unless
182            // it's forced. performedDexOpt will tell us whether we performed dex-opt or skipped
183            // it.
184            pkg.mDexOptPerformed.add(dexCodeInstructionSet);
185        }
186
187        // If we've gotten here, we're sure that no error occurred and that we haven't
188        // deferred dex-opt. We've either dex-opted one more paths or instruction sets or
189        // we've skipped all of them because they are up to date. In both cases this
190        // package doesn't need dexopt any longer.
191        return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
192    }
193
194    /**
195     * Creates oat dir for the specified package. In certain cases oat directory
196     * <strong>cannot</strong> be created:
197     * <ul>
198     *      <li>{@code pkg} is a system app, which is not updated.</li>
199     *      <li>Package location is not a directory, i.e. monolithic install.</li>
200     * </ul>
201     *
202     * @return Absolute path to the oat directory or null, if oat directory
203     * cannot be created.
204     */
205    @Nullable
206    private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) {
207        if (!pkg.canHaveOatDir()) {
208            return null;
209        }
210        File codePath = new File(pkg.codePath);
211        if (codePath.isDirectory()) {
212            File oatDir = getOatDir(codePath);
213            mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
214                    dexInstructionSet);
215            return oatDir.getAbsolutePath();
216        }
217        return null;
218    }
219
220    static File getOatDir(File codePath) {
221        return new File(codePath, OAT_DIR_NAME);
222    }
223
224    private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets,
225            ArraySet<String> done) {
226        for (String libName : libs) {
227            PackageParser.Package libPkg = mPackageManagerService.findSharedNonSystemLibrary(
228                    libName);
229            if (libPkg != null && !done.contains(libName)) {
230                performDexOptLI(libPkg, instructionSets, done);
231            }
232        }
233    }
234
235    void systemReady() {
236        mSystemReady = true;
237    }
238}
239