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