1/*
2 * Copyright (C) 2008 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.PackageStats;
22import android.os.Build;
23import android.os.IBinder;
24import android.os.IBinder.DeathRecipient;
25import android.os.IInstalld;
26import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.text.format.DateUtils;
29import android.util.Slog;
30
31import com.android.internal.os.BackgroundThread;
32import com.android.server.SystemService;
33
34import dalvik.system.VMRuntime;
35
36public class Installer extends SystemService {
37    private static final String TAG = "Installer";
38
39    /* ***************************************************************************
40     * IMPORTANT: These values are passed to native code. Keep them in sync with
41     * frameworks/native/cmds/installd/installd.h
42     * **************************************************************************/
43    /** Application should be visible to everyone */
44    public static final int DEXOPT_PUBLIC         = 1 << 1;
45    /** Application wants to allow debugging of its code */
46    public static final int DEXOPT_DEBUGGABLE     = 1 << 2;
47    /** The system boot has finished */
48    public static final int DEXOPT_BOOTCOMPLETE   = 1 << 3;
49    /** Hint that the dexopt type is profile-guided. */
50    public static final int DEXOPT_PROFILE_GUIDED = 1 << 4;
51    /** The compilation is for a secondary dex file. */
52    public static final int DEXOPT_SECONDARY_DEX  = 1 << 5;
53    /** Ignore the result of dexoptNeeded and force compilation. */
54    public static final int DEXOPT_FORCE          = 1 << 6;
55    /** Indicates that the dex file passed to dexopt in on CE storage. */
56    public static final int DEXOPT_STORAGE_CE     = 1 << 7;
57    /** Indicates that the dex file passed to dexopt in on DE storage. */
58    public static final int DEXOPT_STORAGE_DE     = 1 << 8;
59
60    // NOTE: keep in sync with installd
61    public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
62    public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
63    public static final int FLAG_USE_QUOTA = 1 << 12;
64    public static final int FLAG_FREE_CACHE_V2 = 1 << 13;
65    public static final int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14;
66    public static final int FLAG_FREE_CACHE_NOOP = 1 << 15;
67    public static final int FLAG_FORCE = 1 << 16;
68
69    private final boolean mIsolated;
70
71    private volatile IInstalld mInstalld;
72    private volatile Object mWarnIfHeld;
73
74    public Installer(Context context) {
75        this(context, false);
76    }
77
78    /**
79     * @param isolated indicates if this object should <em>not</em> connect to
80     *            the real {@code installd}. All remote calls will be ignored
81     *            unless you extend this class and intercept them.
82     */
83    public Installer(Context context, boolean isolated) {
84        super(context);
85        mIsolated = isolated;
86    }
87
88    /**
89     * Yell loudly if someone tries making future calls while holding a lock on
90     * the given object.
91     */
92    public void setWarnIfHeld(Object warnIfHeld) {
93        mWarnIfHeld = warnIfHeld;
94    }
95
96    @Override
97    public void onStart() {
98        if (mIsolated) {
99            mInstalld = null;
100        } else {
101            connect();
102        }
103    }
104
105    private void connect() {
106        IBinder binder = ServiceManager.getService("installd");
107        if (binder != null) {
108            try {
109                binder.linkToDeath(new DeathRecipient() {
110                    @Override
111                    public void binderDied() {
112                        Slog.w(TAG, "installd died; reconnecting");
113                        connect();
114                    }
115                }, 0);
116            } catch (RemoteException e) {
117                binder = null;
118            }
119        }
120
121        if (binder != null) {
122            mInstalld = IInstalld.Stub.asInterface(binder);
123            try {
124                invalidateMounts();
125            } catch (InstallerException ignored) {
126            }
127        } else {
128            Slog.w(TAG, "installd not found; trying again");
129            BackgroundThread.getHandler().postDelayed(() -> {
130                connect();
131            }, DateUtils.SECOND_IN_MILLIS);
132        }
133    }
134
135    /**
136     * Do several pre-flight checks before making a remote call.
137     *
138     * @return if the remote call should continue.
139     */
140    private boolean checkBeforeRemote() {
141        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
142            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
143                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
144        }
145        if (mIsolated) {
146            Slog.i(TAG, "Ignoring request because this installer is isolated");
147            return false;
148        } else {
149            return true;
150        }
151    }
152
153    public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
154            String seInfo, int targetSdkVersion) throws InstallerException {
155        if (!checkBeforeRemote()) return -1;
156        try {
157            return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
158                    targetSdkVersion);
159        } catch (Exception e) {
160            throw InstallerException.from(e);
161        }
162    }
163
164    public void restoreconAppData(String uuid, String packageName, int userId, int flags, int appId,
165            String seInfo) throws InstallerException {
166        if (!checkBeforeRemote()) return;
167        try {
168            mInstalld.restoreconAppData(uuid, packageName, userId, flags, appId, seInfo);
169        } catch (Exception e) {
170            throw InstallerException.from(e);
171        }
172    }
173
174    public void migrateAppData(String uuid, String packageName, int userId, int flags)
175            throws InstallerException {
176        if (!checkBeforeRemote()) return;
177        try {
178            mInstalld.migrateAppData(uuid, packageName, userId, flags);
179        } catch (Exception e) {
180            throw InstallerException.from(e);
181        }
182    }
183
184    public void clearAppData(String uuid, String packageName, int userId, int flags,
185            long ceDataInode) throws InstallerException {
186        if (!checkBeforeRemote()) return;
187        try {
188            mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode);
189        } catch (Exception e) {
190            throw InstallerException.from(e);
191        }
192    }
193
194    public void destroyAppData(String uuid, String packageName, int userId, int flags,
195            long ceDataInode) throws InstallerException {
196        if (!checkBeforeRemote()) return;
197        try {
198            mInstalld.destroyAppData(uuid, packageName, userId, flags, ceDataInode);
199        } catch (Exception e) {
200            throw InstallerException.from(e);
201        }
202    }
203
204    public void fixupAppData(String uuid, int flags) throws InstallerException {
205        if (!checkBeforeRemote()) return;
206        try {
207            mInstalld.fixupAppData(uuid, flags);
208        } catch (Exception e) {
209            throw InstallerException.from(e);
210        }
211    }
212
213    public void moveCompleteApp(String fromUuid, String toUuid, String packageName,
214            String dataAppName, int appId, String seInfo, int targetSdkVersion)
215            throws InstallerException {
216        if (!checkBeforeRemote()) return;
217        try {
218            mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, dataAppName, appId, seInfo,
219                    targetSdkVersion);
220        } catch (Exception e) {
221            throw InstallerException.from(e);
222        }
223    }
224
225    public void getAppSize(String uuid, String[] packageNames, int userId, int flags, int appId,
226            long[] ceDataInodes, String[] codePaths, PackageStats stats)
227            throws InstallerException {
228        if (!checkBeforeRemote()) return;
229        try {
230            final long[] res = mInstalld.getAppSize(uuid, packageNames, userId, flags,
231                    appId, ceDataInodes, codePaths);
232            stats.codeSize += res[0];
233            stats.dataSize += res[1];
234            stats.cacheSize += res[2];
235            stats.externalCodeSize += res[3];
236            stats.externalDataSize += res[4];
237            stats.externalCacheSize += res[5];
238        } catch (Exception e) {
239            throw InstallerException.from(e);
240        }
241    }
242
243    public void getUserSize(String uuid, int userId, int flags, int[] appIds, PackageStats stats)
244            throws InstallerException {
245        if (!checkBeforeRemote()) return;
246        try {
247            final long[] res = mInstalld.getUserSize(uuid, userId, flags, appIds);
248            stats.codeSize += res[0];
249            stats.dataSize += res[1];
250            stats.cacheSize += res[2];
251            stats.externalCodeSize += res[3];
252            stats.externalDataSize += res[4];
253            stats.externalCacheSize += res[5];
254        } catch (Exception e) {
255            throw InstallerException.from(e);
256        }
257    }
258
259    public long[] getExternalSize(String uuid, int userId, int flags, int[] appIds)
260            throws InstallerException {
261        if (!checkBeforeRemote()) return new long[6];
262        try {
263            return mInstalld.getExternalSize(uuid, userId, flags, appIds);
264        } catch (Exception e) {
265            throw InstallerException.from(e);
266        }
267    }
268
269    public void setAppQuota(String uuid, int userId, int appId, long cacheQuota)
270            throws InstallerException {
271        if (!checkBeforeRemote()) return;
272        try {
273            mInstalld.setAppQuota(uuid, userId, appId, cacheQuota);
274        } catch (Exception e) {
275            throw InstallerException.from(e);
276        }
277    }
278
279    public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
280            int dexoptNeeded, @Nullable String outputPath, int dexFlags,
281            String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
282            @Nullable String seInfo, boolean downgrade)
283            throws InstallerException {
284        assertValidInstructionSet(instructionSet);
285        if (!checkBeforeRemote()) return;
286        try {
287            mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
288                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
289        } catch (Exception e) {
290            throw InstallerException.from(e);
291        }
292    }
293
294    public boolean mergeProfiles(int uid, String packageName) throws InstallerException {
295        if (!checkBeforeRemote()) return false;
296        try {
297            return mInstalld.mergeProfiles(uid, packageName);
298        } catch (Exception e) {
299            throw InstallerException.from(e);
300        }
301    }
302
303    public boolean dumpProfiles(int uid, String packageName, String codePaths)
304            throws InstallerException {
305        if (!checkBeforeRemote()) return false;
306        try {
307            return mInstalld.dumpProfiles(uid, packageName, codePaths);
308        } catch (Exception e) {
309            throw InstallerException.from(e);
310        }
311    }
312
313    public boolean copySystemProfile(String systemProfile, int uid, String packageName)
314            throws InstallerException {
315        if (!checkBeforeRemote()) return false;
316        try {
317            return mInstalld.copySystemProfile(systemProfile, uid, packageName);
318        } catch (Exception e) {
319            throw InstallerException.from(e);
320        }
321    }
322
323    public void idmap(String targetApkPath, String overlayApkPath, int uid)
324            throws InstallerException {
325        if (!checkBeforeRemote()) return;
326        try {
327            mInstalld.idmap(targetApkPath, overlayApkPath, uid);
328        } catch (Exception e) {
329            throw InstallerException.from(e);
330        }
331    }
332
333    public void removeIdmap(String overlayApkPath) throws InstallerException {
334        if (!checkBeforeRemote()) return;
335        try {
336            mInstalld.removeIdmap(overlayApkPath);
337        } catch (Exception e) {
338            throw InstallerException.from(e);
339        }
340    }
341
342    public void rmdex(String codePath, String instructionSet) throws InstallerException {
343        assertValidInstructionSet(instructionSet);
344        if (!checkBeforeRemote()) return;
345        try {
346            mInstalld.rmdex(codePath, instructionSet);
347        } catch (Exception e) {
348            throw InstallerException.from(e);
349        }
350    }
351
352    public void rmPackageDir(String packageDir) throws InstallerException {
353        if (!checkBeforeRemote()) return;
354        try {
355            mInstalld.rmPackageDir(packageDir);
356        } catch (Exception e) {
357            throw InstallerException.from(e);
358        }
359    }
360
361    public void clearAppProfiles(String packageName) throws InstallerException {
362        if (!checkBeforeRemote()) return;
363        try {
364            mInstalld.clearAppProfiles(packageName);
365        } catch (Exception e) {
366            throw InstallerException.from(e);
367        }
368    }
369
370    public void destroyAppProfiles(String packageName) throws InstallerException {
371        if (!checkBeforeRemote()) return;
372        try {
373            mInstalld.destroyAppProfiles(packageName);
374        } catch (Exception e) {
375            throw InstallerException.from(e);
376        }
377    }
378
379    public void createUserData(String uuid, int userId, int userSerial, int flags)
380            throws InstallerException {
381        if (!checkBeforeRemote()) return;
382        try {
383            mInstalld.createUserData(uuid, userId, userSerial, flags);
384        } catch (Exception e) {
385            throw InstallerException.from(e);
386        }
387    }
388
389    public void destroyUserData(String uuid, int userId, int flags) throws InstallerException {
390        if (!checkBeforeRemote()) return;
391        try {
392            mInstalld.destroyUserData(uuid, userId, flags);
393        } catch (Exception e) {
394            throw InstallerException.from(e);
395        }
396    }
397
398    public void markBootComplete(String instructionSet) throws InstallerException {
399        assertValidInstructionSet(instructionSet);
400        if (!checkBeforeRemote()) return;
401        try {
402            mInstalld.markBootComplete(instructionSet);
403        } catch (Exception e) {
404            throw InstallerException.from(e);
405        }
406    }
407
408    public void freeCache(String uuid, long targetFreeBytes, long cacheReservedBytes, int flags)
409            throws InstallerException {
410        if (!checkBeforeRemote()) return;
411        try {
412            mInstalld.freeCache(uuid, targetFreeBytes, cacheReservedBytes, flags);
413        } catch (Exception e) {
414            throw InstallerException.from(e);
415        }
416    }
417
418    /**
419     * Links the 32 bit native library directory in an application's data
420     * directory to the real location for backward compatibility. Note that no
421     * such symlink is created for 64 bit shared libraries.
422     */
423    public void linkNativeLibraryDirectory(String uuid, String packageName, String nativeLibPath32,
424            int userId) throws InstallerException {
425        if (!checkBeforeRemote()) return;
426        try {
427            mInstalld.linkNativeLibraryDirectory(uuid, packageName, nativeLibPath32, userId);
428        } catch (Exception e) {
429            throw InstallerException.from(e);
430        }
431    }
432
433    public void createOatDir(String oatDir, String dexInstructionSet)
434            throws InstallerException {
435        if (!checkBeforeRemote()) return;
436        try {
437            mInstalld.createOatDir(oatDir, dexInstructionSet);
438        } catch (Exception e) {
439            throw InstallerException.from(e);
440        }
441    }
442
443    public void linkFile(String relativePath, String fromBase, String toBase)
444            throws InstallerException {
445        if (!checkBeforeRemote()) return;
446        try {
447            mInstalld.linkFile(relativePath, fromBase, toBase);
448        } catch (Exception e) {
449            throw InstallerException.from(e);
450        }
451    }
452
453    public void moveAb(String apkPath, String instructionSet, String outputPath)
454            throws InstallerException {
455        if (!checkBeforeRemote()) return;
456        try {
457            mInstalld.moveAb(apkPath, instructionSet, outputPath);
458        } catch (Exception e) {
459            throw InstallerException.from(e);
460        }
461    }
462
463    public void deleteOdex(String apkPath, String instructionSet, String outputPath)
464            throws InstallerException {
465        if (!checkBeforeRemote()) return;
466        try {
467            mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
468        } catch (Exception e) {
469            throw InstallerException.from(e);
470        }
471    }
472
473    public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid,
474            String[] isas, @Nullable String volumeUuid, int flags) throws InstallerException {
475        for (int i = 0; i < isas.length; i++) {
476            assertValidInstructionSet(isas[i]);
477        }
478        if (!checkBeforeRemote()) return false;
479        try {
480            return mInstalld.reconcileSecondaryDexFile(apkPath, packageName, uid, isas,
481                    volumeUuid, flags);
482        } catch (Exception e) {
483            throw InstallerException.from(e);
484        }
485    }
486
487    public void invalidateMounts() throws InstallerException {
488        if (!checkBeforeRemote()) return;
489        try {
490            mInstalld.invalidateMounts();
491        } catch (Exception e) {
492            throw InstallerException.from(e);
493        }
494    }
495
496    public boolean isQuotaSupported(String volumeUuid) throws InstallerException {
497        if (!checkBeforeRemote()) return false;
498        try {
499            return mInstalld.isQuotaSupported(volumeUuid);
500        } catch (Exception e) {
501            throw InstallerException.from(e);
502        }
503    }
504
505    private static void assertValidInstructionSet(String instructionSet)
506            throws InstallerException {
507        for (String abi : Build.SUPPORTED_ABIS) {
508            if (VMRuntime.getInstructionSet(abi).equals(instructionSet)) {
509                return;
510            }
511        }
512        throw new InstallerException("Invalid instruction set: " + instructionSet);
513    }
514
515    public static class InstallerException extends Exception {
516        public InstallerException(String detailMessage) {
517            super(detailMessage);
518        }
519
520        public static InstallerException from(Exception e) throws InstallerException {
521            throw new InstallerException(e.toString());
522        }
523    }
524}
525