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