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