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