• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_BAD_SIGNATURE;
21 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
22 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
23 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
24 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
25 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
26 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
27 import static android.system.OsConstants.O_CREAT;
28 import static android.system.OsConstants.O_RDONLY;
29 import static android.system.OsConstants.O_WRONLY;
30 
31 import static com.android.internal.util.XmlUtils.readBitmapAttribute;
32 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
33 import static com.android.internal.util.XmlUtils.readIntAttribute;
34 import static com.android.internal.util.XmlUtils.readLongAttribute;
35 import static com.android.internal.util.XmlUtils.readStringAttribute;
36 import static com.android.internal.util.XmlUtils.readUriAttribute;
37 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
38 import static com.android.internal.util.XmlUtils.writeIntAttribute;
39 import static com.android.internal.util.XmlUtils.writeLongAttribute;
40 import static com.android.internal.util.XmlUtils.writeStringAttribute;
41 import static com.android.internal.util.XmlUtils.writeUriAttribute;
42 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
43 
44 import android.Manifest;
45 import android.annotation.NonNull;
46 import android.annotation.Nullable;
47 import android.app.admin.DevicePolicyEventLogger;
48 import android.app.admin.DevicePolicyManagerInternal;
49 import android.content.Context;
50 import android.content.IIntentReceiver;
51 import android.content.IIntentSender;
52 import android.content.Intent;
53 import android.content.IntentSender;
54 import android.content.pm.ApplicationInfo;
55 import android.content.pm.IPackageInstallObserver2;
56 import android.content.pm.IPackageInstallerSession;
57 import android.content.pm.PackageInfo;
58 import android.content.pm.PackageInstaller;
59 import android.content.pm.PackageInstaller.SessionInfo;
60 import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
61 import android.content.pm.PackageInstaller.SessionParams;
62 import android.content.pm.PackageManager;
63 import android.content.pm.PackageParser;
64 import android.content.pm.PackageParser.ApkLite;
65 import android.content.pm.PackageParser.PackageLite;
66 import android.content.pm.PackageParser.PackageParserException;
67 import android.content.pm.dex.DexMetadataHelper;
68 import android.graphics.Bitmap;
69 import android.graphics.BitmapFactory;
70 import android.os.Binder;
71 import android.os.Bundle;
72 import android.os.FileBridge;
73 import android.os.FileUtils;
74 import android.os.Handler;
75 import android.os.IBinder;
76 import android.os.Looper;
77 import android.os.Message;
78 import android.os.ParcelFileDescriptor;
79 import android.os.ParcelableException;
80 import android.os.Process;
81 import android.os.RemoteException;
82 import android.os.RevocableFileDescriptor;
83 import android.os.SystemProperties;
84 import android.os.UserHandle;
85 import android.os.storage.StorageManager;
86 import android.stats.devicepolicy.DevicePolicyEnums;
87 import android.system.ErrnoException;
88 import android.system.Int64Ref;
89 import android.system.Os;
90 import android.system.OsConstants;
91 import android.system.StructStat;
92 import android.text.TextUtils;
93 import android.util.ArraySet;
94 import android.util.ExceptionUtils;
95 import android.util.MathUtils;
96 import android.util.Slog;
97 import android.util.SparseIntArray;
98 import android.util.apk.ApkSignatureVerifier;
99 
100 import com.android.internal.annotations.GuardedBy;
101 import com.android.internal.content.NativeLibraryHelper;
102 import com.android.internal.content.PackageHelper;
103 import com.android.internal.os.SomeArgs;
104 import com.android.internal.util.ArrayUtils;
105 import com.android.internal.util.IndentingPrintWriter;
106 import com.android.internal.util.Preconditions;
107 import com.android.server.LocalServices;
108 import com.android.server.pm.Installer.InstallerException;
109 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
110 import com.android.server.pm.dex.DexManager;
111 import com.android.server.security.VerityUtils;
112 
113 import libcore.io.IoUtils;
114 
115 import org.xmlpull.v1.XmlPullParser;
116 import org.xmlpull.v1.XmlPullParserException;
117 import org.xmlpull.v1.XmlSerializer;
118 
119 import java.io.File;
120 import java.io.FileDescriptor;
121 import java.io.FileFilter;
122 import java.io.FileOutputStream;
123 import java.io.IOException;
124 import java.util.ArrayList;
125 import java.util.Arrays;
126 import java.util.LinkedList;
127 import java.util.List;
128 import java.util.concurrent.atomic.AtomicInteger;
129 
130 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
131     private static final String TAG = "PackageInstallerSession";
132     private static final boolean LOGD = true;
133     private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
134 
135     private static final int MSG_COMMIT = 1;
136     private static final int MSG_ON_PACKAGE_INSTALLED = 2;
137 
138     /** XML constants used for persisting a session */
139     static final String TAG_SESSION = "session";
140     static final String TAG_CHILD_SESSION = "childSession";
141     private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
142     private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
143             "whitelisted-restricted-permission";
144     private static final String ATTR_SESSION_ID = "sessionId";
145     private static final String ATTR_USER_ID = "userId";
146     private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
147     private static final String ATTR_INSTALLER_UID = "installerUid";
148     private static final String ATTR_CREATED_MILLIS = "createdMillis";
149     private static final String ATTR_UPDATED_MILLIS = "updatedMillis";
150     private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
151     private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
152     private static final String ATTR_PREPARED = "prepared";
153     private static final String ATTR_COMMITTED = "committed";
154     private static final String ATTR_SEALED = "sealed";
155     private static final String ATTR_MULTI_PACKAGE = "multiPackage";
156     private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
157     private static final String ATTR_STAGED_SESSION = "stagedSession";
158     private static final String ATTR_IS_READY = "isReady";
159     private static final String ATTR_IS_FAILED = "isFailed";
160     private static final String ATTR_IS_APPLIED = "isApplied";
161     private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode";
162     private static final String ATTR_STAGED_SESSION_ERROR_MESSAGE = "errorMessage";
163     private static final String ATTR_MODE = "mode";
164     private static final String ATTR_INSTALL_FLAGS = "installFlags";
165     private static final String ATTR_INSTALL_LOCATION = "installLocation";
166     private static final String ATTR_SIZE_BYTES = "sizeBytes";
167     private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
168     @Deprecated
169     private static final String ATTR_APP_ICON = "appIcon";
170     private static final String ATTR_APP_LABEL = "appLabel";
171     private static final String ATTR_ORIGINATING_URI = "originatingUri";
172     private static final String ATTR_ORIGINATING_UID = "originatingUid";
173     private static final String ATTR_REFERRER_URI = "referrerUri";
174     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
175     private static final String ATTR_VOLUME_UUID = "volumeUuid";
176     private static final String ATTR_NAME = "name";
177     private static final String ATTR_INSTALL_REASON = "installRason";
178 
179     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
180     private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
181 
182     // TODO: enforce INSTALL_ALLOW_TEST
183     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
184 
185     private final PackageInstallerService.InternalCallback mCallback;
186     private final Context mContext;
187     private final PackageManagerService mPm;
188     private final Handler mHandler;
189     private final PackageSessionProvider mSessionProvider;
190     private final StagingManager mStagingManager;
191 
192     final int sessionId;
193     final int userId;
194     final SessionParams params;
195     final long createdMillis;
196 
197     /** Staging location where client data is written. */
198     final File stageDir;
199     final String stageCid;
200 
201     private final AtomicInteger mActiveCount = new AtomicInteger();
202 
203     private final Object mLock = new Object();
204 
205     /** Timestamp of the last time this session changed state  */
206     @GuardedBy("mLock")
207     private long updatedMillis;
208 
209     /** Uid of the creator of this session. */
210     private final int mOriginalInstallerUid;
211 
212     /** Package of the owner of the installer session */
213     @GuardedBy("mLock")
214     private @Nullable String mInstallerPackageName;
215 
216     /** Uid of the owner of the installer session */
217     @GuardedBy("mLock")
218     private int mInstallerUid;
219 
220     @GuardedBy("mLock")
221     private float mClientProgress = 0;
222     @GuardedBy("mLock")
223     private float mInternalProgress = 0;
224 
225     @GuardedBy("mLock")
226     private float mProgress = 0;
227     @GuardedBy("mLock")
228     private float mReportedProgress = -1;
229 
230     /** State of the session. */
231     @GuardedBy("mLock")
232     private boolean mPrepared = false;
233     @GuardedBy("mLock")
234     private boolean mSealed = false;
235     @GuardedBy("mLock")
236     private boolean mShouldBeSealed = false;
237     @GuardedBy("mLock")
238     private boolean mCommitted = false;
239     @GuardedBy("mLock")
240     private boolean mRelinquished = false;
241     @GuardedBy("mLock")
242     private boolean mDestroyed = false;
243 
244     /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
245     @GuardedBy("mLock")
246     private boolean mPermissionsManuallyAccepted = false;
247 
248     @GuardedBy("mLock")
249     private int mFinalStatus;
250     @GuardedBy("mLock")
251     private String mFinalMessage;
252 
253     @GuardedBy("mLock")
254     private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
255     @GuardedBy("mLock")
256     private final ArrayList<FileBridge> mBridges = new ArrayList<>();
257 
258     @GuardedBy("mLock")
259     private IPackageInstallObserver2 mRemoteObserver;
260 
261     /** Fields derived from commit parsing */
262     @GuardedBy("mLock")
263     private String mPackageName;
264     @GuardedBy("mLock")
265     private long mVersionCode;
266     @GuardedBy("mLock")
267     private PackageParser.SigningDetails mSigningDetails;
268     @GuardedBy("mLock")
269     private SparseIntArray mChildSessionIds = new SparseIntArray();
270     @GuardedBy("mLock")
271     private int mParentSessionId;
272 
273     @GuardedBy("mLock")
274     private boolean mStagedSessionApplied;
275     @GuardedBy("mLock")
276     private boolean mStagedSessionReady;
277     @GuardedBy("mLock")
278     private boolean mStagedSessionFailed;
279     @GuardedBy("mLock")
280     private int mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
281     @GuardedBy("mLock")
282     private String mStagedSessionErrorMessage;
283 
284     /**
285      * Path to the validated base APK for this session, which may point at an
286      * APK inside the session (when the session defines the base), or it may
287      * point at the existing base APK (when adding splits to an existing app).
288      * <p>
289      * This is used when confirming permissions, since we can't fully stage the
290      * session inside an ASEC before confirming with user.
291      */
292     @GuardedBy("mLock")
293     private File mResolvedBaseFile;
294 
295     @GuardedBy("mLock")
296     private File mResolvedStageDir;
297 
298     @GuardedBy("mLock")
299     private final List<File> mResolvedStagedFiles = new ArrayList<>();
300     @GuardedBy("mLock")
301     private final List<File> mResolvedInheritedFiles = new ArrayList<>();
302     @GuardedBy("mLock")
303     private final List<String> mResolvedInstructionSets = new ArrayList<>();
304     @GuardedBy("mLock")
305     private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
306     @GuardedBy("mLock")
307     private File mInheritedFilesBase;
308     @GuardedBy("mLock")
309     private boolean mVerityFound;
310 
311     private static final FileFilter sAddedFilter = new FileFilter() {
312         @Override
313         public boolean accept(File file) {
314             // Installers can't stage directories, so it's fine to ignore
315             // entries like "lost+found".
316             if (file.isDirectory()) return false;
317             if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
318             if (DexMetadataHelper.isDexMetadataFile(file)) return false;
319             if (VerityUtils.isFsveritySignatureFile(file)) return false;
320             return true;
321         }
322     };
323     private static final FileFilter sRemovedFilter = new FileFilter() {
324         @Override
325         public boolean accept(File file) {
326             if (file.isDirectory()) return false;
327             if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
328             return true;
329         }
330     };
331 
332     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
333         @Override
334         public boolean handleMessage(Message msg) {
335             switch (msg.what) {
336                 case MSG_COMMIT:
337                     handleCommit();
338                     break;
339                 case MSG_ON_PACKAGE_INSTALLED:
340                     final SomeArgs args = (SomeArgs) msg.obj;
341                     final String packageName = (String) args.arg1;
342                     final String message = (String) args.arg2;
343                     final Bundle extras = (Bundle) args.arg3;
344                     final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;
345                     final int returnCode = args.argi1;
346                     args.recycle();
347 
348                     try {
349                         observer.onPackageInstalled(packageName, returnCode, message, extras);
350                     } catch (RemoteException ignored) {
351                     }
352 
353                     break;
354             }
355 
356             return true;
357         }
358     };
359 
360     /**
361      * @return {@code true} iff the installing is app an device owner or affiliated profile owner.
362      */
363     @GuardedBy("mLock")
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()364     private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
365         if (userId != UserHandle.getUserId(mInstallerUid)) {
366             return false;
367         }
368         DevicePolicyManagerInternal dpmi =
369                 LocalServices.getService(DevicePolicyManagerInternal.class);
370         return dpmi != null && dpmi.canSilentlyInstallPackage(mInstallerPackageName, mInstallerUid);
371     }
372 
373     /**
374      * Checks if the permissions still need to be confirmed.
375      *
376      * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
377      * installer might still {@link #transfer(String) change}.
378      *
379      * @return {@code true} iff we need to ask to confirm the permissions?
380      */
381     @GuardedBy("mLock")
needToAskForPermissionsLocked()382     private boolean needToAskForPermissionsLocked() {
383         if (mPermissionsManuallyAccepted) {
384             return false;
385         }
386 
387         final boolean isInstallPermissionGranted =
388                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
389                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
390         final boolean isSelfUpdatePermissionGranted =
391                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
392                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
393         final boolean isUpdatePermissionGranted =
394                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
395                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
396         final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
397         final boolean isPermissionGranted = isInstallPermissionGranted
398                 || (isUpdatePermissionGranted && targetPackageUid != -1)
399                 || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
400         final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
401         final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
402         final boolean forcePermissionPrompt =
403                 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
404 
405         // Device owners and affiliated profile owners  are allowed to silently install packages, so
406         // the permission check is waived if the installer is the device owner.
407         return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
408                 || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
409     }
410 
PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager, int sessionId, int userId, String installerPackageName, int installerUid, SessionParams params, long createdMillis, File stageDir, String stageCid, boolean prepared, boolean committed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int stagedSessionErrorCode, String stagedSessionErrorMessage)411     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
412             Context context, PackageManagerService pm,
413             PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
414             int sessionId, int userId,
415             String installerPackageName, int installerUid, SessionParams params, long createdMillis,
416             File stageDir, String stageCid, boolean prepared, boolean committed, boolean sealed,
417             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
418             boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
419             String stagedSessionErrorMessage) {
420         mCallback = callback;
421         mContext = context;
422         mPm = pm;
423         mSessionProvider = sessionProvider;
424         mHandler = new Handler(looper, mHandlerCallback);
425         mStagingManager = stagingManager;
426 
427         this.sessionId = sessionId;
428         this.userId = userId;
429         mOriginalInstallerUid = installerUid;
430         mInstallerPackageName = installerPackageName;
431         mInstallerUid = installerUid;
432         this.params = params;
433         this.createdMillis = createdMillis;
434         this.updatedMillis = createdMillis;
435         this.stageDir = stageDir;
436         this.stageCid = stageCid;
437         this.mShouldBeSealed = sealed;
438         if (childSessionIds != null) {
439             for (int childSessionId : childSessionIds) {
440                 mChildSessionIds.put(childSessionId, 0);
441             }
442         }
443         this.mParentSessionId = parentSessionId;
444 
445         if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
446             throw new IllegalArgumentException(
447                     "Exactly one of stageDir or stageCid stage must be set");
448         }
449 
450         mPrepared = prepared;
451         mCommitted = committed;
452         mStagedSessionReady = isReady;
453         mStagedSessionFailed = isFailed;
454         mStagedSessionApplied = isApplied;
455         mStagedSessionErrorCode = stagedSessionErrorCode;
456         mStagedSessionErrorMessage =
457                 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
458     }
459 
460     /**
461      * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially
462      * sensitive data scrubbed from its fields.
463      *
464      * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
465      *                   need to be scrubbed
466      */
shouldScrubData(int callingUid)467     private boolean shouldScrubData(int callingUid) {
468         return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid);
469     }
470 
471     /**
472      * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields
473      * that may contain sensitive info being filtered.
474      *
475      * @param includeIcon true if the icon should be included in the object
476      * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
477      *                   need to be scrubbed
478      * @see #shouldScrubData(int)
479      */
generateInfoForCaller(boolean includeIcon, int callingUid)480     public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) {
481         return generateInfoInternal(includeIcon, shouldScrubData(callingUid));
482     }
483 
484     /**
485      * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields.
486      *
487      * @param includeIcon true if the icon should be included in the object
488      * @see #generateInfoForCaller(boolean, int)
489      */
generateInfoScrubbed(boolean includeIcon)490     public SessionInfo generateInfoScrubbed(boolean includeIcon) {
491         return generateInfoInternal(includeIcon, true /*scrubData*/);
492     }
493 
generateInfoInternal(boolean includeIcon, boolean scrubData)494     private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) {
495         final SessionInfo info = new SessionInfo();
496         synchronized (mLock) {
497             info.sessionId = sessionId;
498             info.userId = userId;
499             info.installerPackageName = mInstallerPackageName;
500             info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
501                     mResolvedBaseFile.getAbsolutePath() : null;
502             info.progress = mProgress;
503             info.sealed = mSealed;
504             info.isCommitted = mCommitted;
505             info.active = mActiveCount.get() > 0;
506 
507             info.mode = params.mode;
508             info.installReason = params.installReason;
509             info.sizeBytes = params.sizeBytes;
510             info.appPackageName = params.appPackageName;
511             if (includeIcon) {
512                 info.appIcon = params.appIcon;
513             }
514             info.appLabel = params.appLabel;
515 
516             info.installLocation = params.installLocation;
517             if (!scrubData) {
518                 info.originatingUri = params.originatingUri;
519             }
520             info.originatingUid = params.originatingUid;
521             if (!scrubData) {
522                 info.referrerUri = params.referrerUri;
523             }
524             info.grantedRuntimePermissions = params.grantedRuntimePermissions;
525             info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions;
526             info.installFlags = params.installFlags;
527             info.isMultiPackage = params.isMultiPackage;
528             info.isStaged = params.isStaged;
529             info.parentSessionId = mParentSessionId;
530             info.childSessionIds = mChildSessionIds.copyKeys();
531             if (info.childSessionIds == null) {
532                 info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
533             }
534             info.isStagedSessionApplied = mStagedSessionApplied;
535             info.isStagedSessionReady = mStagedSessionReady;
536             info.isStagedSessionFailed = mStagedSessionFailed;
537             info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage);
538             info.updatedMillis = updatedMillis;
539         }
540         return info;
541     }
542 
isPrepared()543     public boolean isPrepared() {
544         synchronized (mLock) {
545             return mPrepared;
546         }
547     }
548 
isSealed()549     public boolean isSealed() {
550         synchronized (mLock) {
551             return mSealed;
552         }
553     }
554 
555     /** {@hide} */
isCommitted()556     boolean isCommitted() {
557         synchronized (mLock) {
558             return mCommitted;
559         }
560     }
561 
562     /** Returns true if a staged session has reached a final state and can be forgotten about  */
isStagedAndInTerminalState()563     public boolean isStagedAndInTerminalState() {
564         synchronized (mLock) {
565             return params.isStaged && (mStagedSessionApplied || mStagedSessionFailed);
566         }
567     }
568 
569     @GuardedBy("mLock")
assertPreparedAndNotSealedLocked(String cookie)570     private void assertPreparedAndNotSealedLocked(String cookie) {
571         assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
572         if (mSealed) {
573             throw new SecurityException(cookie + " not allowed after sealing");
574         }
575     }
576 
577     @GuardedBy("mLock")
assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)578     private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
579         assertPreparedAndNotDestroyedLocked(cookie);
580         if (mCommitted) {
581             throw new SecurityException(cookie + " not allowed after commit");
582         }
583     }
584 
585     @GuardedBy("mLock")
assertPreparedAndNotDestroyedLocked(String cookie)586     private void assertPreparedAndNotDestroyedLocked(String cookie) {
587         if (!mPrepared) {
588             throw new IllegalStateException(cookie + " before prepared");
589         }
590         if (mDestroyed) {
591             throw new SecurityException(cookie + " not allowed after destruction");
592         }
593     }
594 
595     /**
596      * Resolve the actual location where staged data should be written. This
597      * might point at an ASEC mount point, which is why we delay path resolution
598      * until someone actively works with the session.
599      */
600     @GuardedBy("mLock")
resolveStageDirLocked()601     private File resolveStageDirLocked() throws IOException {
602         if (mResolvedStageDir == null) {
603             if (stageDir != null) {
604                 mResolvedStageDir = stageDir;
605             } else {
606                 throw new IOException("Missing stageDir");
607             }
608         }
609         return mResolvedStageDir;
610     }
611 
612     @Override
setClientProgress(float progress)613     public void setClientProgress(float progress) {
614         synchronized (mLock) {
615             assertCallerIsOwnerOrRootLocked();
616 
617             // Always publish first staging movement
618             final boolean forcePublish = (mClientProgress == 0);
619             mClientProgress = progress;
620             computeProgressLocked(forcePublish);
621         }
622     }
623 
624     @Override
addClientProgress(float progress)625     public void addClientProgress(float progress) {
626         synchronized (mLock) {
627             assertCallerIsOwnerOrRootLocked();
628 
629             setClientProgress(mClientProgress + progress);
630         }
631     }
632 
633     @GuardedBy("mLock")
computeProgressLocked(boolean forcePublish)634     private void computeProgressLocked(boolean forcePublish) {
635         mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
636                 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
637 
638         // Only publish when meaningful change
639         if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
640             mReportedProgress = mProgress;
641             mCallback.onSessionProgressChanged(this, mProgress);
642         }
643     }
644 
645     @Override
getNames()646     public String[] getNames() {
647         synchronized (mLock) {
648             assertCallerIsOwnerOrRootLocked();
649             assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
650 
651             try {
652                 return resolveStageDirLocked().list();
653             } catch (IOException e) {
654                 throw ExceptionUtils.wrap(e);
655             }
656         }
657     }
658 
659     @Override
removeSplit(String splitName)660     public void removeSplit(String splitName) {
661         if (TextUtils.isEmpty(params.appPackageName)) {
662             throw new IllegalStateException("Must specify package name to remove a split");
663         }
664 
665         synchronized (mLock) {
666             assertCallerIsOwnerOrRootLocked();
667             assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
668 
669             try {
670                 createRemoveSplitMarkerLocked(splitName);
671             } catch (IOException e) {
672                 throw ExceptionUtils.wrap(e);
673             }
674         }
675     }
676 
createRemoveSplitMarkerLocked(String splitName)677     private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
678         try {
679             final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
680             if (!FileUtils.isValidExtFilename(markerName)) {
681                 throw new IllegalArgumentException("Invalid marker: " + markerName);
682             }
683             final File target = new File(resolveStageDirLocked(), markerName);
684             target.createNewFile();
685             Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
686         } catch (ErrnoException e) {
687             throw e.rethrowAsIOException();
688         }
689     }
690 
691     @Override
openWrite(String name, long offsetBytes, long lengthBytes)692     public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
693         try {
694             return doWriteInternal(name, offsetBytes, lengthBytes, null);
695         } catch (IOException e) {
696             throw ExceptionUtils.wrap(e);
697         }
698     }
699 
700     @Override
write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)701     public void write(String name, long offsetBytes, long lengthBytes,
702             ParcelFileDescriptor fd) {
703         try {
704             doWriteInternal(name, offsetBytes, lengthBytes, fd);
705         } catch (IOException e) {
706             throw ExceptionUtils.wrap(e);
707         }
708     }
709 
doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)710     private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
711             ParcelFileDescriptor incomingFd) throws IOException {
712         // Quick sanity check of state, and allocate a pipe for ourselves. We
713         // then do heavy disk allocation outside the lock, but this open pipe
714         // will block any attempted install transitions.
715         final RevocableFileDescriptor fd;
716         final FileBridge bridge;
717         final File stageDir;
718         synchronized (mLock) {
719             assertCallerIsOwnerOrRootLocked();
720             assertPreparedAndNotSealedLocked("openWrite");
721 
722             if (PackageInstaller.ENABLE_REVOCABLE_FD) {
723                 fd = new RevocableFileDescriptor();
724                 bridge = null;
725                 mFds.add(fd);
726             } else {
727                 fd = null;
728                 bridge = new FileBridge();
729                 mBridges.add(bridge);
730             }
731 
732             stageDir = resolveStageDirLocked();
733         }
734 
735         try {
736             // Use installer provided name for now; we always rename later
737             if (!FileUtils.isValidExtFilename(name)) {
738                 throw new IllegalArgumentException("Invalid name: " + name);
739             }
740             final File target;
741             final long identity = Binder.clearCallingIdentity();
742             try {
743                 target = new File(stageDir, name);
744             } finally {
745                 Binder.restoreCallingIdentity(identity);
746             }
747 
748             // TODO: this should delegate to DCS so the system process avoids
749             // holding open FDs into containers.
750             final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),
751                     O_CREAT | O_WRONLY, 0644);
752             Os.chmod(target.getAbsolutePath(), 0644);
753 
754             // If caller specified a total length, allocate it for them. Free up
755             // cache space to grow, if needed.
756             if (stageDir != null && lengthBytes > 0) {
757                 mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
758                         PackageHelper.translateAllocateFlags(params.installFlags));
759             }
760 
761             if (offsetBytes > 0) {
762                 Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
763             }
764 
765             if (incomingFd != null) {
766                 switch (Binder.getCallingUid()) {
767                     case android.os.Process.SHELL_UID:
768                     case android.os.Process.ROOT_UID:
769                     case android.os.Process.SYSTEM_UID:
770                         break;
771                     default:
772                         throw new SecurityException(
773                                 "Reverse mode only supported from shell or system");
774                 }
775 
776                 // In "reverse" mode, we're streaming data ourselves from the
777                 // incoming FD, which means we never have to hand out our
778                 // sensitive internal FD. We still rely on a "bridge" being
779                 // inserted above to hold the session active.
780                 try {
781                     final Int64Ref last = new Int64Ref(0);
782                     FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null,
783                             Runnable::run, (long progress) -> {
784                                 if (params.sizeBytes > 0) {
785                                     final long delta = progress - last.value;
786                                     last.value = progress;
787                                     addClientProgress((float) delta / (float) params.sizeBytes);
788                                 }
789                             });
790                 } finally {
791                     IoUtils.closeQuietly(targetFd);
792                     IoUtils.closeQuietly(incomingFd);
793 
794                     // We're done here, so remove the "bridge" that was holding
795                     // the session active.
796                     synchronized (mLock) {
797                         if (PackageInstaller.ENABLE_REVOCABLE_FD) {
798                             mFds.remove(fd);
799                         } else {
800                             bridge.forceClose();
801                             mBridges.remove(bridge);
802                         }
803                     }
804                 }
805                 return null;
806             } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
807                 fd.init(mContext, targetFd);
808                 return fd.getRevocableFileDescriptor();
809             } else {
810                 bridge.setTargetFile(targetFd);
811                 bridge.start();
812                 return new ParcelFileDescriptor(bridge.getClientSocket());
813             }
814 
815         } catch (ErrnoException e) {
816             throw e.rethrowAsIOException();
817         }
818     }
819 
820     @Override
openRead(String name)821     public ParcelFileDescriptor openRead(String name) {
822         synchronized (mLock) {
823             assertCallerIsOwnerOrRootLocked();
824             assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
825             try {
826                 return openReadInternalLocked(name);
827             } catch (IOException e) {
828                 throw ExceptionUtils.wrap(e);
829             }
830         }
831     }
832 
openReadInternalLocked(String name)833     private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
834         try {
835             if (!FileUtils.isValidExtFilename(name)) {
836                 throw new IllegalArgumentException("Invalid name: " + name);
837             }
838             final File target = new File(resolveStageDirLocked(), name);
839             final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);
840             return new ParcelFileDescriptor(targetFd);
841         } catch (ErrnoException e) {
842             throw e.rethrowAsIOException();
843         }
844     }
845 
846     /**
847      * Check if the caller is the owner of this session. Otherwise throw a
848      * {@link SecurityException}.
849      */
850     @GuardedBy("mLock")
assertCallerIsOwnerOrRootLocked()851     private void assertCallerIsOwnerOrRootLocked() {
852         final int callingUid = Binder.getCallingUid();
853         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
854             throw new SecurityException("Session does not belong to uid " + callingUid);
855         }
856     }
857 
858     /**
859      * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
860      */
861     @GuardedBy("mLock")
assertNoWriteFileTransfersOpenLocked()862     private void assertNoWriteFileTransfersOpenLocked() {
863         // Verify that all writers are hands-off
864         for (RevocableFileDescriptor fd : mFds) {
865             if (!fd.isRevoked()) {
866                 throw new SecurityException("Files still open");
867             }
868         }
869         for (FileBridge bridge : mBridges) {
870             if (!bridge.isClosed()) {
871                 throw new SecurityException("Files still open");
872             }
873         }
874     }
875 
876     @Override
commit(@onNull IntentSender statusReceiver, boolean forTransfer)877     public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
878         if (hasParentSessionId()) {
879             throw new IllegalStateException(
880                     "Session " + sessionId + " is a child of multi-package session "
881                             + mParentSessionId +  " and may not be committed directly.");
882         }
883         if (!markAsCommitted(statusReceiver, forTransfer)) {
884             return;
885         }
886         if (isMultiPackage()) {
887             final SparseIntArray remainingSessions = mChildSessionIds.clone();
888             final IntentSender childIntentSender =
889                     new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
890                             .getIntentSender();
891             RuntimeException commitException = null;
892             boolean commitFailed = false;
893             for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
894                 final int childSessionId = mChildSessionIds.keyAt(i);
895                 try {
896                     // commit all children, regardless if any of them fail; we'll throw/return
897                     // as appropriate once all children have been processed
898                     if (!mSessionProvider.getSession(childSessionId)
899                             .markAsCommitted(childIntentSender, forTransfer)) {
900                         commitFailed = true;
901                     }
902                 } catch (RuntimeException e) {
903                     commitException = e;
904                 }
905             }
906             if (commitException != null) {
907                 throw commitException;
908             }
909             if (commitFailed) {
910                 return;
911             }
912         }
913         mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
914     }
915 
916     private class ChildStatusIntentReceiver {
917         private final SparseIntArray mChildSessionsRemaining;
918         private final IntentSender mStatusReceiver;
919         private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
920             @Override
921             public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
922                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
923                 statusUpdate(intent);
924             }
925         };
926 
ChildStatusIntentReceiver(SparseIntArray remainingSessions, IntentSender statusReceiver)927         private ChildStatusIntentReceiver(SparseIntArray remainingSessions,
928                 IntentSender statusReceiver) {
929             this.mChildSessionsRemaining = remainingSessions;
930             this.mStatusReceiver = statusReceiver;
931         }
932 
getIntentSender()933         public IntentSender getIntentSender() {
934             return new IntentSender((IIntentSender) mLocalSender);
935         }
936 
statusUpdate(Intent intent)937         public void statusUpdate(Intent intent) {
938             mHandler.post(() -> {
939                 if (mChildSessionsRemaining.size() == 0) {
940                     return;
941                 }
942                 final int sessionId = intent.getIntExtra(
943                         PackageInstaller.EXTRA_SESSION_ID, 0);
944                 final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
945                         PackageInstaller.STATUS_FAILURE);
946                 final int sessionIndex = mChildSessionsRemaining.indexOfKey(sessionId);
947                 if (PackageInstaller.STATUS_SUCCESS == status) {
948                     mChildSessionsRemaining.removeAt(sessionIndex);
949                     if (mChildSessionsRemaining.size() == 0) {
950                         try {
951                             intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
952                                     PackageInstallerSession.this.sessionId);
953                             mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
954                         } catch (IntentSender.SendIntentException ignore) {
955                         }
956                     }
957                 } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) {
958                     try {
959                         mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
960                     } catch (IntentSender.SendIntentException ignore) {
961                     }
962                 } else {
963                     intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
964                             PackageInstallerSession.this.sessionId);
965                     mChildSessionsRemaining.clear(); // we're done. Don't send any more.
966                     try {
967                         mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
968                     } catch (IntentSender.SendIntentException ignore) {
969                     }
970                 }
971             });
972         }
973     }
974 
975 
976     /**
977      * Do everything but actually commit the session. If this was not already called, the session
978      * will be sealed and marked as committed. The caller of this method is responsible for
979      * subsequently submitting this session for processing.
980      *
981      * This method may be called multiple times to update the status receiver validate caller
982      * permissions.
983      */
markAsCommitted( @onNull IntentSender statusReceiver, boolean forTransfer)984     public boolean markAsCommitted(
985             @NonNull IntentSender statusReceiver, boolean forTransfer) {
986         Preconditions.checkNotNull(statusReceiver);
987 
988         List<PackageInstallerSession> childSessions = getChildSessions();
989 
990         final boolean wasSealed;
991         synchronized (mLock) {
992             assertCallerIsOwnerOrRootLocked();
993             assertPreparedAndNotDestroyedLocked("commit");
994 
995             final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
996                     mContext, statusReceiver, sessionId,
997                     isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);
998             mRemoteObserver = adapter.getBinder();
999 
1000             if (forTransfer) {
1001                 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
1002 
1003                 if (mInstallerUid == mOriginalInstallerUid) {
1004                     throw new IllegalArgumentException("Session has not been transferred");
1005                 }
1006             } else {
1007                 if (mInstallerUid != mOriginalInstallerUid) {
1008                     throw new IllegalArgumentException("Session has been transferred");
1009                 }
1010             }
1011 
1012             // After validations and updating the observer, we can skip re-sealing, etc. because we
1013             // have already marked ourselves as committed.
1014             if (mCommitted) {
1015                 return true;
1016             }
1017 
1018             wasSealed = mSealed;
1019             if (!mSealed) {
1020                 try {
1021                     sealAndValidateLocked(childSessions);
1022                 } catch (IOException e) {
1023                     throw new IllegalArgumentException(e);
1024                 } catch (PackageManagerException e) {
1025                     // Do now throw an exception here to stay compatible with O and older
1026                     destroyInternal();
1027                     dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
1028                     return false;
1029                 }
1030             }
1031 
1032             // Client staging is fully done at this point
1033             mClientProgress = 1f;
1034             computeProgressLocked(true);
1035 
1036             // This ongoing commit should keep session active, even though client
1037             // will probably close their end.
1038             mActiveCount.incrementAndGet();
1039 
1040             mCommitted = true;
1041         }
1042 
1043         if (!wasSealed) {
1044             // Persist the fact that we've sealed ourselves to prevent
1045             // mutations of any hard links we create. We do this without holding
1046             // the session lock, since otherwise it's a lock inversion.
1047             mCallback.onSessionSealedBlocking(this);
1048         }
1049         return true;
1050     }
1051 
1052     /** Return a list of child sessions or null if the session is not multipackage
1053      *
1054      * <p> This method is handy to prevent potential deadlocks (b/123391593)
1055      */
getChildSessions()1056     private @Nullable List<PackageInstallerSession> getChildSessions() {
1057         List<PackageInstallerSession> childSessions = null;
1058         if (isMultiPackage()) {
1059             final int[] childSessionIds = getChildSessionIds();
1060             childSessions = new ArrayList<>(childSessionIds.length);
1061             for (int childSessionId : childSessionIds) {
1062                 childSessions.add(mSessionProvider.getSession(childSessionId));
1063             }
1064         }
1065         return childSessions;
1066     }
1067 
1068     /**
1069      * Assert multipackage install has consistent sessions.
1070      *
1071      * @throws PackageManagerException if child sessions don't match parent session
1072      *                                  in respect to staged and enable rollback parameters.
1073      */
1074     @GuardedBy("mLock")
assertMultiPackageConsistencyLocked( @onNull List<PackageInstallerSession> childSessions)1075     private void assertMultiPackageConsistencyLocked(
1076             @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException {
1077         for (PackageInstallerSession childSession : childSessions) {
1078             // It might be that the parent session is loaded before all of it's child sessions are,
1079             // e.g. when reading sessions from XML. Those sessions will be null here, and their
1080             // conformance with the multipackage params will be checked when they're loaded.
1081             if (childSession == null) {
1082                 continue;
1083             }
1084             assertConsistencyWithLocked(childSession);
1085         }
1086     }
1087 
1088     /**
1089      * Assert consistency with the given session.
1090      *
1091      * @throws PackageManagerException if other sessions doesn't match this session
1092      *                                  in respect to staged and enable rollback parameters.
1093      */
1094     @GuardedBy("mLock")
assertConsistencyWithLocked(PackageInstallerSession other)1095     private void assertConsistencyWithLocked(PackageInstallerSession other)
1096             throws PackageManagerException {
1097         // Session groups must be consistent wrt to isStaged parameter. Non-staging session
1098         // cannot be grouped with staging sessions.
1099         if (this.params.isStaged != other.params.isStaged) {
1100             throw new PackageManagerException(
1101                 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
1102                 "Multipackage Inconsistency: session " + other.sessionId
1103                     + " and session " + sessionId
1104                     + " have inconsistent staged settings");
1105         }
1106         if (this.params.getEnableRollback() != other.params.getEnableRollback()) {
1107             throw new PackageManagerException(
1108                 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
1109                 "Multipackage Inconsistency: session " + other.sessionId
1110                     + " and session " + sessionId
1111                     + " have inconsistent rollback settings");
1112         }
1113     }
1114 
1115     /**
1116      * Seal the session to prevent further modification and validate the contents of it.
1117      *
1118      * <p>The session will be sealed after calling this method even if it failed.
1119      *
1120      * @param childSessions the child sessions of a multipackage that will be checked for
1121      *                      consistency. Can be null if session is not multipackage.
1122      * @throws PackageManagerException if the session was sealed but something went wrong. If the
1123      *                                 session was sealed this is the only possible exception.
1124      */
1125     @GuardedBy("mLock")
sealAndValidateLocked(List<PackageInstallerSession> childSessions)1126     private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
1127             throws PackageManagerException, IOException {
1128         assertNoWriteFileTransfersOpenLocked();
1129         assertPreparedAndNotDestroyedLocked("sealing of session");
1130 
1131         mSealed = true;
1132 
1133         if (childSessions != null) {
1134             assertMultiPackageConsistencyLocked(childSessions);
1135         }
1136 
1137         if (params.isStaged) {
1138             final PackageInstallerSession activeSession = mStagingManager.getActiveSession();
1139             final boolean anotherSessionAlreadyInProgress =
1140                     activeSession != null && sessionId != activeSession.sessionId
1141                             && mParentSessionId != activeSession.sessionId;
1142             if (anotherSessionAlreadyInProgress) {
1143                 throw new PackageManagerException(
1144                         PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
1145                         "There is already in-progress committed staged session "
1146                                 + activeSession.sessionId, null);
1147             }
1148         }
1149 
1150         // Read transfers from the original owner stay open, but as the session's data
1151         // cannot be modified anymore, there is no leak of information. For staged sessions,
1152         // further validation is performed by the staging manager.
1153         if (!params.isMultiPackage) {
1154             final PackageInfo pkgInfo = mPm.getPackageInfo(
1155                     params.appPackageName, PackageManager.GET_SIGNATURES
1156                             | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
1157 
1158             resolveStageDirLocked();
1159 
1160             try {
1161                 if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
1162                     validateApexInstallLocked();
1163                 } else {
1164                     validateApkInstallLocked(pkgInfo);
1165                 }
1166             } catch (PackageManagerException e) {
1167                 throw e;
1168             } catch (Throwable e) {
1169                 // Convert all exceptions into package manager exceptions as only those are handled
1170                 // in the code above
1171                 throw new PackageManagerException(e);
1172             }
1173         }
1174     }
1175 
1176     /**
1177      * If session should be sealed, then it's sealed to prevent further modification
1178      * and then it's validated.
1179      *
1180      * If the session was sealed but something went wrong then it's destroyed.
1181      *
1182      * <p> This is meant to be called after all of the sessions are loaded and added to
1183      * PackageInstallerService
1184      */
sealAndValidateIfNecessary()1185     void sealAndValidateIfNecessary() {
1186         synchronized (mLock) {
1187             if (!mShouldBeSealed || isStagedAndInTerminalState()) {
1188                 return;
1189             }
1190         }
1191         List<PackageInstallerSession> childSessions = getChildSessions();
1192         synchronized (mLock) {
1193             try {
1194                 sealAndValidateLocked(childSessions);
1195             } catch (IOException e) {
1196                 throw new IllegalStateException(e);
1197             } catch (PackageManagerException e) {
1198                 Slog.e(TAG, "Package not valid", e);
1199                 // Session is sealed but could not be verified, we need to destroy it.
1200                 destroyInternal();
1201                 // Dispatch message to remove session from PackageInstallerService
1202                 dispatchSessionFinished(
1203                         e.error, ExceptionUtils.getCompleteMessage(e), null);
1204             }
1205         }
1206     }
1207 
1208     /** Update the timestamp of when the staged session last changed state */
markUpdated()1209     public void markUpdated() {
1210         synchronized (mLock) {
1211             this.updatedMillis = System.currentTimeMillis();
1212         }
1213     }
1214 
1215     @Override
transfer(String packageName)1216     public void transfer(String packageName) {
1217         Preconditions.checkNotNull(packageName);
1218 
1219         ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
1220         if (newOwnerAppInfo == null) {
1221             throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
1222         }
1223 
1224         if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
1225                 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
1226             throw new SecurityException("Destination package " + packageName + " does not have "
1227                     + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
1228         }
1229 
1230         // Only install flags that can be verified by the app the session is transferred to are
1231         // allowed. The parameters can be read via PackageInstaller.SessionInfo.
1232         if (!params.areHiddenOptionsSet()) {
1233             throw new SecurityException("Can only transfer sessions that use public options");
1234         }
1235 
1236         List<PackageInstallerSession> childSessions = getChildSessions();
1237 
1238         synchronized (mLock) {
1239             assertCallerIsOwnerOrRootLocked();
1240             assertPreparedAndNotSealedLocked("transfer");
1241 
1242             try {
1243                 sealAndValidateLocked(childSessions);
1244             } catch (IOException e) {
1245                 throw new IllegalStateException(e);
1246             } catch (PackageManagerException e) {
1247                 // Session is sealed but could not be verified, we need to destroy it
1248                 destroyInternal();
1249                 dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
1250 
1251                 throw new IllegalArgumentException("Package is not valid", e);
1252             }
1253 
1254             if (!mPackageName.equals(mInstallerPackageName)) {
1255                 throw new SecurityException("Can only transfer sessions that update the original "
1256                         + "installer");
1257             }
1258 
1259             mInstallerPackageName = packageName;
1260             mInstallerUid = newOwnerAppInfo.uid;
1261         }
1262 
1263         // Persist the fact that we've sealed ourselves to prevent
1264         // mutations of any hard links we create. We do this without holding
1265         // the session lock, since otherwise it's a lock inversion.
1266         mCallback.onSessionSealedBlocking(this);
1267     }
1268 
handleCommit()1269     private void handleCommit() {
1270         if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
1271             DevicePolicyEventLogger
1272                     .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
1273                     .setAdmin(mInstallerPackageName)
1274                     .write();
1275         }
1276         if (params.isStaged) {
1277             mStagingManager.commitSession(this);
1278             destroyInternal();
1279             dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
1280             return;
1281         }
1282 
1283         if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
1284             destroyInternal();
1285             dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
1286                     "APEX packages can only be installed using staged sessions.", null);
1287             return;
1288         }
1289 
1290         // For a multiPackage session, read the child sessions
1291         // outside of the lock, because reading the child
1292         // sessions with the lock held could lead to deadlock
1293         // (b/123391593).
1294         List<PackageInstallerSession> childSessions = getChildSessions();
1295 
1296         try {
1297             synchronized (mLock) {
1298                 commitNonStagedLocked(childSessions);
1299             }
1300         } catch (PackageManagerException e) {
1301             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
1302             Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
1303             destroyInternal();
1304             dispatchSessionFinished(e.error, completeMsg, null);
1305         }
1306     }
1307 
1308     @GuardedBy("mLock")
commitNonStagedLocked(List<PackageInstallerSession> childSessions)1309     private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
1310             throws PackageManagerException {
1311         final PackageManagerService.ActiveInstallSession committingSession =
1312                 makeSessionActiveLocked();
1313         if (committingSession == null) {
1314             return;
1315         }
1316         if (isMultiPackage()) {
1317             List<PackageManagerService.ActiveInstallSession> activeChildSessions =
1318                     new ArrayList<>(childSessions.size());
1319             boolean success = true;
1320             PackageManagerException failure = null;
1321             for (int i = 0; i < childSessions.size(); ++i) {
1322                 final PackageInstallerSession session = childSessions.get(i);
1323                 try {
1324                     final PackageManagerService.ActiveInstallSession activeSession =
1325                             session.makeSessionActiveLocked();
1326                     if (activeSession != null) {
1327                         activeChildSessions.add(activeSession);
1328                     }
1329                 } catch (PackageManagerException e) {
1330                     failure = e;
1331                     success = false;
1332                 }
1333             }
1334             if (!success) {
1335                 try {
1336                     mRemoteObserver.onPackageInstalled(
1337                             null, failure.error, failure.getLocalizedMessage(), null);
1338                 } catch (RemoteException ignored) {
1339                 }
1340                 return;
1341             }
1342             mPm.installStage(activeChildSessions);
1343         } else {
1344             mPm.installStage(committingSession);
1345         }
1346     }
1347 
1348     /**
1349      * Stages this session for install and returns a
1350      * {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
1351      * in case permissions need to be requested before install can proceed.
1352      */
1353     @GuardedBy("mLock")
makeSessionActiveLocked()1354     private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
1355             throws PackageManagerException {
1356         if (mRelinquished) {
1357             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1358                     "Session relinquished");
1359         }
1360         if (mDestroyed) {
1361             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
1362         }
1363         if (!mSealed) {
1364             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
1365         }
1366 
1367         final IPackageInstallObserver2 localObserver;
1368         if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
1369             localObserver = null;
1370         } else {
1371             if (!params.isMultiPackage) {
1372                 Preconditions.checkNotNull(mPackageName);
1373                 Preconditions.checkNotNull(mSigningDetails);
1374                 Preconditions.checkNotNull(mResolvedBaseFile);
1375 
1376                 if (needToAskForPermissionsLocked()) {
1377                     // User needs to confirm installation;
1378                     // give installer an intent they can use to involve
1379                     // user.
1380                     final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
1381                     intent.setPackage(mPm.getPackageInstallerPackageName());
1382                     intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
1383                     try {
1384                         mRemoteObserver.onUserActionRequired(intent);
1385                     } catch (RemoteException ignored) {
1386                     }
1387 
1388                     // Commit was keeping session marked as active until now; release
1389                     // that extra refcount so session appears idle.
1390                     closeInternal(false);
1391                     return null;
1392                 }
1393 
1394                 // Inherit any packages and native libraries from existing install that
1395                 // haven't been overridden.
1396                 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
1397                     try {
1398                         final List<File> fromFiles = mResolvedInheritedFiles;
1399                         final File toDir = resolveStageDirLocked();
1400 
1401                         if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
1402                         if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
1403                             throw new IllegalStateException("mInheritedFilesBase == null");
1404                         }
1405 
1406                         if (isLinkPossible(fromFiles, toDir)) {
1407                             if (!mResolvedInstructionSets.isEmpty()) {
1408                                 final File oatDir = new File(toDir, "oat");
1409                                 createOatDirs(mResolvedInstructionSets, oatDir);
1410                             }
1411                             // pre-create lib dirs for linking if necessary
1412                             if (!mResolvedNativeLibPaths.isEmpty()) {
1413                                 for (String libPath : mResolvedNativeLibPaths) {
1414                                     // "/lib/arm64" -> ["lib", "arm64"]
1415                                     final int splitIndex = libPath.lastIndexOf('/');
1416                                     if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
1417                                         Slog.e(TAG,
1418                                                 "Skipping native library creation for linking due"
1419                                                         + " to invalid path: " + libPath);
1420                                         continue;
1421                                     }
1422                                     final String libDirPath = libPath.substring(1, splitIndex);
1423                                     final File libDir = new File(toDir, libDirPath);
1424                                     if (!libDir.exists()) {
1425                                         NativeLibraryHelper.createNativeLibrarySubdir(libDir);
1426                                     }
1427                                     final String archDirPath = libPath.substring(splitIndex + 1);
1428                                     NativeLibraryHelper.createNativeLibrarySubdir(
1429                                             new File(libDir, archDirPath));
1430                                 }
1431                             }
1432                             linkFiles(fromFiles, toDir, mInheritedFilesBase);
1433                         } else {
1434                             // TODO: this should delegate to DCS so the system process
1435                             // avoids holding open FDs into containers.
1436                             copyFiles(fromFiles, toDir);
1437                         }
1438                     } catch (IOException e) {
1439                         throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
1440                                 "Failed to inherit existing install", e);
1441                     }
1442                 }
1443 
1444                 // TODO: surface more granular state from dexopt
1445                 mInternalProgress = 0.5f;
1446                 computeProgressLocked(true);
1447 
1448                 // Unpack native libraries
1449                 extractNativeLibraries(mResolvedStageDir, params.abiOverride,
1450                         mayInheritNativeLibs());
1451             }
1452 
1453             // We've reached point of no return; call into PMS to install the stage.
1454             // Regardless of success or failure we always destroy session.
1455             localObserver = new IPackageInstallObserver2.Stub() {
1456                 @Override
1457                 public void onUserActionRequired(Intent intent) {
1458                     throw new IllegalStateException();
1459                 }
1460 
1461                 @Override
1462                 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
1463                         Bundle extras) {
1464                     destroyInternal();
1465                     dispatchSessionFinished(returnCode, msg, extras);
1466                 }
1467             };
1468         }
1469 
1470         final UserHandle user;
1471         if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
1472             user = UserHandle.ALL;
1473         } else {
1474             user = new UserHandle(userId);
1475         }
1476 
1477         mRelinquished = true;
1478         return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
1479                 localObserver, params, mInstallerPackageName, mInstallerUid, user,
1480                 mSigningDetails);
1481     }
1482 
maybeRenameFile(File from, File to)1483     private static void maybeRenameFile(File from, File to) throws PackageManagerException {
1484         if (!from.equals(to)) {
1485             if (!from.renameTo(to)) {
1486                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1487                         "Could not rename file " + from + " to " + to);
1488             }
1489         }
1490     }
1491 
1492     /**
1493      * Returns true if the session should attempt to inherit any existing native libraries already
1494      * extracted at the current install location. This is necessary to prevent double loading of
1495      * native libraries already loaded by the running app.
1496      */
mayInheritNativeLibs()1497     private boolean mayInheritNativeLibs() {
1498         return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) &&
1499                 params.mode == SessionParams.MODE_INHERIT_EXISTING &&
1500                 (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
1501     }
1502 
1503     /**
1504      * Validate apex install.
1505      * <p>
1506      * Sets {@link #mResolvedBaseFile} for RollbackManager to use.
1507      */
1508     @GuardedBy("mLock")
validateApexInstallLocked()1509     private void validateApexInstallLocked()
1510             throws PackageManagerException {
1511         final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
1512         if (ArrayUtils.isEmpty(addedFiles)) {
1513             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
1514         }
1515 
1516         if (ArrayUtils.size(addedFiles) > 1) {
1517             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1518                     "Too many files for apex install");
1519         }
1520 
1521         mResolvedBaseFile = addedFiles[0];
1522     }
1523 
1524     /**
1525      * Validate install by confirming that all application packages are have
1526      * consistent package name, version code, and signing certificates.
1527      * <p>
1528      * Clears and populates {@link #mResolvedBaseFile},
1529      * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
1530      * <p>
1531      * Renames package files in stage to match split names defined inside.
1532      * <p>
1533      * Note that upgrade compatibility is still performed by
1534      * {@link PackageManagerService}.
1535      */
1536     @GuardedBy("mLock")
validateApkInstallLocked(@ullable PackageInfo pkgInfo)1537     private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
1538             throws PackageManagerException {
1539         ApkLite baseApk = null;
1540         mPackageName = null;
1541         mVersionCode = -1;
1542         mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
1543 
1544         mResolvedBaseFile = null;
1545         mResolvedStagedFiles.clear();
1546         mResolvedInheritedFiles.clear();
1547 
1548         // Partial installs must be consistent with existing install
1549         if (params.mode == SessionParams.MODE_INHERIT_EXISTING
1550                 && (pkgInfo == null || pkgInfo.applicationInfo == null)) {
1551             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1552                     "Missing existing base package");
1553         }
1554         // Default to require only if existing base has fs-verity.
1555         mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
1556                 && params.mode == SessionParams.MODE_INHERIT_EXISTING
1557                 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
1558 
1559         try {
1560             resolveStageDirLocked();
1561         } catch (IOException e) {
1562             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1563                     "Failed to resolve stage location", e);
1564         }
1565 
1566         final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
1567         final List<String> removeSplitList = new ArrayList<>();
1568         if (!ArrayUtils.isEmpty(removedFiles)) {
1569             for (File removedFile : removedFiles) {
1570                 final String fileName = removedFile.getName();
1571                 final String splitName = fileName.substring(
1572                         0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
1573                 removeSplitList.add(splitName);
1574             }
1575         }
1576 
1577         final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
1578         if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
1579             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
1580         }
1581 
1582         // Verify that all staged packages are internally consistent
1583         final ArraySet<String> stagedSplits = new ArraySet<>();
1584         for (File addedFile : addedFiles) {
1585             final ApkLite apk;
1586             try {
1587                 apk = PackageParser.parseApkLite(
1588                         addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
1589             } catch (PackageParserException e) {
1590                 throw PackageManagerException.from(e);
1591             }
1592 
1593             if (!stagedSplits.add(apk.splitName)) {
1594                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1595                         "Split " + apk.splitName + " was defined multiple times");
1596             }
1597 
1598             // Use first package to define unknown values
1599             if (mPackageName == null) {
1600                 mPackageName = apk.packageName;
1601                 mVersionCode = apk.getLongVersionCode();
1602             }
1603             if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
1604                 mSigningDetails = apk.signingDetails;
1605             }
1606 
1607             assertApkConsistentLocked(String.valueOf(addedFile), apk);
1608 
1609             // Take this opportunity to enforce uniform naming
1610             final String targetName;
1611             if (apk.splitName == null) {
1612                 targetName = "base" + APK_FILE_EXTENSION;
1613             } else {
1614                 targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
1615             }
1616             if (!FileUtils.isValidExtFilename(targetName)) {
1617                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1618                         "Invalid filename: " + targetName);
1619             }
1620 
1621             final File targetFile = new File(mResolvedStageDir, targetName);
1622             resolveAndStageFile(addedFile, targetFile);
1623 
1624             // Base is coming from session
1625             if (apk.splitName == null) {
1626                 mResolvedBaseFile = targetFile;
1627                 baseApk = apk;
1628             }
1629 
1630             final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
1631             if (dexMetadataFile != null) {
1632                 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
1633                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1634                             "Invalid filename: " + dexMetadataFile);
1635                 }
1636                 final File targetDexMetadataFile = new File(mResolvedStageDir,
1637                         DexMetadataHelper.buildDexMetadataPathForApk(targetName));
1638                 resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
1639             }
1640         }
1641 
1642         if (removeSplitList.size() > 0) {
1643             if (pkgInfo == null) {
1644                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1645                         "Missing existing base package for " + mPackageName);
1646             }
1647 
1648             // validate split names marked for removal
1649             for (String splitName : removeSplitList) {
1650                 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
1651                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1652                             "Split not found: " + splitName);
1653                 }
1654             }
1655 
1656             // ensure we've got appropriate package name, version code and signatures
1657             if (mPackageName == null) {
1658                 mPackageName = pkgInfo.packageName;
1659                 mVersionCode = pkgInfo.getLongVersionCode();
1660             }
1661             if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
1662                 try {
1663                     mSigningDetails = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
1664                             pkgInfo.applicationInfo.sourceDir,
1665                             PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
1666                 } catch (PackageParserException e) {
1667                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1668                             "Couldn't obtain signatures from base APK");
1669                 }
1670             }
1671         }
1672 
1673         if (params.mode == SessionParams.MODE_FULL_INSTALL) {
1674             // Full installs must include a base package
1675             if (!stagedSplits.contains(null)) {
1676                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1677                         "Full install must include a base package");
1678             }
1679 
1680         } else {
1681             final PackageLite existing;
1682             final ApkLite existingBase;
1683             ApplicationInfo appInfo = pkgInfo.applicationInfo;
1684             try {
1685                 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
1686                 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
1687                         PackageParser.PARSE_COLLECT_CERTIFICATES);
1688             } catch (PackageParserException e) {
1689                 throw PackageManagerException.from(e);
1690             }
1691 
1692             assertApkConsistentLocked("Existing base", existingBase);
1693 
1694             // Inherit base if not overridden
1695             if (mResolvedBaseFile == null) {
1696                 mResolvedBaseFile = new File(appInfo.getBaseCodePath());
1697                 resolveInheritedFile(mResolvedBaseFile);
1698                 // Inherit the dex metadata if present.
1699                 final File baseDexMetadataFile =
1700                         DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
1701                 if (baseDexMetadataFile != null) {
1702                     resolveInheritedFile(baseDexMetadataFile);
1703                 }
1704                 baseApk = existingBase;
1705             }
1706 
1707             // Inherit splits if not overridden
1708             if (!ArrayUtils.isEmpty(existing.splitNames)) {
1709                 for (int i = 0; i < existing.splitNames.length; i++) {
1710                     final String splitName = existing.splitNames[i];
1711                     final File splitFile = new File(existing.splitCodePaths[i]);
1712                     final boolean splitRemoved = removeSplitList.contains(splitName);
1713                     if (!stagedSplits.contains(splitName) && !splitRemoved) {
1714                         resolveInheritedFile(splitFile);
1715                         // Inherit the dex metadata if present.
1716                         final File splitDexMetadataFile =
1717                                 DexMetadataHelper.findDexMetadataForFile(splitFile);
1718                         if (splitDexMetadataFile != null) {
1719                             resolveInheritedFile(splitDexMetadataFile);
1720                         }
1721                     }
1722                 }
1723             }
1724 
1725             // Inherit compiled oat directory.
1726             final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
1727             mInheritedFilesBase = packageInstallDir;
1728             final File oatDir = new File(packageInstallDir, "oat");
1729             if (oatDir.exists()) {
1730                 final File[] archSubdirs = oatDir.listFiles();
1731 
1732                 // Keep track of all instruction sets we've seen compiled output for.
1733                 // If we're linking (and not copying) inherited files, we can recreate the
1734                 // instruction set hierarchy and link compiled output.
1735                 if (archSubdirs != null && archSubdirs.length > 0) {
1736                     final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
1737                     for (File archSubDir : archSubdirs) {
1738                         // Skip any directory that isn't an ISA subdir.
1739                         if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
1740                             continue;
1741                         }
1742 
1743                         mResolvedInstructionSets.add(archSubDir.getName());
1744                         List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
1745                         if (!oatFiles.isEmpty()) {
1746                             mResolvedInheritedFiles.addAll(oatFiles);
1747                         }
1748                     }
1749                 }
1750             }
1751 
1752             // Inherit native libraries for DONT_KILL sessions.
1753             if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
1754                 File[] libDirs = new File[]{
1755                         new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME),
1756                         new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)};
1757                 for (File libDir : libDirs) {
1758                     if (!libDir.exists() || !libDir.isDirectory()) {
1759                         continue;
1760                     }
1761                     final List<File> libDirsToInherit = new LinkedList<>();
1762                     for (File archSubDir : libDir.listFiles()) {
1763                         if (!archSubDir.isDirectory()) {
1764                             continue;
1765                         }
1766                         String relLibPath;
1767                         try {
1768                             relLibPath = getRelativePath(archSubDir, packageInstallDir);
1769                         } catch (IOException e) {
1770                             Slog.e(TAG, "Skipping linking of native library directory!", e);
1771                             // shouldn't be possible, but let's avoid inheriting these to be safe
1772                             libDirsToInherit.clear();
1773                             break;
1774                         }
1775                         if (!mResolvedNativeLibPaths.contains(relLibPath)) {
1776                             mResolvedNativeLibPaths.add(relLibPath);
1777                         }
1778                         libDirsToInherit.addAll(Arrays.asList(archSubDir.listFiles()));
1779                     }
1780                     mResolvedInheritedFiles.addAll(libDirsToInherit);
1781                 }
1782             }
1783         }
1784         if (baseApk.useEmbeddedDex) {
1785             for (File file : mResolvedStagedFiles) {
1786                 if (file.getName().endsWith(".apk")
1787                         && !DexManager.auditUncompressedDexInApk(file.getPath())) {
1788                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1789                             "Some dex are not uncompressed and aligned correctly for "
1790                             + mPackageName);
1791                 }
1792             }
1793         }
1794         if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
1795             throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
1796                     "Missing split for " + mPackageName);
1797         }
1798     }
1799 
resolveAndStageFile(File origFile, File targetFile)1800     private void resolveAndStageFile(File origFile, File targetFile)
1801             throws PackageManagerException {
1802         mResolvedStagedFiles.add(targetFile);
1803         maybeRenameFile(origFile, targetFile);
1804 
1805         final File originalSignature = new File(
1806                 VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
1807         // Make sure .fsv_sig exists when it should, then resolve and stage it.
1808         if (originalSignature.exists()) {
1809             // mVerityFound can only change from false to true here during the staging loop. Since
1810             // all or none of files should have .fsv_sig, this should only happen in the first time
1811             // (or never), otherwise bail out.
1812             if (!mVerityFound) {
1813                 mVerityFound = true;
1814                 if (mResolvedStagedFiles.size() > 1) {
1815                     throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
1816                             "Some file is missing fs-verity signature");
1817                 }
1818             }
1819         } else {
1820             if (!mVerityFound) {
1821                 return;
1822             }
1823             throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
1824                     "Missing corresponding fs-verity signature to " + origFile);
1825         }
1826 
1827         final File stagedSignature = new File(
1828                 VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
1829         maybeRenameFile(originalSignature, stagedSignature);
1830         mResolvedStagedFiles.add(stagedSignature);
1831     }
1832 
resolveInheritedFile(File origFile)1833     private void resolveInheritedFile(File origFile) {
1834         mResolvedInheritedFiles.add(origFile);
1835 
1836         // Inherit the fsverity signature file if present.
1837         final File fsveritySignatureFile = new File(
1838                 VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
1839         if (fsveritySignatureFile.exists()) {
1840             mResolvedInheritedFiles.add(fsveritySignatureFile);
1841         }
1842     }
1843 
1844     @GuardedBy("mLock")
assertApkConsistentLocked(String tag, ApkLite apk)1845     private void assertApkConsistentLocked(String tag, ApkLite apk)
1846             throws PackageManagerException {
1847         if (!mPackageName.equals(apk.packageName)) {
1848             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
1849                     + apk.packageName + " inconsistent with " + mPackageName);
1850         }
1851         if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
1852             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1853                     + " specified package " + params.appPackageName
1854                     + " inconsistent with " + apk.packageName);
1855         }
1856         if (mVersionCode != apk.getLongVersionCode()) {
1857             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1858                     + " version code " + apk.versionCode + " inconsistent with "
1859                     + mVersionCode);
1860         }
1861         if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
1862             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1863                     tag + " signatures are inconsistent");
1864         }
1865     }
1866 
1867     /**
1868      * Determine if creating hard links between source and destination is
1869      * possible. That is, do they all live on the same underlying device.
1870      */
isLinkPossible(List<File> fromFiles, File toDir)1871     private boolean isLinkPossible(List<File> fromFiles, File toDir) {
1872         try {
1873             final StructStat toStat = Os.stat(toDir.getAbsolutePath());
1874             for (File fromFile : fromFiles) {
1875                 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
1876                 if (fromStat.st_dev != toStat.st_dev) {
1877                     return false;
1878                 }
1879             }
1880         } catch (ErrnoException e) {
1881             Slog.w(TAG, "Failed to detect if linking possible: " + e);
1882             return false;
1883         }
1884         return true;
1885     }
1886 
1887     /**
1888      * @return the uid of the owner this session
1889      */
getInstallerUid()1890     public int getInstallerUid() {
1891         synchronized (mLock) {
1892             return mInstallerUid;
1893         }
1894     }
1895 
1896     /**
1897      * @return the timestamp of when this session last changed state
1898      */
getUpdatedMillis()1899     public long getUpdatedMillis() {
1900         synchronized (mLock) {
1901             return updatedMillis;
1902         }
1903     }
1904 
getInstallerPackageName()1905     String getInstallerPackageName() {
1906         synchronized (mLock) {
1907             return mInstallerPackageName;
1908         }
1909     }
1910 
getRelativePath(File file, File base)1911     private static String getRelativePath(File file, File base) throws IOException {
1912         final String pathStr = file.getAbsolutePath();
1913         final String baseStr = base.getAbsolutePath();
1914         // Don't allow relative paths.
1915         if (pathStr.contains("/.") ) {
1916             throw new IOException("Invalid path (was relative) : " + pathStr);
1917         }
1918 
1919         if (pathStr.startsWith(baseStr)) {
1920             return pathStr.substring(baseStr.length());
1921         }
1922 
1923         throw new IOException("File: " + pathStr + " outside base: " + baseStr);
1924     }
1925 
createOatDirs(List<String> instructionSets, File fromDir)1926     private void createOatDirs(List<String> instructionSets, File fromDir)
1927             throws PackageManagerException {
1928         for (String instructionSet : instructionSets) {
1929             try {
1930                 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
1931             } catch (InstallerException e) {
1932                 throw PackageManagerException.from(e);
1933             }
1934         }
1935     }
1936 
linkFiles(List<File> fromFiles, File toDir, File fromDir)1937     private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
1938             throws IOException {
1939         for (File fromFile : fromFiles) {
1940             final String relativePath = getRelativePath(fromFile, fromDir);
1941             try {
1942                 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
1943                         toDir.getAbsolutePath());
1944             } catch (InstallerException e) {
1945                 throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
1946                         + fromDir + ", " + toDir + ")", e);
1947             }
1948         }
1949 
1950         Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
1951     }
1952 
copyFiles(List<File> fromFiles, File toDir)1953     private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
1954         // Remove any partial files from previous attempt
1955         for (File file : toDir.listFiles()) {
1956             if (file.getName().endsWith(".tmp")) {
1957                 file.delete();
1958             }
1959         }
1960 
1961         for (File fromFile : fromFiles) {
1962             final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
1963             if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
1964             if (!FileUtils.copyFile(fromFile, tmpFile)) {
1965                 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
1966             }
1967             try {
1968                 Os.chmod(tmpFile.getAbsolutePath(), 0644);
1969             } catch (ErrnoException e) {
1970                 throw new IOException("Failed to chmod " + tmpFile);
1971             }
1972             final File toFile = new File(toDir, fromFile.getName());
1973             if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
1974             if (!tmpFile.renameTo(toFile)) {
1975                 throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
1976             }
1977         }
1978         Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
1979     }
1980 
extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)1981     private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
1982             throws PackageManagerException {
1983         final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
1984         if (!inherit) {
1985             // Start from a clean slate
1986             NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
1987         }
1988 
1989         NativeLibraryHelper.Handle handle = null;
1990         try {
1991             handle = NativeLibraryHelper.Handle.create(packageDir);
1992             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
1993                     abiOverride);
1994             if (res != PackageManager.INSTALL_SUCCEEDED) {
1995                 throw new PackageManagerException(res,
1996                         "Failed to extract native libraries, res=" + res);
1997             }
1998         } catch (IOException e) {
1999             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2000                     "Failed to extract native libraries", e);
2001         } finally {
2002             IoUtils.closeQuietly(handle);
2003         }
2004     }
2005 
setPermissionsResult(boolean accepted)2006     void setPermissionsResult(boolean accepted) {
2007         if (!mSealed) {
2008             throw new SecurityException("Must be sealed to accept permissions");
2009         }
2010 
2011         if (accepted) {
2012             // Mark and kick off another install pass
2013             synchronized (mLock) {
2014                 mPermissionsManuallyAccepted = true;
2015                 mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
2016             }
2017         } else {
2018             destroyInternal();
2019             dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
2020         }
2021     }
2022 
2023     /**
2024      * Adds a child session ID without any safety / sanity checks. This should only be used to
2025      * build a session from XML or similar.
2026      */
addChildSessionIdInternal(int sessionId)2027     void addChildSessionIdInternal(int sessionId) {
2028         mChildSessionIds.put(sessionId, 0);
2029     }
2030 
open()2031     public void open() throws IOException {
2032         if (mActiveCount.getAndIncrement() == 0) {
2033             mCallback.onSessionActiveChanged(this, true);
2034         }
2035 
2036         boolean wasPrepared;
2037         synchronized (mLock) {
2038             wasPrepared = mPrepared;
2039             if (!mPrepared) {
2040                 if (stageDir != null) {
2041                     prepareStageDir(stageDir);
2042                 } else if (params.isMultiPackage) {
2043                     // it's all ok
2044                 } else {
2045                     throw new IllegalArgumentException("stageDir must be set");
2046                 }
2047 
2048                 mPrepared = true;
2049             }
2050         }
2051 
2052         if (!wasPrepared) {
2053             mCallback.onSessionPrepared(this);
2054         }
2055     }
2056 
2057     @Override
close()2058     public void close() {
2059         closeInternal(true);
2060     }
2061 
closeInternal(boolean checkCaller)2062     private void closeInternal(boolean checkCaller) {
2063         int activeCount;
2064         synchronized (mLock) {
2065             if (checkCaller) {
2066                 assertCallerIsOwnerOrRootLocked();
2067             }
2068 
2069             activeCount = mActiveCount.decrementAndGet();
2070         }
2071 
2072         if (activeCount == 0) {
2073             mCallback.onSessionActiveChanged(this, false);
2074         }
2075     }
2076 
2077     @Override
abandon()2078     public void abandon() {
2079         if (hasParentSessionId()) {
2080             throw new IllegalStateException(
2081                     "Session " + sessionId + " is a child of multi-package session "
2082                             + mParentSessionId +  " and may not be abandoned directly.");
2083         }
2084         synchronized (mLock) {
2085             assertCallerIsOwnerOrRootLocked();
2086 
2087             if (isStagedAndInTerminalState()) {
2088                 // We keep the session in the database if it's in a finalized state. It will be
2089                 // removed by PackageInstallerService when the last update time is old enough.
2090                 // Also, in such cases cleanStageDir() has already been executed so no need to
2091                 // do it now.
2092                 return;
2093             }
2094             if (mCommitted && params.isStaged) {
2095                 synchronized (mLock) {
2096                     mDestroyed = true;
2097                 }
2098                 mStagingManager.abortCommittedSession(this);
2099 
2100                 cleanStageDir();
2101             }
2102 
2103             if (mRelinquished) {
2104                 Slog.d(TAG, "Ignoring abandon after commit relinquished control");
2105                 return;
2106             }
2107             destroyInternal();
2108         }
2109 
2110         dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
2111     }
2112 
2113     @Override
isMultiPackage()2114     public boolean isMultiPackage() {
2115         return params.isMultiPackage;
2116     }
2117 
2118     @Override
isStaged()2119     public boolean isStaged() {
2120         return params.isStaged;
2121     }
2122 
2123     @Override
getChildSessionIds()2124     public int[] getChildSessionIds() {
2125         final int[] childSessionIds = mChildSessionIds.copyKeys();
2126         if (childSessionIds != null) {
2127             return childSessionIds;
2128         }
2129         return EMPTY_CHILD_SESSION_ARRAY;
2130     }
2131 
2132     @Override
addChildSessionId(int childSessionId)2133     public void addChildSessionId(int childSessionId) {
2134         final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
2135         if (childSession == null
2136                 || (childSession.hasParentSessionId() && childSession.mParentSessionId != sessionId)
2137                 || childSession.mCommitted
2138                 || childSession.mDestroyed) {
2139             throw new IllegalStateException("Unable to add child session " + childSessionId
2140                             + " as it does not exist or is in an invalid state.");
2141         }
2142         synchronized (mLock) {
2143             assertCallerIsOwnerOrRootLocked();
2144             assertPreparedAndNotSealedLocked("addChildSessionId");
2145 
2146             final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId);
2147             if (indexOfSession >= 0) {
2148                 return;
2149             }
2150             childSession.setParentSessionId(this.sessionId);
2151             addChildSessionIdInternal(childSessionId);
2152         }
2153     }
2154 
2155     @Override
removeChildSessionId(int sessionId)2156     public void removeChildSessionId(int sessionId) {
2157         final PackageInstallerSession session = mSessionProvider.getSession(sessionId);
2158         synchronized (mLock) {
2159             final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
2160             if (session != null) {
2161                 session.setParentSessionId(SessionInfo.INVALID_ID);
2162             }
2163             if (indexOfSession < 0) {
2164                 // not added in the first place; no-op
2165                 return;
2166             }
2167             mChildSessionIds.removeAt(indexOfSession);
2168         }
2169     }
2170 
2171     /**
2172      * Sets the parent session ID if not already set.
2173      * If {@link SessionInfo#INVALID_ID} is passed, it will be unset.
2174      */
setParentSessionId(int parentSessionId)2175     void setParentSessionId(int parentSessionId) {
2176         synchronized (mLock) {
2177             if (parentSessionId != SessionInfo.INVALID_ID
2178                     && mParentSessionId != SessionInfo.INVALID_ID) {
2179                 throw new IllegalStateException("The parent of " + sessionId + " is" + " already"
2180                         + "set to " + mParentSessionId);
2181             }
2182             this.mParentSessionId = parentSessionId;
2183         }
2184     }
2185 
hasParentSessionId()2186     boolean hasParentSessionId() {
2187         return mParentSessionId != SessionInfo.INVALID_ID;
2188     }
2189 
2190     @Override
getParentSessionId()2191     public int getParentSessionId() {
2192         return mParentSessionId;
2193     }
2194 
dispatchSessionFinished(int returnCode, String msg, Bundle extras)2195     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
2196         final IPackageInstallObserver2 observer;
2197         final String packageName;
2198         synchronized (mLock) {
2199             mFinalStatus = returnCode;
2200             mFinalMessage = msg;
2201 
2202             observer = mRemoteObserver;
2203             packageName = mPackageName;
2204         }
2205 
2206         if (observer != null) {
2207             // Execute observer.onPackageInstalled on different tread as we don't want callers
2208             // inside the system server have to worry about catching the callbacks while they are
2209             // calling into the session
2210             final SomeArgs args = SomeArgs.obtain();
2211             args.arg1 = packageName;
2212             args.arg2 = msg;
2213             args.arg3 = extras;
2214             args.arg4 = observer;
2215             args.argi1 = returnCode;
2216 
2217             mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
2218         }
2219 
2220         final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
2221 
2222         // Send broadcast to default launcher only if it's a new install
2223         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
2224         if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
2225             mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);
2226         }
2227 
2228         mCallback.onSessionFinished(this, success);
2229     }
2230 
2231     /** {@hide} */
setStagedSessionReady()2232     void setStagedSessionReady() {
2233         synchronized (mLock) {
2234             mStagedSessionReady = true;
2235             mStagedSessionApplied = false;
2236             mStagedSessionFailed = false;
2237             mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
2238             mStagedSessionErrorMessage = "";
2239         }
2240         mCallback.onStagedSessionChanged(this);
2241     }
2242 
2243     /** {@hide} */
setStagedSessionFailed(@tagedSessionErrorCode int errorCode, String errorMessage)2244     void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
2245                                 String errorMessage) {
2246         synchronized (mLock) {
2247             mStagedSessionReady = false;
2248             mStagedSessionApplied = false;
2249             mStagedSessionFailed = true;
2250             mStagedSessionErrorCode = errorCode;
2251             mStagedSessionErrorMessage = errorMessage;
2252             Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
2253         }
2254         cleanStageDir();
2255         mCallback.onStagedSessionChanged(this);
2256     }
2257 
2258     /** {@hide} */
setStagedSessionApplied()2259     void setStagedSessionApplied() {
2260         synchronized (mLock) {
2261             mStagedSessionReady = false;
2262             mStagedSessionApplied = true;
2263             mStagedSessionFailed = false;
2264             mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
2265             mStagedSessionErrorMessage = "";
2266             Slog.d(TAG, "Marking session " + sessionId + " as applied");
2267         }
2268         cleanStageDir();
2269         mCallback.onStagedSessionChanged(this);
2270     }
2271 
2272     /** {@hide} */
isStagedSessionReady()2273     boolean isStagedSessionReady() {
2274         return mStagedSessionReady;
2275     }
2276 
2277     /** {@hide} */
isStagedSessionApplied()2278     boolean isStagedSessionApplied() {
2279         return mStagedSessionApplied;
2280     }
2281 
2282     /** {@hide} */
isStagedSessionFailed()2283     boolean isStagedSessionFailed() {
2284         return mStagedSessionFailed;
2285     }
2286 
2287     /** {@hide} */
getStagedSessionErrorCode()2288     @StagedSessionErrorCode int getStagedSessionErrorCode() {
2289         return mStagedSessionErrorCode;
2290     }
2291 
2292     /** {@hide} */
getStagedSessionErrorMessage()2293     String getStagedSessionErrorMessage() {
2294         return mStagedSessionErrorMessage;
2295     }
2296 
destroyInternal()2297     private void destroyInternal() {
2298         synchronized (mLock) {
2299             mSealed = true;
2300             if (!params.isStaged || isStagedAndInTerminalState()) {
2301                 mDestroyed = true;
2302             }
2303             // Force shut down all bridges
2304             for (RevocableFileDescriptor fd : mFds) {
2305                 fd.revoke();
2306             }
2307             for (FileBridge bridge : mBridges) {
2308                 bridge.forceClose();
2309             }
2310         }
2311         // For staged sessions, we don't delete the directory where the packages have been copied,
2312         // since these packages are supposed to be read on reboot.
2313         // Those dirs are deleted when the staged session has reached a final state.
2314         if (stageDir != null && !params.isStaged) {
2315             try {
2316                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
2317             } catch (InstallerException ignored) {
2318             }
2319         }
2320     }
2321 
cleanStageDir()2322     private void cleanStageDir() {
2323         if (isMultiPackage()) {
2324             for (int childSessionId : getChildSessionIds()) {
2325                 mSessionProvider.getSession(childSessionId).cleanStageDir();
2326             }
2327         } else {
2328             try {
2329                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
2330             } catch (InstallerException ignored) {
2331             }
2332         }
2333     }
2334 
dump(IndentingPrintWriter pw)2335     void dump(IndentingPrintWriter pw) {
2336         synchronized (mLock) {
2337             dumpLocked(pw);
2338         }
2339     }
2340 
2341     @GuardedBy("mLock")
dumpLocked(IndentingPrintWriter pw)2342     private void dumpLocked(IndentingPrintWriter pw) {
2343         pw.println("Session " + sessionId + ":");
2344         pw.increaseIndent();
2345 
2346         pw.printPair("userId", userId);
2347         pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
2348         pw.printPair("mInstallerPackageName", mInstallerPackageName);
2349         pw.printPair("mInstallerUid", mInstallerUid);
2350         pw.printPair("createdMillis", createdMillis);
2351         pw.printPair("stageDir", stageDir);
2352         pw.printPair("stageCid", stageCid);
2353         pw.println();
2354 
2355         params.dump(pw);
2356 
2357         pw.printPair("mClientProgress", mClientProgress);
2358         pw.printPair("mProgress", mProgress);
2359         pw.printPair("mCommitted", mCommitted);
2360         pw.printPair("mSealed", mSealed);
2361         pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
2362         pw.printPair("mRelinquished", mRelinquished);
2363         pw.printPair("mDestroyed", mDestroyed);
2364         pw.printPair("mFds", mFds.size());
2365         pw.printPair("mBridges", mBridges.size());
2366         pw.printPair("mFinalStatus", mFinalStatus);
2367         pw.printPair("mFinalMessage", mFinalMessage);
2368         pw.printPair("params.isMultiPackage", params.isMultiPackage);
2369         pw.printPair("params.isStaged", params.isStaged);
2370         pw.println();
2371 
2372         pw.decreaseIndent();
2373     }
2374 
writeGrantedRuntimePermissionsLocked(XmlSerializer out, String[] grantedRuntimePermissions)2375     private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
2376             String[] grantedRuntimePermissions) throws IOException {
2377         if (grantedRuntimePermissions != null) {
2378             for (String permission : grantedRuntimePermissions) {
2379                 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
2380                 writeStringAttribute(out, ATTR_NAME, permission);
2381                 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
2382             }
2383         }
2384     }
2385 
writeWhitelistedRestrictedPermissionsLocked(@onNull XmlSerializer out, @Nullable List<String> whitelistedRestrictedPermissions)2386     private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull XmlSerializer out,
2387             @Nullable List<String> whitelistedRestrictedPermissions) throws IOException {
2388         if (whitelistedRestrictedPermissions != null) {
2389             final int permissionCount = whitelistedRestrictedPermissions.size();
2390             for (int i = 0; i < permissionCount; i++) {
2391                 out.startTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION);
2392                 writeStringAttribute(out, ATTR_NAME, whitelistedRestrictedPermissions.get(i));
2393                 out.endTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION);
2394             }
2395         }
2396     }
2397 
2398 
buildAppIconFile(int sessionId, @NonNull File sessionsDir)2399     private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
2400         return new File(sessionsDir, "app_icon." + sessionId + ".png");
2401     }
2402 
2403     /**
2404      * Write this session to a {@link XmlSerializer}.
2405      *
2406      * @param out Where to write the session to
2407      * @param sessionsDir The directory containing the sessions
2408      */
write(@onNull XmlSerializer out, @NonNull File sessionsDir)2409     void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
2410         synchronized (mLock) {
2411             if (mDestroyed) {
2412                 return;
2413             }
2414 
2415             out.startTag(null, TAG_SESSION);
2416 
2417             writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
2418             writeIntAttribute(out, ATTR_USER_ID, userId);
2419             writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
2420                     mInstallerPackageName);
2421             writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
2422             writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
2423             writeLongAttribute(out, ATTR_UPDATED_MILLIS, updatedMillis);
2424             if (stageDir != null) {
2425                 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
2426                         stageDir.getAbsolutePath());
2427             }
2428             if (stageCid != null) {
2429                 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
2430             }
2431             writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
2432             writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
2433             writeBooleanAttribute(out, ATTR_SEALED, isSealed());
2434 
2435             writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
2436             writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
2437             writeBooleanAttribute(out, ATTR_IS_READY, mStagedSessionReady);
2438             writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed);
2439             writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied);
2440             writeIntAttribute(out, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode);
2441             writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE,
2442                     mStagedSessionErrorMessage);
2443             // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
2444             //                       we've read all sessions.
2445             writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId);
2446             writeIntAttribute(out, ATTR_MODE, params.mode);
2447             writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
2448             writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
2449             writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
2450             writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
2451             writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
2452             writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
2453             writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
2454             writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
2455             writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
2456             writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
2457             writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
2458 
2459             writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
2460             writeWhitelistedRestrictedPermissionsLocked(out,
2461                     params.whitelistedRestrictedPermissions);
2462 
2463             // Persist app icon if changed since last written
2464             File appIconFile = buildAppIconFile(sessionId, sessionsDir);
2465             if (params.appIcon == null && appIconFile.exists()) {
2466                 appIconFile.delete();
2467             } else if (params.appIcon != null
2468                     && appIconFile.lastModified() != params.appIconLastModified) {
2469                 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
2470                 FileOutputStream os = null;
2471                 try {
2472                     os = new FileOutputStream(appIconFile);
2473                     params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
2474                 } catch (IOException e) {
2475                     Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
2476                 } finally {
2477                     IoUtils.closeQuietly(os);
2478                 }
2479 
2480                 params.appIconLastModified = appIconFile.lastModified();
2481             }
2482             final int[] childSessionIds = getChildSessionIds();
2483             for (int childSessionId : childSessionIds) {
2484                 out.startTag(null, TAG_CHILD_SESSION);
2485                 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
2486                 out.endTag(null, TAG_CHILD_SESSION);
2487             }
2488         }
2489 
2490         out.endTag(null, TAG_SESSION);
2491     }
2492 
2493     // Sanity check to be performed when the session is restored from an external file. Only one
2494     // of the session states should be true, or none of them.
isStagedSessionStateValid(boolean isReady, boolean isApplied, boolean isFailed)2495     private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
2496                                                      boolean isFailed) {
2497         return (!isReady && !isApplied && !isFailed)
2498                 || (isReady && !isApplied && !isFailed)
2499                 || (!isReady && isApplied && !isFailed)
2500                 || (!isReady && !isApplied && isFailed);
2501     }
2502 
2503     /**
2504      * Read new session from a {@link XmlPullParser xml description} and create it.
2505      *
2506      * @param in The source of the description
2507      * @param callback Callback the session uses to notify about changes of it's state
2508      * @param context Context to be used by the session
2509      * @param pm PackageManager to use by the session
2510      * @param installerThread Thread to be used for callbacks of this session
2511      * @param sessionsDir The directory the sessions are stored in
2512      *
2513      * @param sessionProvider
2514      * @return The newly created session
2515      */
readFromXml(@onNull XmlPullParser in, @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, @NonNull PackageManagerService pm, Looper installerThread, @NonNull StagingManager stagingManager, @NonNull File sessionsDir, @NonNull PackageSessionProvider sessionProvider)2516     public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
2517             @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
2518             @NonNull PackageManagerService pm, Looper installerThread,
2519             @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
2520             @NonNull PackageSessionProvider sessionProvider)
2521             throws IOException, XmlPullParserException {
2522         final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
2523         final int userId = readIntAttribute(in, ATTR_USER_ID);
2524         final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
2525         final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
2526                 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
2527         final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
2528         long updatedMillis = readLongAttribute(in, ATTR_UPDATED_MILLIS);
2529         final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
2530         final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
2531         final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
2532         final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
2533         final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED);
2534         final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
2535         final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID,
2536                 SessionInfo.INVALID_ID);
2537 
2538         final SessionParams params = new SessionParams(
2539                 SessionParams.MODE_INVALID);
2540         params.isMultiPackage = readBooleanAttribute(in, ATTR_MULTI_PACKAGE, false);
2541         params.isStaged = readBooleanAttribute(in, ATTR_STAGED_SESSION, false);
2542         params.mode = readIntAttribute(in, ATTR_MODE);
2543         params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
2544         params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
2545         params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
2546         params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
2547         params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
2548         params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
2549         params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
2550         params.originatingUid =
2551                 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
2552         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
2553         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
2554         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
2555         params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
2556 
2557         final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
2558         if (appIconFile.exists()) {
2559             params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
2560             params.appIconLastModified = appIconFile.lastModified();
2561         }
2562         final boolean isReady = readBooleanAttribute(in, ATTR_IS_READY);
2563         final boolean isFailed = readBooleanAttribute(in, ATTR_IS_FAILED);
2564         final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
2565         final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE,
2566                 SessionInfo.STAGED_SESSION_NO_ERROR);
2567         final String stagedSessionErrorMessage = readStringAttribute(in,
2568                 ATTR_STAGED_SESSION_ERROR_MESSAGE);
2569 
2570         if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
2571             throw new IllegalArgumentException("Can't restore staged session with invalid state.");
2572         }
2573 
2574         // Parse sub tags of this session, typically used for repeated values / arrays.
2575         // Sub tags can come in any order, therefore we need to keep track of what we find while
2576         // parsing and only set the right values at the end.
2577 
2578         // Store the current depth. We should stop parsing when we reach an end tag at the same
2579         // depth.
2580         List<String> grantedRuntimePermissions = new ArrayList<>();
2581         List<String> whitelistedRestrictedPermissions = new ArrayList<>();
2582         List<Integer> childSessionIds = new ArrayList<>();
2583         int outerDepth = in.getDepth();
2584         int type;
2585         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
2586                 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
2587             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
2588                 continue;
2589             }
2590             if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
2591                 grantedRuntimePermissions.add(readStringAttribute(in, ATTR_NAME));
2592             }
2593             if (TAG_WHITELISTED_RESTRICTED_PERMISSION.equals(in.getName())) {
2594                 whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME));
2595 
2596             }
2597             if (TAG_CHILD_SESSION.equals(in.getName())) {
2598                 childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
2599             }
2600         }
2601 
2602         if (grantedRuntimePermissions.size() > 0) {
2603             params.grantedRuntimePermissions = grantedRuntimePermissions
2604                     .stream().toArray(String[]::new);
2605         }
2606 
2607         if (whitelistedRestrictedPermissions.size() > 0) {
2608             params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
2609         }
2610 
2611         int[] childSessionIdsArray;
2612         if (childSessionIds.size() > 0) {
2613             childSessionIdsArray = childSessionIds.stream().mapToInt(i -> i).toArray();
2614         } else {
2615             childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
2616         }
2617 
2618         return new PackageInstallerSession(callback, context, pm, sessionProvider,
2619                 installerThread, stagingManager, sessionId, userId, installerPackageName,
2620                 installerUid, params, createdMillis, stageDir, stageCid, prepared, committed,
2621                 sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
2622                 stagedSessionErrorCode, stagedSessionErrorMessage);
2623     }
2624 
2625     /**
2626      * Reads the session ID from a child session tag stored in the provided {@link XmlPullParser}
2627      */
readChildSessionIdFromXml(@onNull XmlPullParser in)2628     static int readChildSessionIdFromXml(@NonNull XmlPullParser in) {
2629         return readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID);
2630     }
2631 }
2632