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