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