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