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