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