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