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