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