OtaDexoptService.java revision 6b4736d604fd91aaedc6f3fe9be5a1e757aab86c
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 android.app.AppGlobals;
20import android.content.Context;
21import android.content.Intent;
22import android.content.pm.IOtaDexopt;
23import android.content.pm.PackageParser;
24import android.content.pm.PackageParser.Package;
25import android.content.pm.ResolveInfo;
26import android.os.Environment;
27import android.os.RemoteException;
28import android.os.ResultReceiver;
29import android.os.ServiceManager;
30import android.os.UserHandle;
31import android.os.storage.StorageManager;
32import android.util.ArraySet;
33import android.util.Log;
34
35import dalvik.system.DexFile;
36
37import java.io.File;
38import java.io.FileDescriptor;
39import java.util.ArrayList;
40import java.util.Collection;
41import java.util.Date;
42import java.util.HashSet;
43import java.util.Iterator;
44import java.util.LinkedList;
45import java.util.List;
46import java.util.Set;
47
48import static com.android.server.pm.Installer.DEXOPT_OTA;
49
50/**
51 * A service for A/B OTA dexopting.
52 *
53 * {@hide}
54 */
55public class OtaDexoptService extends IOtaDexopt.Stub {
56    private final static String TAG = "OTADexopt";
57    private final static boolean DEBUG_DEXOPT = true;
58
59    private final Context mContext;
60    private final PackageDexOptimizer mPackageDexOptimizer;
61    private final PackageManagerService mPackageManagerService;
62
63    // TODO: Evaluate the need for WeakReferences here.
64    private List<PackageParser.Package> mDexoptPackages;
65
66    public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
67        this.mContext = context;
68        this.mPackageManagerService = packageManagerService;
69
70        // Use the package manager install and install lock here for the OTA dex optimizer.
71        mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller,
72                packageManagerService.mInstallLock, context);
73    }
74
75    public static OtaDexoptService main(Context context,
76            PackageManagerService packageManagerService) {
77        OtaDexoptService ota = new OtaDexoptService(context, packageManagerService);
78        ServiceManager.addService("otadexopt", ota);
79
80        return ota;
81    }
82
83    @Override
84    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
85            String[] args, ResultReceiver resultReceiver) throws RemoteException {
86        (new OtaDexoptShellCommand(this)).exec(
87                this, in, out, err, args, resultReceiver);
88    }
89
90    @Override
91    public synchronized void prepare() throws RemoteException {
92        if (mDexoptPackages != null) {
93            throw new IllegalStateException("already called prepare()");
94        }
95        synchronized (mPackageManagerService.mPackages) {
96            mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
97                    mPackageManagerService.mPackages.values(), mPackageManagerService);
98        }
99    }
100
101    @Override
102    public synchronized void cleanup() throws RemoteException {
103        if (DEBUG_DEXOPT) {
104            Log.i(TAG, "Cleaning up OTA Dexopt state.");
105        }
106        mDexoptPackages = null;
107    }
108
109    @Override
110    public synchronized boolean isDone() throws RemoteException {
111        if (mDexoptPackages == null) {
112            throw new IllegalStateException("done() called before prepare()");
113        }
114
115        return mDexoptPackages.isEmpty();
116    }
117
118    @Override
119    public synchronized void dexoptNextPackage() throws RemoteException {
120        if (mDexoptPackages == null) {
121            throw new IllegalStateException("dexoptNextPackage() called before prepare()");
122        }
123        if (mDexoptPackages.isEmpty()) {
124            // Tolerate repeated calls.
125            return;
126        }
127
128        PackageParser.Package nextPackage = mDexoptPackages.remove(0);
129
130        if (DEBUG_DEXOPT) {
131            Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
132        }
133
134        // Check for low space.
135        // TODO: If apps are not installed in the internal /data partition, we should compare
136        //       against that storage's free capacity.
137        File dataDir = Environment.getDataDirectory();
138        long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
139        if (lowThreshold == 0) {
140            throw new IllegalStateException("Invalid low memory threshold");
141        }
142        long usableSpace = dataDir.getUsableSpace();
143        if (usableSpace < lowThreshold) {
144            Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
145                    usableSpace);
146            return;
147        }
148
149        mPackageDexOptimizer.performDexOpt(nextPackage, null /* ISAs */, false /* useProfiles */,
150                false /* extractOnly */);
151    }
152
153    private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
154        List<ResolveInfo> ris = null;
155        try {
156            ris = AppGlobals.getPackageManager().queryIntentReceivers(
157                    intent, null, 0, userId);
158        } catch (RemoteException e) {
159        }
160        ArraySet<String> pkgNames = new ArraySet<String>(ris == null ? 0 : ris.size());
161        if (ris != null) {
162            for (ResolveInfo ri : ris) {
163                pkgNames.add(ri.activityInfo.packageName);
164            }
165        }
166        return pkgNames;
167    }
168
169    private static class OTADexoptPackageDexOptimizer extends
170            PackageDexOptimizer.ForcedUpdatePackageDexOptimizer {
171
172        public OTADexoptPackageDexOptimizer(Installer installer, Object installLock,
173                Context context) {
174            super(installer, installLock, context, "*otadexopt*");
175        }
176
177        @Override
178        protected int adjustDexoptFlags(int dexoptFlags) {
179            // Add the OTA flag.
180            return dexoptFlags | DEXOPT_OTA;
181        }
182
183        @Override
184        protected void recordSuccessfulDexopt(Package pkg, String instructionSet) {
185            // Never record the dexopt, as it's in the B partition.
186        }
187
188    }
189}
190