PackageInstallerSession.java revision 1bea237aa296a35c263f0c4965ba0f5a140cd848
1/*
2 * Copyright (C) 2014 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 static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
20import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
21import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
22import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
23import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
24import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
25import static android.system.OsConstants.O_CREAT;
26import static android.system.OsConstants.O_RDONLY;
27import static android.system.OsConstants.O_WRONLY;
28
29import static com.android.internal.util.XmlUtils.readBitmapAttribute;
30import static com.android.internal.util.XmlUtils.readBooleanAttribute;
31import static com.android.internal.util.XmlUtils.readIntAttribute;
32import static com.android.internal.util.XmlUtils.readLongAttribute;
33import static com.android.internal.util.XmlUtils.readStringAttribute;
34import static com.android.internal.util.XmlUtils.readUriAttribute;
35import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
36import static com.android.internal.util.XmlUtils.writeIntAttribute;
37import static com.android.internal.util.XmlUtils.writeLongAttribute;
38import static com.android.internal.util.XmlUtils.writeStringAttribute;
39import static com.android.internal.util.XmlUtils.writeUriAttribute;
40import static com.android.server.pm.PackageInstallerService.prepareStageDir;
41
42import android.Manifest;
43import android.annotation.NonNull;
44import android.annotation.Nullable;
45import android.app.admin.DeviceAdminInfo;
46import android.app.admin.DevicePolicyManagerInternal;
47import android.content.Context;
48import android.content.Intent;
49import android.content.IntentSender;
50import android.content.pm.ApplicationInfo;
51import android.content.pm.IPackageInstallObserver2;
52import android.content.pm.IPackageInstallerSession;
53import android.content.pm.PackageInfo;
54import android.content.pm.PackageInstaller;
55import android.content.pm.PackageInstaller.SessionInfo;
56import android.content.pm.PackageInstaller.SessionParams;
57import android.content.pm.PackageManager;
58import android.content.pm.PackageParser;
59import android.content.pm.PackageParser.ApkLite;
60import android.content.pm.PackageParser.PackageLite;
61import android.content.pm.PackageParser.PackageParserException;
62import android.graphics.Bitmap;
63import android.graphics.BitmapFactory;
64import android.os.Binder;
65import android.os.Bundle;
66import android.os.FileBridge;
67import android.os.FileUtils;
68import android.os.Handler;
69import android.os.Looper;
70import android.os.Message;
71import android.os.ParcelFileDescriptor;
72import android.os.ParcelableException;
73import android.os.Process;
74import android.os.RemoteException;
75import android.os.RevocableFileDescriptor;
76import android.os.SystemProperties;
77import android.os.UserHandle;
78import android.os.storage.StorageManager;
79import android.system.ErrnoException;
80import android.system.Int64Ref;
81import android.system.Os;
82import android.system.OsConstants;
83import android.system.StructStat;
84import android.text.TextUtils;
85import android.util.ArraySet;
86import android.util.ExceptionUtils;
87import android.util.MathUtils;
88import android.util.Slog;
89import android.util.apk.ApkSignatureVerifier;
90
91import com.android.internal.annotations.GuardedBy;
92import com.android.internal.content.NativeLibraryHelper;
93import com.android.internal.content.PackageHelper;
94import com.android.internal.os.SomeArgs;
95import com.android.internal.util.ArrayUtils;
96import com.android.internal.util.IndentingPrintWriter;
97import com.android.internal.util.Preconditions;
98import com.android.server.LocalServices;
99import com.android.server.pm.Installer.InstallerException;
100import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
101
102import android.content.pm.dex.DexMetadataHelper;
103import libcore.io.IoUtils;
104
105import org.xmlpull.v1.XmlPullParser;
106import org.xmlpull.v1.XmlPullParserException;
107import org.xmlpull.v1.XmlSerializer;
108
109import java.io.File;
110import java.io.FileDescriptor;
111import java.io.FileFilter;
112import java.io.FileOutputStream;
113import java.io.IOException;
114import java.util.ArrayList;
115import java.util.Arrays;
116import java.util.LinkedList;
117import java.util.List;
118import java.util.concurrent.atomic.AtomicInteger;
119
120public class PackageInstallerSession extends IPackageInstallerSession.Stub {
121    private static final String TAG = "PackageInstaller";
122    private static final boolean LOGD = true;
123    private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
124
125    private static final int MSG_EARLY_BIND = 0;
126    private static final int MSG_COMMIT = 1;
127    private static final int MSG_ON_PACKAGE_INSTALLED = 2;
128
129    /** XML constants used for persisting a session */
130    static final String TAG_SESSION = "session";
131    private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
132    private static final String ATTR_SESSION_ID = "sessionId";
133    private static final String ATTR_USER_ID = "userId";
134    private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
135    private static final String ATTR_INSTALLER_UID = "installerUid";
136    private static final String ATTR_CREATED_MILLIS = "createdMillis";
137    private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
138    private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
139    private static final String ATTR_PREPARED = "prepared";
140    private static final String ATTR_SEALED = "sealed";
141    private static final String ATTR_MODE = "mode";
142    private static final String ATTR_INSTALL_FLAGS = "installFlags";
143    private static final String ATTR_INSTALL_LOCATION = "installLocation";
144    private static final String ATTR_SIZE_BYTES = "sizeBytes";
145    private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
146    @Deprecated
147    private static final String ATTR_APP_ICON = "appIcon";
148    private static final String ATTR_APP_LABEL = "appLabel";
149    private static final String ATTR_ORIGINATING_URI = "originatingUri";
150    private static final String ATTR_ORIGINATING_UID = "originatingUid";
151    private static final String ATTR_REFERRER_URI = "referrerUri";
152    private static final String ATTR_ABI_OVERRIDE = "abiOverride";
153    private static final String ATTR_VOLUME_UUID = "volumeUuid";
154    private static final String ATTR_NAME = "name";
155    private static final String ATTR_INSTALL_REASON = "installRason";
156
157    private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
158
159    // TODO: enforce INSTALL_ALLOW_TEST
160    // TODO: enforce INSTALL_ALLOW_DOWNGRADE
161
162    private final PackageInstallerService.InternalCallback mCallback;
163    private final Context mContext;
164    private final PackageManagerService mPm;
165    private final Handler mHandler;
166
167    final int sessionId;
168    final int userId;
169    final SessionParams params;
170    final long createdMillis;
171    final int defaultContainerGid;
172
173    /** Staging location where client data is written. */
174    final File stageDir;
175    final String stageCid;
176
177    private final AtomicInteger mActiveCount = new AtomicInteger();
178
179    private final Object mLock = new Object();
180
181    /** Uid of the creator of this session. */
182    private final int mOriginalInstallerUid;
183
184    /** Package of the owner of the installer session */
185    @GuardedBy("mLock")
186    private String mInstallerPackageName;
187
188    /** Uid of the owner of the installer session */
189    @GuardedBy("mLock")
190    private int mInstallerUid;
191
192    @GuardedBy("mLock")
193    private float mClientProgress = 0;
194    @GuardedBy("mLock")
195    private float mInternalProgress = 0;
196
197    @GuardedBy("mLock")
198    private float mProgress = 0;
199    @GuardedBy("mLock")
200    private float mReportedProgress = -1;
201
202    /** State of the session. */
203    @GuardedBy("mLock")
204    private boolean mPrepared = false;
205    @GuardedBy("mLock")
206    private boolean mSealed = false;
207    @GuardedBy("mLock")
208    private boolean mCommitted = false;
209    @GuardedBy("mLock")
210    private boolean mRelinquished = false;
211    @GuardedBy("mLock")
212    private boolean mDestroyed = false;
213
214    /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
215    @GuardedBy("mLock")
216    private boolean mPermissionsManuallyAccepted = false;
217
218    @GuardedBy("mLock")
219    private int mFinalStatus;
220    @GuardedBy("mLock")
221    private String mFinalMessage;
222
223    @GuardedBy("mLock")
224    private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
225    @GuardedBy("mLock")
226    private final ArrayList<FileBridge> mBridges = new ArrayList<>();
227
228    @GuardedBy("mLock")
229    private IPackageInstallObserver2 mRemoteObserver;
230
231    /** Fields derived from commit parsing */
232    @GuardedBy("mLock")
233    private String mPackageName;
234    @GuardedBy("mLock")
235    private long mVersionCode;
236    @GuardedBy("mLock")
237    private PackageParser.SigningDetails mSigningDetails;
238
239    /**
240     * Path to the validated base APK for this session, which may point at an
241     * APK inside the session (when the session defines the base), or it may
242     * point at the existing base APK (when adding splits to an existing app).
243     * <p>
244     * This is used when confirming permissions, since we can't fully stage the
245     * session inside an ASEC before confirming with user.
246     */
247    @GuardedBy("mLock")
248    private File mResolvedBaseFile;
249
250    @GuardedBy("mLock")
251    private File mResolvedStageDir;
252
253    @GuardedBy("mLock")
254    private final List<File> mResolvedStagedFiles = new ArrayList<>();
255    @GuardedBy("mLock")
256    private final List<File> mResolvedInheritedFiles = new ArrayList<>();
257    @GuardedBy("mLock")
258    private final List<String> mResolvedInstructionSets = new ArrayList<>();
259    @GuardedBy("mLock")
260    private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
261    @GuardedBy("mLock")
262    private File mInheritedFilesBase;
263
264    private static final FileFilter sAddedFilter = new FileFilter() {
265        @Override
266        public boolean accept(File file) {
267            // Installers can't stage directories, so it's fine to ignore
268            // entries like "lost+found".
269            if (file.isDirectory()) return false;
270            if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
271            if (DexMetadataHelper.isDexMetadataFile(file)) return false;
272            return true;
273        }
274    };
275    private static final FileFilter sRemovedFilter = new FileFilter() {
276        @Override
277        public boolean accept(File file) {
278            if (file.isDirectory()) return false;
279            if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
280            return true;
281        }
282    };
283
284    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
285        @Override
286        public boolean handleMessage(Message msg) {
287            switch (msg.what) {
288                case MSG_EARLY_BIND:
289                    earlyBindToDefContainer();
290                    break;
291                case MSG_COMMIT:
292                    synchronized (mLock) {
293                        try {
294                            commitLocked();
295                        } catch (PackageManagerException e) {
296                            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
297                            Slog.e(TAG,
298                                    "Commit of session " + sessionId + " failed: " + completeMsg);
299                            destroyInternal();
300                            dispatchSessionFinished(e.error, completeMsg, null);
301                        }
302                    }
303
304                    break;
305                case MSG_ON_PACKAGE_INSTALLED:
306                    final SomeArgs args = (SomeArgs) msg.obj;
307                    final String packageName = (String) args.arg1;
308                    final String message = (String) args.arg2;
309                    final Bundle extras = (Bundle) args.arg3;
310                    final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;
311                    final int returnCode = args.argi1;
312                    args.recycle();
313
314                    try {
315                        observer.onPackageInstalled(packageName, returnCode, message, extras);
316                    } catch (RemoteException ignored) {
317                    }
318
319                    break;
320            }
321
322            return true;
323        }
324    };
325
326    private void earlyBindToDefContainer() {
327        mPm.earlyBindToDefContainer();
328    }
329
330    /**
331     * @return {@code true} iff the installing is app an device owner or affiliated profile owner.
332     */
333    @GuardedBy("mLock")
334    private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
335        DevicePolicyManagerInternal dpmi =
336                LocalServices.getService(DevicePolicyManagerInternal.class);
337        return dpmi != null && dpmi.isActiveAdminWithPolicy(mInstallerUid,
338                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) && dpmi.isUserAffiliatedWithDevice(
339                userId);
340    }
341
342    /**
343     * Checks if the permissions still need to be confirmed.
344     *
345     * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
346     * installer might still {@link #transfer(String) change}.
347     *
348     * @return {@code true} iff we need to ask to confirm the permissions?
349     */
350    @GuardedBy("mLock")
351    private boolean needToAskForPermissionsLocked() {
352        if (mPermissionsManuallyAccepted) {
353            return false;
354        }
355
356        final boolean isInstallPermissionGranted =
357                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
358                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
359        final boolean isSelfUpdatePermissionGranted =
360                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
361                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
362        final boolean isUpdatePermissionGranted =
363                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
364                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
365        final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
366        final boolean isPermissionGranted = isInstallPermissionGranted
367                || (isUpdatePermissionGranted && targetPackageUid != -1)
368                || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
369        final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
370        final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
371        final boolean forcePermissionPrompt =
372                (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
373
374        // Device owners and affiliated profile owners  are allowed to silently install packages, so
375        // the permission check is waived if the installer is the device owner.
376        return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
377                || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
378    }
379
380    public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
381            Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
382            String installerPackageName, int installerUid, SessionParams params, long createdMillis,
383            File stageDir, String stageCid, boolean prepared, boolean sealed) {
384        mCallback = callback;
385        mContext = context;
386        mPm = pm;
387        mHandler = new Handler(looper, mHandlerCallback);
388
389        this.sessionId = sessionId;
390        this.userId = userId;
391        mOriginalInstallerUid = installerUid;
392        mInstallerPackageName = installerPackageName;
393        mInstallerUid = installerUid;
394        this.params = params;
395        this.createdMillis = createdMillis;
396        this.stageDir = stageDir;
397        this.stageCid = stageCid;
398
399        if ((stageDir == null) == (stageCid == null)) {
400            throw new IllegalArgumentException(
401                    "Exactly one of stageDir or stageCid stage must be set");
402        }
403
404        mPrepared = prepared;
405
406        if (sealed) {
407            synchronized (mLock) {
408                try {
409                    sealAndValidateLocked();
410                } catch (PackageManagerException | IOException e) {
411                    destroyInternal();
412                    throw new IllegalArgumentException(e);
413                }
414            }
415        }
416
417        final long identity = Binder.clearCallingIdentity();
418        try {
419            final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
420                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
421            defaultContainerGid = UserHandle.getSharedAppGid(uid);
422        } finally {
423            Binder.restoreCallingIdentity(identity);
424        }
425        // attempt to bind to the DefContainer as early as possible
426        if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
427            mHandler.sendMessage(mHandler.obtainMessage(MSG_EARLY_BIND));
428        }
429    }
430
431    public SessionInfo generateInfo() {
432        return generateInfo(true);
433    }
434
435    public SessionInfo generateInfo(boolean includeIcon) {
436        final SessionInfo info = new SessionInfo();
437        synchronized (mLock) {
438            info.sessionId = sessionId;
439            info.installerPackageName = mInstallerPackageName;
440            info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
441                    mResolvedBaseFile.getAbsolutePath() : null;
442            info.progress = mProgress;
443            info.sealed = mSealed;
444            info.active = mActiveCount.get() > 0;
445
446            info.mode = params.mode;
447            info.installReason = params.installReason;
448            info.sizeBytes = params.sizeBytes;
449            info.appPackageName = params.appPackageName;
450            if (includeIcon) {
451                info.appIcon = params.appIcon;
452            }
453            info.appLabel = params.appLabel;
454
455            info.installLocation = params.installLocation;
456            info.originatingUri = params.originatingUri;
457            info.originatingUid = params.originatingUid;
458            info.referrerUri = params.referrerUri;
459            info.grantedRuntimePermissions = params.grantedRuntimePermissions;
460            info.installFlags = params.installFlags;
461        }
462        return info;
463    }
464
465    public boolean isPrepared() {
466        synchronized (mLock) {
467            return mPrepared;
468        }
469    }
470
471    public boolean isSealed() {
472        synchronized (mLock) {
473            return mSealed;
474        }
475    }
476
477    @GuardedBy("mLock")
478    private void assertPreparedAndNotSealedLocked(String cookie) {
479        assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
480        if (mSealed) {
481            throw new SecurityException(cookie + " not allowed after sealing");
482        }
483    }
484
485    @GuardedBy("mLock")
486    private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
487        assertPreparedAndNotDestroyedLocked(cookie);
488        if (mCommitted) {
489            throw new SecurityException(cookie + " not allowed after commit");
490        }
491    }
492
493    @GuardedBy("mLock")
494    private void assertPreparedAndNotDestroyedLocked(String cookie) {
495        if (!mPrepared) {
496            throw new IllegalStateException(cookie + " before prepared");
497        }
498        if (mDestroyed) {
499            throw new SecurityException(cookie + " not allowed after destruction");
500        }
501    }
502
503    /**
504     * Resolve the actual location where staged data should be written. This
505     * might point at an ASEC mount point, which is why we delay path resolution
506     * until someone actively works with the session.
507     */
508    @GuardedBy("mLock")
509    private File resolveStageDirLocked() throws IOException {
510        if (mResolvedStageDir == null) {
511            if (stageDir != null) {
512                mResolvedStageDir = stageDir;
513            } else {
514                throw new IOException("Missing stageDir");
515            }
516        }
517        return mResolvedStageDir;
518    }
519
520    @Override
521    public void setClientProgress(float progress) {
522        synchronized (mLock) {
523            assertCallerIsOwnerOrRootLocked();
524
525            // Always publish first staging movement
526            final boolean forcePublish = (mClientProgress == 0);
527            mClientProgress = progress;
528            computeProgressLocked(forcePublish);
529        }
530    }
531
532    @Override
533    public void addClientProgress(float progress) {
534        synchronized (mLock) {
535            assertCallerIsOwnerOrRootLocked();
536
537            setClientProgress(mClientProgress + progress);
538        }
539    }
540
541    @GuardedBy("mLock")
542    private void computeProgressLocked(boolean forcePublish) {
543        mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
544                + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
545
546        // Only publish when meaningful change
547        if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
548            mReportedProgress = mProgress;
549            mCallback.onSessionProgressChanged(this, mProgress);
550        }
551    }
552
553    @Override
554    public String[] getNames() {
555        synchronized (mLock) {
556            assertCallerIsOwnerOrRootLocked();
557            assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
558
559            try {
560                return resolveStageDirLocked().list();
561            } catch (IOException e) {
562                throw ExceptionUtils.wrap(e);
563            }
564        }
565    }
566
567    @Override
568    public void removeSplit(String splitName) {
569        if (TextUtils.isEmpty(params.appPackageName)) {
570            throw new IllegalStateException("Must specify package name to remove a split");
571        }
572
573        synchronized (mLock) {
574            assertCallerIsOwnerOrRootLocked();
575            assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
576
577            try {
578                createRemoveSplitMarkerLocked(splitName);
579            } catch (IOException e) {
580                throw ExceptionUtils.wrap(e);
581            }
582        }
583    }
584
585    private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
586        try {
587            final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
588            if (!FileUtils.isValidExtFilename(markerName)) {
589                throw new IllegalArgumentException("Invalid marker: " + markerName);
590            }
591            final File target = new File(resolveStageDirLocked(), markerName);
592            target.createNewFile();
593            Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
594        } catch (ErrnoException e) {
595            throw e.rethrowAsIOException();
596        }
597    }
598
599    @Override
600    public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
601        try {
602            return doWriteInternal(name, offsetBytes, lengthBytes, null);
603        } catch (IOException e) {
604            throw ExceptionUtils.wrap(e);
605        }
606    }
607
608    @Override
609    public void write(String name, long offsetBytes, long lengthBytes,
610            ParcelFileDescriptor fd) {
611        try {
612            doWriteInternal(name, offsetBytes, lengthBytes, fd);
613        } catch (IOException e) {
614            throw ExceptionUtils.wrap(e);
615        }
616    }
617
618    private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
619            ParcelFileDescriptor incomingFd) throws IOException {
620        // Quick sanity check of state, and allocate a pipe for ourselves. We
621        // then do heavy disk allocation outside the lock, but this open pipe
622        // will block any attempted install transitions.
623        final RevocableFileDescriptor fd;
624        final FileBridge bridge;
625        final File stageDir;
626        synchronized (mLock) {
627            assertCallerIsOwnerOrRootLocked();
628            assertPreparedAndNotSealedLocked("openWrite");
629
630            if (PackageInstaller.ENABLE_REVOCABLE_FD) {
631                fd = new RevocableFileDescriptor();
632                bridge = null;
633                mFds.add(fd);
634            } else {
635                fd = null;
636                bridge = new FileBridge();
637                mBridges.add(bridge);
638            }
639
640            stageDir = resolveStageDirLocked();
641        }
642
643        try {
644            // Use installer provided name for now; we always rename later
645            if (!FileUtils.isValidExtFilename(name)) {
646                throw new IllegalArgumentException("Invalid name: " + name);
647            }
648            final File target;
649            final long identity = Binder.clearCallingIdentity();
650            try {
651                target = new File(stageDir, name);
652            } finally {
653                Binder.restoreCallingIdentity(identity);
654            }
655
656            // TODO: this should delegate to DCS so the system process avoids
657            // holding open FDs into containers.
658            final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),
659                    O_CREAT | O_WRONLY, 0644);
660            Os.chmod(target.getAbsolutePath(), 0644);
661
662            // If caller specified a total length, allocate it for them. Free up
663            // cache space to grow, if needed.
664            if (stageDir != null && lengthBytes > 0) {
665                mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
666                        PackageHelper.translateAllocateFlags(params.installFlags));
667            }
668
669            if (offsetBytes > 0) {
670                Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
671            }
672
673            if (incomingFd != null) {
674                switch (Binder.getCallingUid()) {
675                    case android.os.Process.SHELL_UID:
676                    case android.os.Process.ROOT_UID:
677                        break;
678                    default:
679                        throw new SecurityException("Reverse mode only supported from shell");
680                }
681
682                // In "reverse" mode, we're streaming data ourselves from the
683                // incoming FD, which means we never have to hand out our
684                // sensitive internal FD. We still rely on a "bridge" being
685                // inserted above to hold the session active.
686                try {
687                    final Int64Ref last = new Int64Ref(0);
688                    FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> {
689                        if (params.sizeBytes > 0) {
690                            final long delta = progress - last.value;
691                            last.value = progress;
692                            addClientProgress((float) delta / (float) params.sizeBytes);
693                        }
694                    }, null, lengthBytes);
695                } finally {
696                    IoUtils.closeQuietly(targetFd);
697                    IoUtils.closeQuietly(incomingFd);
698
699                    // We're done here, so remove the "bridge" that was holding
700                    // the session active.
701                    synchronized (mLock) {
702                        if (PackageInstaller.ENABLE_REVOCABLE_FD) {
703                            mFds.remove(fd);
704                        } else {
705                            mBridges.remove(bridge);
706                        }
707                    }
708                }
709                return null;
710            } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
711                fd.init(mContext, targetFd);
712                return fd.getRevocableFileDescriptor();
713            } else {
714                bridge.setTargetFile(targetFd);
715                bridge.start();
716                return new ParcelFileDescriptor(bridge.getClientSocket());
717            }
718
719        } catch (ErrnoException e) {
720            throw e.rethrowAsIOException();
721        }
722    }
723
724    @Override
725    public ParcelFileDescriptor openRead(String name) {
726        synchronized (mLock) {
727            assertCallerIsOwnerOrRootLocked();
728            assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
729            try {
730                return openReadInternalLocked(name);
731            } catch (IOException e) {
732                throw ExceptionUtils.wrap(e);
733            }
734        }
735    }
736
737    private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
738        try {
739            if (!FileUtils.isValidExtFilename(name)) {
740                throw new IllegalArgumentException("Invalid name: " + name);
741            }
742            final File target = new File(resolveStageDirLocked(), name);
743            final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);
744            return new ParcelFileDescriptor(targetFd);
745        } catch (ErrnoException e) {
746            throw e.rethrowAsIOException();
747        }
748    }
749
750    /**
751     * Check if the caller is the owner of this session. Otherwise throw a
752     * {@link SecurityException}.
753     */
754    @GuardedBy("mLock")
755    private void assertCallerIsOwnerOrRootLocked() {
756        final int callingUid = Binder.getCallingUid();
757        if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
758            throw new SecurityException("Session does not belong to uid " + callingUid);
759        }
760    }
761
762    /**
763     * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
764     */
765    @GuardedBy("mLock")
766    private void assertNoWriteFileTransfersOpenLocked() {
767        // Verify that all writers are hands-off
768        for (RevocableFileDescriptor fd : mFds) {
769            if (!fd.isRevoked()) {
770                throw new SecurityException("Files still open");
771            }
772        }
773        for (FileBridge bridge : mBridges) {
774            if (!bridge.isClosed()) {
775                throw new SecurityException("Files still open");
776            }
777        }
778    }
779
780    @Override
781    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
782        Preconditions.checkNotNull(statusReceiver);
783
784        final boolean wasSealed;
785        synchronized (mLock) {
786            assertCallerIsOwnerOrRootLocked();
787            assertPreparedAndNotDestroyedLocked("commit");
788
789            final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
790                    mContext, statusReceiver, sessionId,
791                    isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);
792            mRemoteObserver = adapter.getBinder();
793
794            if (forTransfer) {
795                mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
796
797                if (mInstallerUid == mOriginalInstallerUid) {
798                    throw new IllegalArgumentException("Session has not been transferred");
799                }
800            } else {
801                if (mInstallerUid != mOriginalInstallerUid) {
802                    throw new IllegalArgumentException("Session has been transferred");
803                }
804            }
805
806            wasSealed = mSealed;
807            if (!mSealed) {
808                try {
809                    sealAndValidateLocked();
810                } catch (IOException e) {
811                    throw new IllegalArgumentException(e);
812                } catch (PackageManagerException e) {
813                    // Do now throw an exception here to stay compatible with O and older
814                    destroyInternal();
815                    dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
816                    return;
817                }
818            }
819
820            // Client staging is fully done at this point
821            mClientProgress = 1f;
822            computeProgressLocked(true);
823
824            // This ongoing commit should keep session active, even though client
825            // will probably close their end.
826            mActiveCount.incrementAndGet();
827
828            mCommitted = true;
829            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
830        }
831
832        if (!wasSealed) {
833            // Persist the fact that we've sealed ourselves to prevent
834            // mutations of any hard links we create. We do this without holding
835            // the session lock, since otherwise it's a lock inversion.
836            mCallback.onSessionSealedBlocking(this);
837        }
838    }
839
840    /**
841     * Seal the session to prevent further modification and validate the contents of it.
842     *
843     * <p>The session will be sealed after calling this method even if it failed.
844     *
845     * @throws PackageManagerException if the session was sealed but something went wrong. If the
846     *                                 session was sealed this is the only possible exception.
847     */
848    @GuardedBy("mLock")
849    private void sealAndValidateLocked() throws PackageManagerException, IOException {
850        assertNoWriteFileTransfersOpenLocked();
851        assertPreparedAndNotDestroyedLocked("sealing of session");
852
853        final PackageInfo pkgInfo = mPm.getPackageInfo(
854                params.appPackageName, PackageManager.GET_SIGNATURES
855                        | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
856
857        resolveStageDirLocked();
858
859        mSealed = true;
860
861        // Verify that stage looks sane with respect to existing application.
862        // This currently only ensures packageName, versionCode, and certificate
863        // consistency.
864        try {
865            validateInstallLocked(pkgInfo);
866        } catch (PackageManagerException e) {
867            throw e;
868        } catch (Throwable e) {
869            // Convert all exceptions into package manager exceptions as only those are handled
870            // in the code above
871            throw new PackageManagerException(e);
872        }
873
874        // Read transfers from the original owner stay open, but as the session's data
875        // cannot be modified anymore, there is no leak of information.
876    }
877
878    @Override
879    public void transfer(String packageName) {
880        Preconditions.checkNotNull(packageName);
881
882        ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
883        if (newOwnerAppInfo == null) {
884            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
885        }
886
887        if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
888                Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
889            throw new SecurityException("Destination package " + packageName + " does not have "
890                    + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
891        }
892
893        // Only install flags that can be verified by the app the session is transferred to are
894        // allowed. The parameters can be read via PackageInstaller.SessionInfo.
895        if (!params.areHiddenOptionsSet()) {
896            throw new SecurityException("Can only transfer sessions that use public options");
897        }
898
899        synchronized (mLock) {
900            assertCallerIsOwnerOrRootLocked();
901            assertPreparedAndNotSealedLocked("transfer");
902
903            try {
904                sealAndValidateLocked();
905            } catch (IOException e) {
906                throw new IllegalStateException(e);
907            } catch (PackageManagerException e) {
908                // Session is sealed but could not be verified, we need to destroy it
909                destroyInternal();
910                dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
911
912                throw new IllegalArgumentException("Package is not valid", e);
913            }
914
915            if (!mPackageName.equals(mInstallerPackageName)) {
916                throw new SecurityException("Can only transfer sessions that update the original "
917                        + "installer");
918            }
919
920            mInstallerPackageName = packageName;
921            mInstallerUid = newOwnerAppInfo.uid;
922        }
923
924        // Persist the fact that we've sealed ourselves to prevent
925        // mutations of any hard links we create. We do this without holding
926        // the session lock, since otherwise it's a lock inversion.
927        mCallback.onSessionSealedBlocking(this);
928    }
929
930    @GuardedBy("mLock")
931    private void commitLocked()
932            throws PackageManagerException {
933        if (mDestroyed) {
934            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
935        }
936        if (!mSealed) {
937            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
938        }
939
940        Preconditions.checkNotNull(mPackageName);
941        Preconditions.checkNotNull(mSigningDetails);
942        Preconditions.checkNotNull(mResolvedBaseFile);
943
944        if (needToAskForPermissionsLocked()) {
945            // User needs to accept permissions; give installer an intent they
946            // can use to involve user.
947            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
948            intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
949            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
950            try {
951                mRemoteObserver.onUserActionRequired(intent);
952            } catch (RemoteException ignored) {
953            }
954
955            // Commit was keeping session marked as active until now; release
956            // that extra refcount so session appears idle.
957            closeInternal(false);
958            return;
959        }
960
961        // Inherit any packages and native libraries from existing install that
962        // haven't been overridden.
963        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
964            try {
965                final List<File> fromFiles = mResolvedInheritedFiles;
966                final File toDir = resolveStageDirLocked();
967
968                if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
969                if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
970                    throw new IllegalStateException("mInheritedFilesBase == null");
971                }
972
973                if (isLinkPossible(fromFiles, toDir)) {
974                    if (!mResolvedInstructionSets.isEmpty()) {
975                        final File oatDir = new File(toDir, "oat");
976                        createOatDirs(mResolvedInstructionSets, oatDir);
977                    }
978                    // pre-create lib dirs for linking if necessary
979                    if (!mResolvedNativeLibPaths.isEmpty()) {
980                        for (String libPath : mResolvedNativeLibPaths) {
981                            // "/lib/arm64" -> ["lib", "arm64"]
982                            final int splitIndex = libPath.lastIndexOf('/');
983                            if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
984                                Slog.e(TAG, "Skipping native library creation for linking due to "
985                                        + "invalid path: " + libPath);
986                                continue;
987                            }
988                            final String libDirPath = libPath.substring(1, splitIndex);
989                            final File libDir = new File(toDir, libDirPath);
990                            if (!libDir.exists()) {
991                                NativeLibraryHelper.createNativeLibrarySubdir(libDir);
992                            }
993                            final String archDirPath = libPath.substring(splitIndex + 1);
994                            NativeLibraryHelper.createNativeLibrarySubdir(
995                                    new File(libDir, archDirPath));
996                        }
997                    }
998                    linkFiles(fromFiles, toDir, mInheritedFilesBase);
999                } else {
1000                    // TODO: this should delegate to DCS so the system process
1001                    // avoids holding open FDs into containers.
1002                    copyFiles(fromFiles, toDir);
1003                }
1004            } catch (IOException e) {
1005                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
1006                        "Failed to inherit existing install", e);
1007            }
1008        }
1009
1010        // TODO: surface more granular state from dexopt
1011        mInternalProgress = 0.5f;
1012        computeProgressLocked(true);
1013
1014        // Unpack native libraries
1015        extractNativeLibraries(mResolvedStageDir, params.abiOverride, mayInheritNativeLibs());
1016
1017        // We've reached point of no return; call into PMS to install the stage.
1018        // Regardless of success or failure we always destroy session.
1019        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
1020            @Override
1021            public void onUserActionRequired(Intent intent) {
1022                throw new IllegalStateException();
1023            }
1024
1025            @Override
1026            public void onPackageInstalled(String basePackageName, int returnCode, String msg,
1027                    Bundle extras) {
1028                destroyInternal();
1029                dispatchSessionFinished(returnCode, msg, extras);
1030            }
1031        };
1032
1033        final UserHandle user;
1034        if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
1035            user = UserHandle.ALL;
1036        } else {
1037            user = new UserHandle(userId);
1038        }
1039
1040        mRelinquished = true;
1041        mPm.installStage(mPackageName, stageDir, localObserver, params,
1042                mInstallerPackageName, mInstallerUid, user, mSigningDetails);
1043    }
1044
1045    private static void maybeRenameFile(File from, File to) throws PackageManagerException {
1046        if (!from.equals(to)) {
1047            if (!from.renameTo(to)) {
1048                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1049                        "Could not rename file " + from + " to " + to);
1050            }
1051        }
1052    }
1053
1054    /**
1055     * Returns true if the session should attempt to inherit any existing native libraries already
1056     * extracted at the current install location. This is necessary to prevent double loading of
1057     * native libraries already loaded by the running app.
1058     */
1059    private boolean mayInheritNativeLibs() {
1060        return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) &&
1061                params.mode == SessionParams.MODE_INHERIT_EXISTING &&
1062                (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
1063    }
1064
1065    /**
1066     * Validate install by confirming that all application packages are have
1067     * consistent package name, version code, and signing certificates.
1068     * <p>
1069     * Clears and populates {@link #mResolvedBaseFile},
1070     * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
1071     * <p>
1072     * Renames package files in stage to match split names defined inside.
1073     * <p>
1074     * Note that upgrade compatibility is still performed by
1075     * {@link PackageManagerService}.
1076     */
1077    @GuardedBy("mLock")
1078    private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
1079            throws PackageManagerException {
1080        mPackageName = null;
1081        mVersionCode = -1;
1082        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
1083
1084        mResolvedBaseFile = null;
1085        mResolvedStagedFiles.clear();
1086        mResolvedInheritedFiles.clear();
1087
1088        try {
1089            resolveStageDirLocked();
1090        } catch (IOException e) {
1091            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1092                    "Failed to resolve stage location", e);
1093        }
1094
1095        final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
1096        final List<String> removeSplitList = new ArrayList<>();
1097        if (!ArrayUtils.isEmpty(removedFiles)) {
1098            for (File removedFile : removedFiles) {
1099                final String fileName = removedFile.getName();
1100                final String splitName = fileName.substring(
1101                        0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
1102                removeSplitList.add(splitName);
1103            }
1104        }
1105
1106        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
1107        if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
1108            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
1109        }
1110
1111        // Verify that all staged packages are internally consistent
1112        final ArraySet<String> stagedSplits = new ArraySet<>();
1113        for (File addedFile : addedFiles) {
1114            final ApkLite apk;
1115            try {
1116                apk = PackageParser.parseApkLite(
1117                        addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
1118            } catch (PackageParserException e) {
1119                throw PackageManagerException.from(e);
1120            }
1121
1122            if (!stagedSplits.add(apk.splitName)) {
1123                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1124                        "Split " + apk.splitName + " was defined multiple times");
1125            }
1126
1127            // Use first package to define unknown values
1128            if (mPackageName == null) {
1129                mPackageName = apk.packageName;
1130                mVersionCode = apk.getLongVersionCode();
1131            }
1132            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
1133                mSigningDetails = apk.signingDetails;
1134            }
1135
1136            assertApkConsistentLocked(String.valueOf(addedFile), apk);
1137
1138            // Take this opportunity to enforce uniform naming
1139            final String targetName;
1140            if (apk.splitName == null) {
1141                targetName = "base" + APK_FILE_EXTENSION;
1142            } else {
1143                targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
1144            }
1145            if (!FileUtils.isValidExtFilename(targetName)) {
1146                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1147                        "Invalid filename: " + targetName);
1148            }
1149
1150            final File targetFile = new File(mResolvedStageDir, targetName);
1151            maybeRenameFile(addedFile, targetFile);
1152
1153            // Base is coming from session
1154            if (apk.splitName == null) {
1155                mResolvedBaseFile = targetFile;
1156            }
1157
1158            mResolvedStagedFiles.add(targetFile);
1159
1160            final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
1161            if (dexMetadataFile != null) {
1162                if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
1163                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1164                            "Invalid filename: " + dexMetadataFile);
1165                }
1166                final File targetDexMetadataFile = new File(mResolvedStageDir,
1167                        DexMetadataHelper.buildDexMetadataPathForApk(targetName));
1168                mResolvedStagedFiles.add(targetDexMetadataFile);
1169                maybeRenameFile(dexMetadataFile, targetDexMetadataFile);
1170            }
1171        }
1172
1173        if (removeSplitList.size() > 0) {
1174            if (pkgInfo == null) {
1175                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1176                        "Missing existing base package for " + mPackageName);
1177            }
1178
1179            // validate split names marked for removal
1180            for (String splitName : removeSplitList) {
1181                if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
1182                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1183                            "Split not found: " + splitName);
1184                }
1185            }
1186
1187            // ensure we've got appropriate package name, version code and signatures
1188            if (mPackageName == null) {
1189                mPackageName = pkgInfo.packageName;
1190                mVersionCode = pkgInfo.getLongVersionCode();
1191            }
1192            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
1193                try {
1194                    mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
1195                            pkgInfo.applicationInfo.sourceDir,
1196                            PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
1197                } catch (PackageParserException e) {
1198                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1199                            "Couldn't obtain signatures from base APK");
1200                }
1201            }
1202        }
1203
1204        if (params.mode == SessionParams.MODE_FULL_INSTALL) {
1205            // Full installs must include a base package
1206            if (!stagedSplits.contains(null)) {
1207                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1208                        "Full install must include a base package");
1209            }
1210
1211        } else {
1212            // Partial installs must be consistent with existing install
1213            if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1214                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1215                        "Missing existing base package for " + mPackageName);
1216            }
1217
1218            final PackageLite existing;
1219            final ApkLite existingBase;
1220            ApplicationInfo appInfo = pkgInfo.applicationInfo;
1221            try {
1222                existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
1223                existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
1224                        PackageParser.PARSE_COLLECT_CERTIFICATES);
1225            } catch (PackageParserException e) {
1226                throw PackageManagerException.from(e);
1227            }
1228
1229            assertApkConsistentLocked("Existing base", existingBase);
1230
1231            // Inherit base if not overridden
1232            if (mResolvedBaseFile == null) {
1233                mResolvedBaseFile = new File(appInfo.getBaseCodePath());
1234                mResolvedInheritedFiles.add(mResolvedBaseFile);
1235                // Inherit the dex metadata if present.
1236                final File baseDexMetadataFile =
1237                        DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
1238                if (baseDexMetadataFile != null) {
1239                    mResolvedInheritedFiles.add(baseDexMetadataFile);
1240                }
1241            }
1242
1243            // Inherit splits if not overridden
1244            if (!ArrayUtils.isEmpty(existing.splitNames)) {
1245                for (int i = 0; i < existing.splitNames.length; i++) {
1246                    final String splitName = existing.splitNames[i];
1247                    final File splitFile = new File(existing.splitCodePaths[i]);
1248                    final boolean splitRemoved = removeSplitList.contains(splitName);
1249                    if (!stagedSplits.contains(splitName) && !splitRemoved) {
1250                        mResolvedInheritedFiles.add(splitFile);
1251                        // Inherit the dex metadata if present.
1252                        final File splitDexMetadataFile =
1253                                DexMetadataHelper.findDexMetadataForFile(splitFile);
1254                        if (splitDexMetadataFile != null) {
1255                            mResolvedInheritedFiles.add(splitDexMetadataFile);
1256                        }
1257                    }
1258                }
1259            }
1260
1261            // Inherit compiled oat directory.
1262            final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
1263            mInheritedFilesBase = packageInstallDir;
1264            final File oatDir = new File(packageInstallDir, "oat");
1265            if (oatDir.exists()) {
1266                final File[] archSubdirs = oatDir.listFiles();
1267
1268                // Keep track of all instruction sets we've seen compiled output for.
1269                // If we're linking (and not copying) inherited files, we can recreate the
1270                // instruction set hierarchy and link compiled output.
1271                if (archSubdirs != null && archSubdirs.length > 0) {
1272                    final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
1273                    for (File archSubDir : archSubdirs) {
1274                        // Skip any directory that isn't an ISA subdir.
1275                        if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
1276                            continue;
1277                        }
1278
1279                        mResolvedInstructionSets.add(archSubDir.getName());
1280                        List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
1281                        if (!oatFiles.isEmpty()) {
1282                            mResolvedInheritedFiles.addAll(oatFiles);
1283                        }
1284                    }
1285                }
1286            }
1287
1288            // Inherit native libraries for DONT_KILL sessions.
1289            if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
1290                File[] libDirs = new File[]{
1291                        new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME),
1292                        new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)};
1293                for (File libDir : libDirs) {
1294                    if (!libDir.exists() || !libDir.isDirectory()) {
1295                        continue;
1296                    }
1297                    final List<File> libDirsToInherit = new LinkedList<>();
1298                    for (File archSubDir : libDir.listFiles()) {
1299                        if (!archSubDir.isDirectory()) {
1300                            continue;
1301                        }
1302                        String relLibPath;
1303                        try {
1304                            relLibPath = getRelativePath(archSubDir, packageInstallDir);
1305                        } catch (IOException e) {
1306                            Slog.e(TAG, "Skipping linking of native library directory!", e);
1307                            // shouldn't be possible, but let's avoid inheriting these to be safe
1308                            libDirsToInherit.clear();
1309                            break;
1310                        }
1311                        if (!mResolvedNativeLibPaths.contains(relLibPath)) {
1312                            mResolvedNativeLibPaths.add(relLibPath);
1313                        }
1314                        libDirsToInherit.addAll(Arrays.asList(archSubDir.listFiles()));
1315                    }
1316                    mResolvedInheritedFiles.addAll(libDirsToInherit);
1317                }
1318            }
1319        }
1320    }
1321
1322    @GuardedBy("mLock")
1323    private void assertApkConsistentLocked(String tag, ApkLite apk)
1324            throws PackageManagerException {
1325        if (!mPackageName.equals(apk.packageName)) {
1326            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
1327                    + apk.packageName + " inconsistent with " + mPackageName);
1328        }
1329        if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
1330            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1331                    + " specified package " + params.appPackageName
1332                    + " inconsistent with " + apk.packageName);
1333        }
1334        if (mVersionCode != apk.getLongVersionCode()) {
1335            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1336                    + " version code " + apk.versionCode + " inconsistent with "
1337                    + mVersionCode);
1338        }
1339        if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
1340            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1341                    tag + " signatures are inconsistent");
1342        }
1343    }
1344
1345    /**
1346     * Determine if creating hard links between source and destination is
1347     * possible. That is, do they all live on the same underlying device.
1348     */
1349    private boolean isLinkPossible(List<File> fromFiles, File toDir) {
1350        try {
1351            final StructStat toStat = Os.stat(toDir.getAbsolutePath());
1352            for (File fromFile : fromFiles) {
1353                final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
1354                if (fromStat.st_dev != toStat.st_dev) {
1355                    return false;
1356                }
1357            }
1358        } catch (ErrnoException e) {
1359            Slog.w(TAG, "Failed to detect if linking possible: " + e);
1360            return false;
1361        }
1362        return true;
1363    }
1364
1365    /**
1366     * @return the uid of the owner this session
1367     */
1368    public int getInstallerUid() {
1369        synchronized (mLock) {
1370            return mInstallerUid;
1371        }
1372    }
1373
1374    private static String getRelativePath(File file, File base) throws IOException {
1375        final String pathStr = file.getAbsolutePath();
1376        final String baseStr = base.getAbsolutePath();
1377        // Don't allow relative paths.
1378        if (pathStr.contains("/.") ) {
1379            throw new IOException("Invalid path (was relative) : " + pathStr);
1380        }
1381
1382        if (pathStr.startsWith(baseStr)) {
1383            return pathStr.substring(baseStr.length());
1384        }
1385
1386        throw new IOException("File: " + pathStr + " outside base: " + baseStr);
1387    }
1388
1389    private void createOatDirs(List<String> instructionSets, File fromDir)
1390            throws PackageManagerException {
1391        for (String instructionSet : instructionSets) {
1392            try {
1393                mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
1394            } catch (InstallerException e) {
1395                throw PackageManagerException.from(e);
1396            }
1397        }
1398    }
1399
1400    private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
1401            throws IOException {
1402        for (File fromFile : fromFiles) {
1403            final String relativePath = getRelativePath(fromFile, fromDir);
1404            try {
1405                mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
1406                        toDir.getAbsolutePath());
1407            } catch (InstallerException e) {
1408                throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
1409                        + fromDir + ", " + toDir + ")", e);
1410            }
1411        }
1412
1413        Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
1414    }
1415
1416    private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
1417        // Remove any partial files from previous attempt
1418        for (File file : toDir.listFiles()) {
1419            if (file.getName().endsWith(".tmp")) {
1420                file.delete();
1421            }
1422        }
1423
1424        for (File fromFile : fromFiles) {
1425            final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
1426            if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
1427            if (!FileUtils.copyFile(fromFile, tmpFile)) {
1428                throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
1429            }
1430            try {
1431                Os.chmod(tmpFile.getAbsolutePath(), 0644);
1432            } catch (ErrnoException e) {
1433                throw new IOException("Failed to chmod " + tmpFile);
1434            }
1435            final File toFile = new File(toDir, fromFile.getName());
1436            if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
1437            if (!tmpFile.renameTo(toFile)) {
1438                throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
1439            }
1440        }
1441        Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
1442    }
1443
1444    private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
1445            throws PackageManagerException {
1446        final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
1447        if (!inherit) {
1448            // Start from a clean slate
1449            NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
1450        }
1451
1452        NativeLibraryHelper.Handle handle = null;
1453        try {
1454            handle = NativeLibraryHelper.Handle.create(packageDir);
1455            final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
1456                    abiOverride);
1457            if (res != PackageManager.INSTALL_SUCCEEDED) {
1458                throw new PackageManagerException(res,
1459                        "Failed to extract native libraries, res=" + res);
1460            }
1461        } catch (IOException e) {
1462            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1463                    "Failed to extract native libraries", e);
1464        } finally {
1465            IoUtils.closeQuietly(handle);
1466        }
1467    }
1468
1469    void setPermissionsResult(boolean accepted) {
1470        if (!mSealed) {
1471            throw new SecurityException("Must be sealed to accept permissions");
1472        }
1473
1474        if (accepted) {
1475            // Mark and kick off another install pass
1476            synchronized (mLock) {
1477                mPermissionsManuallyAccepted = true;
1478                mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
1479            }
1480        } else {
1481            destroyInternal();
1482            dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
1483        }
1484    }
1485
1486    public void open() throws IOException {
1487        if (mActiveCount.getAndIncrement() == 0) {
1488            mCallback.onSessionActiveChanged(this, true);
1489        }
1490
1491        boolean wasPrepared;
1492        synchronized (mLock) {
1493            wasPrepared = mPrepared;
1494            if (!mPrepared) {
1495                if (stageDir != null) {
1496                    prepareStageDir(stageDir);
1497                } else {
1498                    throw new IllegalArgumentException("stageDir must be set");
1499                }
1500
1501                mPrepared = true;
1502            }
1503        }
1504
1505        if (!wasPrepared) {
1506            mCallback.onSessionPrepared(this);
1507        }
1508    }
1509
1510    @Override
1511    public void close() {
1512        closeInternal(true);
1513    }
1514
1515    private void closeInternal(boolean checkCaller) {
1516        int activeCount;
1517        synchronized (mLock) {
1518            if (checkCaller) {
1519                assertCallerIsOwnerOrRootLocked();
1520            }
1521
1522            activeCount = mActiveCount.decrementAndGet();
1523        }
1524
1525        if (activeCount == 0) {
1526            mCallback.onSessionActiveChanged(this, false);
1527        }
1528    }
1529
1530    @Override
1531    public void abandon() {
1532        synchronized (mLock) {
1533            assertCallerIsOwnerOrRootLocked();
1534
1535            if (mRelinquished) {
1536                Slog.d(TAG, "Ignoring abandon after commit relinquished control");
1537                return;
1538            }
1539            destroyInternal();
1540        }
1541
1542        dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
1543    }
1544
1545    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
1546        final IPackageInstallObserver2 observer;
1547        final String packageName;
1548        synchronized (mLock) {
1549            mFinalStatus = returnCode;
1550            mFinalMessage = msg;
1551
1552            observer = mRemoteObserver;
1553            packageName = mPackageName;
1554        }
1555
1556        if (observer != null) {
1557            // Execute observer.onPackageInstalled on different tread as we don't want callers
1558            // inside the system server have to worry about catching the callbacks while they are
1559            // calling into the session
1560            final SomeArgs args = SomeArgs.obtain();
1561            args.arg1 = packageName;
1562            args.arg2 = msg;
1563            args.arg3 = extras;
1564            args.arg4 = observer;
1565            args.argi1 = returnCode;
1566
1567            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
1568        }
1569
1570        final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
1571
1572        // Send broadcast to default launcher only if it's a new install
1573        final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
1574        if (success && isNewInstall) {
1575            mPm.sendSessionCommitBroadcast(generateInfo(), userId);
1576        }
1577
1578        mCallback.onSessionFinished(this, success);
1579    }
1580
1581    private void destroyInternal() {
1582        synchronized (mLock) {
1583            mSealed = true;
1584            mDestroyed = true;
1585
1586            // Force shut down all bridges
1587            for (RevocableFileDescriptor fd : mFds) {
1588                fd.revoke();
1589            }
1590            for (FileBridge bridge : mBridges) {
1591                bridge.forceClose();
1592            }
1593        }
1594        if (stageDir != null) {
1595            try {
1596                mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
1597            } catch (InstallerException ignored) {
1598            }
1599        }
1600    }
1601
1602    void dump(IndentingPrintWriter pw) {
1603        synchronized (mLock) {
1604            dumpLocked(pw);
1605        }
1606    }
1607
1608    @GuardedBy("mLock")
1609    private void dumpLocked(IndentingPrintWriter pw) {
1610        pw.println("Session " + sessionId + ":");
1611        pw.increaseIndent();
1612
1613        pw.printPair("userId", userId);
1614        pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
1615        pw.printPair("mInstallerPackageName", mInstallerPackageName);
1616        pw.printPair("mInstallerUid", mInstallerUid);
1617        pw.printPair("createdMillis", createdMillis);
1618        pw.printPair("stageDir", stageDir);
1619        pw.printPair("stageCid", stageCid);
1620        pw.println();
1621
1622        params.dump(pw);
1623
1624        pw.printPair("mClientProgress", mClientProgress);
1625        pw.printPair("mProgress", mProgress);
1626        pw.printPair("mSealed", mSealed);
1627        pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
1628        pw.printPair("mRelinquished", mRelinquished);
1629        pw.printPair("mDestroyed", mDestroyed);
1630        pw.printPair("mFds", mFds.size());
1631        pw.printPair("mBridges", mBridges.size());
1632        pw.printPair("mFinalStatus", mFinalStatus);
1633        pw.printPair("mFinalMessage", mFinalMessage);
1634        pw.println();
1635
1636        pw.decreaseIndent();
1637    }
1638
1639    private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
1640            String[] grantedRuntimePermissions) throws IOException {
1641        if (grantedRuntimePermissions != null) {
1642            for (String permission : grantedRuntimePermissions) {
1643                out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1644                writeStringAttribute(out, ATTR_NAME, permission);
1645                out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1646            }
1647        }
1648    }
1649
1650    private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
1651        return new File(sessionsDir, "app_icon." + sessionId + ".png");
1652    }
1653
1654    /**
1655     * Write this session to a {@link XmlSerializer}.
1656     *
1657     * @param out Where to write the session to
1658     * @param sessionsDir The directory containing the sessions
1659     */
1660    void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
1661        synchronized (mLock) {
1662            if (mDestroyed) {
1663                return;
1664            }
1665
1666            out.startTag(null, TAG_SESSION);
1667
1668            writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
1669            writeIntAttribute(out, ATTR_USER_ID, userId);
1670            writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
1671                    mInstallerPackageName);
1672            writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
1673            writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
1674            if (stageDir != null) {
1675                writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
1676                        stageDir.getAbsolutePath());
1677            }
1678            if (stageCid != null) {
1679                writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
1680            }
1681            writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
1682            writeBooleanAttribute(out, ATTR_SEALED, isSealed());
1683
1684            writeIntAttribute(out, ATTR_MODE, params.mode);
1685            writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
1686            writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
1687            writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
1688            writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
1689            writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
1690            writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
1691            writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
1692            writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
1693            writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
1694            writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
1695            writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
1696
1697            writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
1698
1699            // Persist app icon if changed since last written
1700            File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1701            if (params.appIcon == null && appIconFile.exists()) {
1702                appIconFile.delete();
1703            } else if (params.appIcon != null
1704                    && appIconFile.lastModified() != params.appIconLastModified) {
1705                if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
1706                FileOutputStream os = null;
1707                try {
1708                    os = new FileOutputStream(appIconFile);
1709                    params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
1710                } catch (IOException e) {
1711                    Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
1712                } finally {
1713                    IoUtils.closeQuietly(os);
1714                }
1715
1716                params.appIconLastModified = appIconFile.lastModified();
1717            }
1718        }
1719
1720        out.endTag(null, TAG_SESSION);
1721    }
1722
1723    private static String[] readGrantedRuntimePermissions(XmlPullParser in)
1724            throws IOException, XmlPullParserException {
1725        List<String> permissions = null;
1726
1727        final int outerDepth = in.getDepth();
1728        int type;
1729        while ((type = in.next()) != XmlPullParser.END_DOCUMENT
1730                && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
1731            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1732                continue;
1733            }
1734            if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
1735                String permission = readStringAttribute(in, ATTR_NAME);
1736                if (permissions == null) {
1737                    permissions = new ArrayList<>();
1738                }
1739                permissions.add(permission);
1740            }
1741        }
1742
1743        if (permissions == null) {
1744            return null;
1745        }
1746
1747        String[] permissionsArray = new String[permissions.size()];
1748        permissions.toArray(permissionsArray);
1749        return permissionsArray;
1750    }
1751
1752    /**
1753     * Read new session from a {@link XmlPullParser xml description} and create it.
1754     *
1755     * @param in The source of the description
1756     * @param callback Callback the session uses to notify about changes of it's state
1757     * @param context Context to be used by the session
1758     * @param pm PackageManager to use by the session
1759     * @param installerThread Thread to be used for callbacks of this session
1760     * @param sessionsDir The directory the sessions are stored in
1761     *
1762     * @return The newly created session
1763     */
1764    public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
1765            @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
1766            @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)
1767            throws IOException, XmlPullParserException {
1768        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
1769        final int userId = readIntAttribute(in, ATTR_USER_ID);
1770        final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
1771        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
1772                installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
1773        final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1774        final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
1775        final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
1776        final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
1777        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
1778        final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
1779
1780        final SessionParams params = new SessionParams(
1781                SessionParams.MODE_INVALID);
1782        params.mode = readIntAttribute(in, ATTR_MODE);
1783        params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
1784        params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
1785        params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
1786        params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
1787        params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
1788        params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
1789        params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
1790        params.originatingUid =
1791                readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
1792        params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
1793        params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
1794        params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
1795        params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
1796
1797        params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
1798
1799        final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1800        if (appIconFile.exists()) {
1801            params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
1802            params.appIconLastModified = appIconFile.lastModified();
1803        }
1804
1805        return new PackageInstallerSession(callback, context, pm,
1806                installerThread, sessionId, userId, installerPackageName, installerUid,
1807                params, createdMillis, stageDir, stageCid, prepared, sealed);
1808    }
1809}
1810