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