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