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.app.AppOpsManager.MODE_DEFAULT;
20 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO;
21 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
22 import static android.content.pm.DataLoaderType.INCREMENTAL;
23 import static android.content.pm.DataLoaderType.STREAMING;
24 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
25 import static android.content.pm.PackageItemInfo.MAX_SAFE_LABEL_LENGTH;
26 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
27 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
28 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
29 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
30 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
31 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
32 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
33 import static android.content.pm.PackageManager.INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE;
34 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
35 import static android.content.pm.PackageManager.INSTALL_STAGED;
36 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
37 import static android.os.Process.INVALID_UID;
38 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
39 import static android.system.OsConstants.O_CREAT;
40 import static android.system.OsConstants.O_RDONLY;
41 import static android.system.OsConstants.O_WRONLY;
42 
43 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
44 import static com.android.internal.util.XmlUtils.readBitmapAttribute;
45 import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
46 import static com.android.internal.util.XmlUtils.readStringAttribute;
47 import static com.android.internal.util.XmlUtils.readUriAttribute;
48 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
49 import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
50 import static com.android.internal.util.XmlUtils.writeStringAttribute;
51 import static com.android.internal.util.XmlUtils.writeUriAttribute;
52 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
53 import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
54 import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb;
55 
56 import android.Manifest;
57 import android.annotation.AnyThread;
58 import android.annotation.IntDef;
59 import android.annotation.NonNull;
60 import android.annotation.Nullable;
61 import android.annotation.WorkerThread;
62 import android.app.AppOpsManager;
63 import android.app.BroadcastOptions;
64 import android.app.Notification;
65 import android.app.NotificationManager;
66 import android.app.admin.DevicePolicyEventLogger;
67 import android.app.admin.DevicePolicyManager;
68 import android.app.admin.DevicePolicyManagerInternal;
69 import android.compat.annotation.ChangeId;
70 import android.compat.annotation.Disabled;
71 import android.compat.annotation.EnabledSince;
72 import android.content.ComponentName;
73 import android.content.Context;
74 import android.content.Intent;
75 import android.content.IntentSender;
76 import android.content.pm.ApplicationInfo;
77 import android.content.pm.Checksum;
78 import android.content.pm.DataLoaderManager;
79 import android.content.pm.DataLoaderParams;
80 import android.content.pm.DataLoaderParamsParcel;
81 import android.content.pm.FileSystemControlParcel;
82 import android.content.pm.IDataLoader;
83 import android.content.pm.IDataLoaderStatusListener;
84 import android.content.pm.IOnChecksumsReadyListener;
85 import android.content.pm.IPackageInstallObserver2;
86 import android.content.pm.IPackageInstallerSession;
87 import android.content.pm.IPackageInstallerSessionFileSystemConnector;
88 import android.content.pm.IPackageLoadingProgressCallback;
89 import android.content.pm.InstallSourceInfo;
90 import android.content.pm.InstallationFile;
91 import android.content.pm.InstallationFileParcel;
92 import android.content.pm.PackageInfo;
93 import android.content.pm.PackageInstaller;
94 import android.content.pm.PackageInstaller.PreapprovalDetails;
95 import android.content.pm.PackageInstaller.SessionInfo;
96 import android.content.pm.PackageInstaller.SessionParams;
97 import android.content.pm.PackageInstaller.UserActionReason;
98 import android.content.pm.PackageManager;
99 import android.content.pm.PackageManager.PackageInfoFlags;
100 import android.content.pm.PackageManagerInternal;
101 import android.content.pm.SigningDetails;
102 import android.content.pm.dex.DexMetadataHelper;
103 import android.content.pm.parsing.ApkLite;
104 import android.content.pm.parsing.ApkLiteParseUtils;
105 import android.content.pm.parsing.PackageLite;
106 import android.content.pm.parsing.result.ParseResult;
107 import android.content.pm.parsing.result.ParseTypeImpl;
108 import android.content.res.ApkAssets;
109 import android.content.res.AssetManager;
110 import android.content.res.Configuration;
111 import android.content.res.Resources;
112 import android.graphics.Bitmap;
113 import android.graphics.BitmapFactory;
114 import android.icu.util.ULocale;
115 import android.os.Binder;
116 import android.os.Build;
117 import android.os.Bundle;
118 import android.os.Environment;
119 import android.os.FileBridge;
120 import android.os.FileUtils;
121 import android.os.Handler;
122 import android.os.Looper;
123 import android.os.Message;
124 import android.os.ParcelFileDescriptor;
125 import android.os.ParcelableException;
126 import android.os.Process;
127 import android.os.RemoteException;
128 import android.os.RevocableFileDescriptor;
129 import android.os.SELinux;
130 import android.os.ServiceManager;
131 import android.os.SystemProperties;
132 import android.os.UserHandle;
133 import android.os.incremental.IStorageHealthListener;
134 import android.os.incremental.IncrementalFileStorages;
135 import android.os.incremental.IncrementalManager;
136 import android.os.incremental.PerUidReadTimeouts;
137 import android.os.incremental.StorageHealthCheckParams;
138 import android.os.storage.StorageManager;
139 import android.provider.DeviceConfig;
140 import android.provider.Settings.Global;
141 import android.stats.devicepolicy.DevicePolicyEnums;
142 import android.system.ErrnoException;
143 import android.system.Int64Ref;
144 import android.system.Os;
145 import android.system.OsConstants;
146 import android.system.StructStat;
147 import android.text.TextUtils;
148 import android.util.ArrayMap;
149 import android.util.ArraySet;
150 import android.util.EventLog;
151 import android.util.ExceptionUtils;
152 import android.util.IntArray;
153 import android.util.Log;
154 import android.util.MathUtils;
155 import android.util.Slog;
156 import android.util.SparseArray;
157 import android.util.apk.ApkSignatureVerifier;
158 
159 import com.android.internal.R;
160 import com.android.internal.annotations.GuardedBy;
161 import com.android.internal.annotations.VisibleForTesting;
162 import com.android.internal.compat.IPlatformCompat;
163 import com.android.internal.content.InstallLocationUtils;
164 import com.android.internal.content.NativeLibraryHelper;
165 import com.android.internal.messages.nano.SystemMessageProto;
166 import com.android.internal.os.SomeArgs;
167 import com.android.internal.security.VerityUtils;
168 import com.android.internal.util.ArrayUtils;
169 import com.android.internal.util.CollectionUtils;
170 import com.android.internal.util.FrameworkStatsLog;
171 import com.android.internal.util.IndentingPrintWriter;
172 import com.android.internal.util.Preconditions;
173 import com.android.modules.utils.TypedXmlPullParser;
174 import com.android.modules.utils.TypedXmlSerializer;
175 import com.android.server.LocalServices;
176 import com.android.server.pm.Installer.InstallerException;
177 import com.android.server.pm.dex.DexManager;
178 import com.android.server.pm.pkg.AndroidPackage;
179 import com.android.server.pm.pkg.PackageStateInternal;
180 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
181 
182 import libcore.io.IoUtils;
183 import libcore.util.EmptyArray;
184 
185 import org.xmlpull.v1.XmlPullParser;
186 import org.xmlpull.v1.XmlPullParserException;
187 
188 import java.io.ByteArrayOutputStream;
189 import java.io.File;
190 import java.io.FileDescriptor;
191 import java.io.FileFilter;
192 import java.io.FileNotFoundException;
193 import java.io.FileOutputStream;
194 import java.io.IOException;
195 import java.nio.file.Files;
196 import java.security.NoSuchAlgorithmException;
197 import java.security.SignatureException;
198 import java.security.cert.Certificate;
199 import java.util.ArrayList;
200 import java.util.Arrays;
201 import java.util.Collections;
202 import java.util.List;
203 import java.util.Objects;
204 import java.util.Set;
205 import java.util.concurrent.CompletableFuture;
206 import java.util.concurrent.atomic.AtomicBoolean;
207 import java.util.concurrent.atomic.AtomicInteger;
208 import java.util.function.Predicate;
209 
210 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
211     private static final String TAG = "PackageInstallerSession";
212     private static final boolean LOGD = true;
213     private static final String REMOVE_MARKER_EXTENSION = ".removed";
214 
215     private static final int MSG_ON_SESSION_SEALED = 1;
216     private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 2;
217     private static final int MSG_INSTALL = 3;
218     private static final int MSG_ON_PACKAGE_INSTALLED = 4;
219     private static final int MSG_SESSION_VALIDATION_FAILURE = 5;
220     private static final int MSG_PRE_APPROVAL_REQUEST = 6;
221 
222     /** XML constants used for persisting a session */
223     static final String TAG_SESSION = "session";
224     static final String TAG_CHILD_SESSION = "childSession";
225     static final String TAG_SESSION_FILE = "sessionFile";
226     static final String TAG_SESSION_CHECKSUM = "sessionChecksum";
227     static final String TAG_SESSION_CHECKSUM_SIGNATURE = "sessionChecksumSignature";
228     private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
229     private static final String TAG_GRANT_PERMISSION = "grant-permission";
230     private static final String TAG_DENY_PERMISSION = "deny-permission";
231     private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
232             "whitelisted-restricted-permission";
233     private static final String TAG_AUTO_REVOKE_PERMISSIONS_MODE =
234             "auto-revoke-permissions-mode";
235     private static final String ATTR_SESSION_ID = "sessionId";
236     private static final String ATTR_USER_ID = "userId";
237     private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
238     private static final String ATTR_INSTALLER_PACKAGE_UID = "installerPackageUid";
239     private static final String ATTR_UPDATE_OWNER_PACKAGE_NAME = "updateOwnererPackageName";
240     private static final String ATTR_INSTALLER_ATTRIBUTION_TAG = "installerAttributionTag";
241     private static final String ATTR_INSTALLER_UID = "installerUid";
242     private static final String ATTR_INITIATING_PACKAGE_NAME =
243             "installInitiatingPackageName";
244     private static final String ATTR_ORIGINATING_PACKAGE_NAME =
245             "installOriginatingPackageName";
246     private static final String ATTR_CREATED_MILLIS = "createdMillis";
247     private static final String ATTR_UPDATED_MILLIS = "updatedMillis";
248     private static final String ATTR_COMMITTED_MILLIS = "committedMillis";
249     private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
250     private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
251     private static final String ATTR_PREPARED = "prepared";
252     private static final String ATTR_COMMITTED = "committed";
253     private static final String ATTR_DESTROYED = "destroyed";
254     private static final String ATTR_SEALED = "sealed";
255     private static final String ATTR_MULTI_PACKAGE = "multiPackage";
256     private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
257     private static final String ATTR_STAGED_SESSION = "stagedSession";
258     private static final String ATTR_IS_READY = "isReady";
259     private static final String ATTR_IS_FAILED = "isFailed";
260     private static final String ATTR_IS_APPLIED = "isApplied";
261     private static final String ATTR_PACKAGE_SOURCE = "packageSource";
262     private static final String ATTR_SESSION_ERROR_CODE = "errorCode";
263     private static final String ATTR_SESSION_ERROR_MESSAGE = "errorMessage";
264     private static final String ATTR_MODE = "mode";
265     private static final String ATTR_INSTALL_FLAGS = "installFlags";
266     private static final String ATTR_INSTALL_LOCATION = "installLocation";
267     private static final String ATTR_SIZE_BYTES = "sizeBytes";
268     private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
269     @Deprecated
270     private static final String ATTR_APP_ICON = "appIcon";
271     private static final String ATTR_APP_LABEL = "appLabel";
272     private static final String ATTR_ORIGINATING_URI = "originatingUri";
273     private static final String ATTR_ORIGINATING_UID = "originatingUid";
274     private static final String ATTR_REFERRER_URI = "referrerUri";
275     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
276     private static final String ATTR_VOLUME_UUID = "volumeUuid";
277     private static final String ATTR_NAME = "name";
278     private static final String ATTR_INSTALL_REASON = "installRason";
279     private static final String ATTR_IS_DATALOADER = "isDataLoader";
280     private static final String ATTR_DATALOADER_TYPE = "dataLoaderType";
281     private static final String ATTR_DATALOADER_PACKAGE_NAME = "dataLoaderPackageName";
282     private static final String ATTR_DATALOADER_CLASS_NAME = "dataLoaderClassName";
283     private static final String ATTR_DATALOADER_ARGUMENTS = "dataLoaderArguments";
284     private static final String ATTR_LOCATION = "location";
285     private static final String ATTR_LENGTH_BYTES = "lengthBytes";
286     private static final String ATTR_METADATA = "metadata";
287     private static final String ATTR_SIGNATURE = "signature";
288     private static final String ATTR_CHECKSUM_KIND = "checksumKind";
289     private static final String ATTR_CHECKSUM_VALUE = "checksumValue";
290     private static final String ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT =
291             "applicationEnabledSettingPersistent";
292 
293     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
294     private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
295     private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {};
296 
297     private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
298     private static final String APEX_FILE_EXTENSION = ".apex";
299 
300     private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000;
301     private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
302     private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
303 
304     /**
305      * If an app being installed targets {@link Build.VERSION_CODES#S API 31} and above, the app
306      * can be installed without user action.
307      * See {@link PackageInstaller.SessionParams#setRequireUserAction} for other conditions required
308      * to be satisfied for a silent install.
309      */
310     @ChangeId
311     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
312     private static final long SILENT_INSTALL_ALLOWED = 265131695L;
313 
314     /**
315      * The system supports pre-approval and update ownership features from
316      * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34}. The change id is used to make sure
317      * the system includes the fix of pre-approval with update ownership case. When checking the
318      * change id, if it is disabled, it means the build includes the fix. The more detail is on
319      * b/293644536.
320      * See {@link PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)} and
321      * {@link #requestUserPreapproval(PreapprovalDetails, IntentSender)} for more details.
322      */
323     @Disabled
324     @ChangeId
325     private static final long PRE_APPROVAL_WITH_UPDATE_OWNERSHIP_FIX = 293644536L;
326 
327     /**
328      * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link
329      * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#S} before getting the
330      * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
331      * result will not trigger any user action in
332      * {@link #checkUserActionRequirement(PackageInstallerSession, IntentSender)}.
333      */
334     private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE;
335 
336     /**
337      * Byte size limit for app metadata.
338      *
339      * Flag type: {@code long}
340      * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
341      */
342     private static final String PROPERTY_APP_METADATA_BYTE_SIZE_LIMIT =
343             "app_metadata_byte_size_limit";
344 
345     /** Default byte size limit for app metadata */
346     private static final long DEFAULT_APP_METADATA_BYTE_SIZE_LIMIT = 32000;
347 
348     private static final int APP_METADATA_FILE_ACCESS_MODE = 0640;
349 
350     // TODO: enforce INSTALL_ALLOW_TEST
351     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
352 
353     private final PackageInstallerService.InternalCallback mCallback;
354     private final Context mContext;
355     private final PackageManagerService mPm;
356     private final Installer mInstaller;
357     private final Handler mHandler;
358     private final PackageSessionProvider mSessionProvider;
359     private final SilentUpdatePolicy mSilentUpdatePolicy;
360     /**
361      * Note all calls must be done outside {@link #mLock} to prevent lock inversion.
362      */
363     private final StagingManager mStagingManager;
364 
365     final int sessionId;
366     final int userId;
367     final SessionParams params;
368     final long createdMillis;
369 
370     /** Used for tracking whether user action was required for an install. */
371     @Nullable
372     private Boolean mUserActionRequired;
373 
374     /** Staging location where client data is written. */
375     final File stageDir;
376     final String stageCid;
377 
378     private final AtomicInteger mActiveCount = new AtomicInteger();
379 
380     private final Object mLock = new Object();
381 
382     /**
383      * Used to detect and reject concurrent access to this session object to ensure mutation
384      * to multiple objects like {@link #addChildSessionId} are done atomically.
385      */
386     private final AtomicBoolean mTransactionLock = new AtomicBoolean(false);
387 
388     /** Timestamp of the last time this session changed state  */
389     @GuardedBy("mLock")
390     private long updatedMillis;
391 
392     /** Timestamp of the time this session is committed  */
393     @GuardedBy("mLock")
394     private long committedMillis;
395 
396     /** Uid of the creator of this session. */
397     private final int mOriginalInstallerUid;
398 
399     /** Package name of the app that created the installation session. */
400     private final String mOriginalInstallerPackageName;
401 
402     /** Uid of the owner of the installer session */
403     private volatile int mInstallerUid;
404 
405     /** Where this install request came from */
406     @GuardedBy("mLock")
407     private InstallSource mInstallSource;
408 
409     private final Object mProgressLock = new Object();
410 
411     @GuardedBy("mProgressLock")
412     private float mClientProgress = 0;
413     @GuardedBy("mProgressLock")
414     private float mInternalProgress = 0;
415 
416     @GuardedBy("mProgressLock")
417     private float mProgress = 0;
418     @GuardedBy("mProgressLock")
419     private float mReportedProgress = -1;
420     @GuardedBy("mProgressLock")
421     private float mIncrementalProgress = 0;
422 
423     /** State of the session. */
424     @GuardedBy("mLock")
425     private boolean mPrepared = false;
426     @GuardedBy("mLock")
427     private boolean mSealed = false;
428     @GuardedBy("mLock")
429     private boolean mShouldBeSealed = false;
430 
431     private final AtomicBoolean mPreapprovalRequested = new AtomicBoolean(false);
432     private final AtomicBoolean mCommitted = new AtomicBoolean(false);
433 
434     /**
435      * True if staging files are being used by external entities like {@link PackageSessionVerifier}
436      * or {@link PackageManagerService} which means it is not safe for {@link #abandon()} to clean
437      * up the files.
438      */
439     @GuardedBy("mLock")
440     private boolean mStageDirInUse = false;
441 
442     /**
443      * True if the verification is already in progress. This is used to prevent running
444      * verification again while one is already in progress which will break internal states.
445      *
446      * Worker thread only.
447      */
448     private boolean mVerificationInProgress = false;
449 
450     /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
451     @GuardedBy("mLock")
452     private boolean mPermissionsManuallyAccepted = false;
453 
454     @GuardedBy("mLock")
455     private int mFinalStatus;
456     @GuardedBy("mLock")
457     private String mFinalMessage;
458 
459     @GuardedBy("mLock")
460     private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
461     @GuardedBy("mLock")
462     private final ArrayList<FileBridge> mBridges = new ArrayList<>();
463 
464     @GuardedBy("mLock")
465     private IntentSender mRemoteStatusReceiver;
466 
467     @GuardedBy("mLock")
468     private PreapprovalDetails mPreapprovalDetails;
469 
470     /** Fields derived from commit parsing */
471     @GuardedBy("mLock")
472     private String mPackageName;
473     @GuardedBy("mLock")
474     private long mVersionCode;
475     @GuardedBy("mLock")
476     private SigningDetails mSigningDetails;
477     @GuardedBy("mLock")
478     private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
479     @GuardedBy("mLock")
480     private int mParentSessionId;
481 
482     @GuardedBy("mLock")
483     private boolean mHasDeviceAdminReceiver;
484 
485     @GuardedBy("mLock")
486     private int mUserActionRequirement;
487 
488     static class FileEntry {
489         private final int mIndex;
490         private final InstallationFile mFile;
491 
FileEntry(int index, InstallationFile file)492         FileEntry(int index, InstallationFile file) {
493             this.mIndex = index;
494             this.mFile = file;
495         }
496 
getIndex()497         int getIndex() {
498             return this.mIndex;
499         }
500 
getFile()501         InstallationFile getFile() {
502             return this.mFile;
503         }
504 
505         @Override
equals(Object obj)506         public boolean equals(Object obj) {
507             if (!(obj instanceof FileEntry)) {
508                 return false;
509             }
510             final FileEntry rhs = (FileEntry) obj;
511             return (mFile.getLocation() == rhs.mFile.getLocation()) && TextUtils.equals(
512                     mFile.getName(), rhs.mFile.getName());
513         }
514 
515         @Override
hashCode()516         public int hashCode() {
517             return Objects.hash(mFile.getLocation(), mFile.getName());
518         }
519     }
520 
521     @GuardedBy("mLock")
522     private ArraySet<FileEntry> mFiles = new ArraySet<>();
523 
524     static class PerFileChecksum {
525         private final Checksum[] mChecksums;
526         private final byte[] mSignature;
527 
PerFileChecksum(Checksum[] checksums, byte[] signature)528         PerFileChecksum(Checksum[] checksums, byte[] signature) {
529             mChecksums = checksums;
530             mSignature = signature;
531         }
532 
getChecksums()533         Checksum[] getChecksums() {
534             return this.mChecksums;
535         }
536 
getSignature()537         byte[] getSignature() {
538             return this.mSignature;
539         }
540     }
541 
542     @GuardedBy("mLock")
543     private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
544 
545     @GuardedBy("mLock")
546     private boolean mSessionApplied;
547     @GuardedBy("mLock")
548     private boolean mSessionReady;
549     @GuardedBy("mLock")
550     private boolean mSessionFailed;
551     @GuardedBy("mLock")
552     private int mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
553     @GuardedBy("mLock")
554     private String mSessionErrorMessage;
555 
556     @Nullable
557     final StagedSession mStagedSession;
558 
559     /**
560      * The callback to run when pre-reboot verification has ended. Used by {@link #abandon()}
561      * to delay session clean-up until it is safe to do so.
562      */
563     @GuardedBy("mLock")
564     @Nullable
565     private Runnable mPendingAbandonCallback;
566 
567     @VisibleForTesting
568     public class StagedSession implements StagingManager.StagedSession {
569         @Override
getChildSessions()570         public List<StagingManager.StagedSession> getChildSessions() {
571             if (!params.isMultiPackage) {
572                 return Collections.EMPTY_LIST;
573             }
574             synchronized (mLock) {
575                 int size = mChildSessions.size();
576                 List<StagingManager.StagedSession> childSessions = new ArrayList<>(size);
577                 for (int i = 0; i < size; ++i) {
578                     childSessions.add(mChildSessions.valueAt(i).mStagedSession);
579                 }
580                 return childSessions;
581             }
582         }
583 
584         @Override
sessionParams()585         public SessionParams sessionParams() {
586             return params;
587         }
588 
589         @Override
isMultiPackage()590         public boolean isMultiPackage() {
591             return params.isMultiPackage;
592         }
593 
594         @Override
isApexSession()595         public boolean isApexSession() {
596             return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
597         }
598 
599         @Override
sessionId()600         public int sessionId() {
601             return sessionId;
602         }
603 
604         @Override
containsApexSession()605         public boolean containsApexSession() {
606             return sessionContains((s) -> s.isApexSession());
607         }
608 
609         @Override
getPackageName()610         public String getPackageName() {
611             return PackageInstallerSession.this.getPackageName();
612         }
613 
614         @Override
setSessionReady()615         public void setSessionReady() {
616             PackageInstallerSession.this.setSessionReady();
617         }
618 
619         @Override
setSessionFailed(int errorCode, String errorMessage)620         public void setSessionFailed(int errorCode, String errorMessage) {
621             PackageInstallerSession.this.setSessionFailed(errorCode, errorMessage);
622         }
623 
624         @Override
setSessionApplied()625         public void setSessionApplied() {
626             PackageInstallerSession.this.setSessionApplied();
627         }
628 
629         @Override
containsApkSession()630         public boolean containsApkSession() {
631             return PackageInstallerSession.this.containsApkSession();
632         }
633 
634         /**
635          * Installs apks of staged session while skipping the verification process for a committed
636          * and ready session.
637          *
638          * @return a CompletableFuture that will be completed when installation completes.
639          */
640         @Override
installSession()641         public CompletableFuture<Void> installSession() {
642             assertCallerIsOwnerOrRootOrSystem();
643             assertNotChild("StagedSession#installSession");
644             Preconditions.checkArgument(isCommitted() && isSessionReady());
645             return install();
646         }
647 
648         @Override
hasParentSessionId()649         public boolean hasParentSessionId() {
650             return PackageInstallerSession.this.hasParentSessionId();
651         }
652 
653         @Override
getParentSessionId()654         public int getParentSessionId() {
655             return PackageInstallerSession.this.getParentSessionId();
656         }
657 
658         @Override
isCommitted()659         public boolean isCommitted() {
660             return PackageInstallerSession.this.isCommitted();
661         }
662 
663         @Override
isInTerminalState()664         public boolean isInTerminalState() {
665             return PackageInstallerSession.this.isInTerminalState();
666         }
667 
668         @Override
isDestroyed()669         public boolean isDestroyed() {
670             return PackageInstallerSession.this.isDestroyed();
671         }
672 
673         @Override
getCommittedMillis()674         public long getCommittedMillis() {
675             return PackageInstallerSession.this.getCommittedMillis();
676         }
677 
678         @Override
sessionContains(Predicate<StagingManager.StagedSession> filter)679         public boolean sessionContains(Predicate<StagingManager.StagedSession> filter) {
680             return PackageInstallerSession.this.sessionContains(s -> filter.test(s.mStagedSession));
681         }
682 
683         @Override
isSessionReady()684         public boolean isSessionReady() {
685             return PackageInstallerSession.this.isSessionReady();
686         }
687 
688         @Override
isSessionApplied()689         public boolean isSessionApplied() {
690             return PackageInstallerSession.this.isSessionApplied();
691         }
692 
693         @Override
isSessionFailed()694         public boolean isSessionFailed() {
695             return PackageInstallerSession.this.isSessionFailed();
696         }
697 
698         @Override
abandon()699         public void abandon() {
700             PackageInstallerSession.this.abandon();
701         }
702 
703         /**
704          * Resumes verification process for non-final committed staged session.
705          *
706          * Useful if a device gets rebooted before verification is complete and we need to restart
707          * the verification.
708          */
709         @Override
verifySession()710         public void verifySession() {
711             assertCallerIsOwnerOrRootOrSystem();
712             Preconditions.checkArgument(isCommitted());
713             Preconditions.checkArgument(!isInTerminalState());
714             verify();
715         }
716     }
717 
718     /**
719      * Path to the validated base APK for this session, which may point at an
720      * APK inside the session (when the session defines the base), or it may
721      * point at the existing base APK (when adding splits to an existing app).
722      * <p>
723      * This is used when confirming permissions, since we can't fully stage the
724      * session inside an ASEC before confirming with user.
725      */
726     @GuardedBy("mLock")
727     private File mResolvedBaseFile;
728 
729     @GuardedBy("mLock")
730     private final List<File> mResolvedStagedFiles = new ArrayList<>();
731     @GuardedBy("mLock")
732     private final List<File> mResolvedInheritedFiles = new ArrayList<>();
733     @GuardedBy("mLock")
734     private final List<String> mResolvedInstructionSets = new ArrayList<>();
735     @GuardedBy("mLock")
736     private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
737     @GuardedBy("mLock")
738     private File mInheritedFilesBase;
739     @GuardedBy("mLock")
740     private boolean mVerityFoundForApks;
741 
742     /**
743      * Both flags should be guarded with mLock whenever changes need to be in lockstep.
744      * Ok to check without mLock in case the proper check is done later, e.g. status callbacks
745      * for DataLoaders with deferred processing.
746      */
747     private volatile boolean mDestroyed = false;
748     private volatile boolean mDataLoaderFinished = false;
749 
750     @GuardedBy("mLock")
751     private IncrementalFileStorages mIncrementalFileStorages;
752 
753     @GuardedBy("mLock")
754     private PackageLite mPackageLite;
755 
756     /**
757      * Keep the target sdk of a validated apk.
758      */
759     @GuardedBy("mLock")
760     private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION;
761 
762     private static final FileFilter sAddedApkFilter = new FileFilter() {
763         @Override
764         public boolean accept(File file) {
765             // Installers can't stage directories, so it's fine to ignore
766             // entries like "lost+found".
767             if (file.isDirectory()) return false;
768             if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
769             if (isAppMetadata(file)) return false;
770             if (DexMetadataHelper.isDexMetadataFile(file)) return false;
771             if (VerityUtils.isFsveritySignatureFile(file)) return false;
772             if (ApkChecksums.isDigestOrDigestSignatureFile(file)) return false;
773             return true;
774         }
775     };
776     private static final FileFilter sAddedFilter = new FileFilter() {
777         @Override
778         public boolean accept(File file) {
779             // Installers can't stage directories, so it's fine to ignore
780             // entries like "lost+found".
781             if (file.isDirectory()) return false;
782             if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
783             return true;
784         }
785     };
786     private static final FileFilter sRemovedFilter = new FileFilter() {
787         @Override
788         public boolean accept(File file) {
789             if (file.isDirectory()) return false;
790             if (!file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
791             return true;
792         }
793     };
794 
isDataLoaderInstallation(SessionParams params)795     static boolean isDataLoaderInstallation(SessionParams params) {
796         return params.dataLoaderParams != null;
797     }
798 
isSystemDataLoaderInstallation(SessionParams params)799     static boolean isSystemDataLoaderInstallation(SessionParams params) {
800         if (!isDataLoaderInstallation(params)) {
801             return false;
802         }
803         return SYSTEM_DATA_LOADER_PACKAGE.equals(
804                 params.dataLoaderParams.getComponentName().getPackageName());
805     }
806 
807     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
808         @Override
809         public boolean handleMessage(Message msg) {
810             switch (msg.what) {
811                 case MSG_ON_SESSION_SEALED:
812                     handleSessionSealed();
813                     break;
814                 case MSG_STREAM_VALIDATE_AND_COMMIT:
815                     handleStreamValidateAndCommit();
816                     break;
817                 case MSG_INSTALL:
818                     handleInstall();
819                     break;
820                 case MSG_ON_PACKAGE_INSTALLED:
821                     final SomeArgs args = (SomeArgs) msg.obj;
822                     final String packageName = (String) args.arg1;
823                     final String message = (String) args.arg2;
824                     final Bundle extras = (Bundle) args.arg3;
825                     final IntentSender statusReceiver = (IntentSender) args.arg4;
826                     final int returnCode = args.argi1;
827                     final boolean isPreapproval = args.argi2 == 1;
828                     args.recycle();
829 
830                     sendOnPackageInstalled(mContext, statusReceiver, sessionId,
831                             isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
832                             packageName, returnCode, isPreapproval, message, extras);
833 
834                     break;
835                 case MSG_SESSION_VALIDATION_FAILURE:
836                     final int error = msg.arg1;
837                     final String detailMessage = (String) msg.obj;
838                     onSessionValidationFailure(error, detailMessage);
839                     break;
840                 case MSG_PRE_APPROVAL_REQUEST:
841                     handlePreapprovalRequest();
842                     break;
843             }
844 
845             return true;
846         }
847     };
848 
isDataLoaderInstallation()849     private boolean isDataLoaderInstallation() {
850         return isDataLoaderInstallation(this.params);
851     }
852 
isStreamingInstallation()853     private boolean isStreamingInstallation() {
854         return isDataLoaderInstallation() && params.dataLoaderParams.getType() == STREAMING;
855     }
856 
isIncrementalInstallation()857     private boolean isIncrementalInstallation() {
858         return isDataLoaderInstallation() && params.dataLoaderParams.getType() == INCREMENTAL;
859     }
860 
isSystemDataLoaderInstallation()861     private boolean isSystemDataLoaderInstallation() {
862         return isSystemDataLoaderInstallation(this.params);
863     }
864 
865     /**
866      * @return {@code true} iff the installing is app an device owner or affiliated profile owner.
867      */
isInstallerDeviceOwnerOrAffiliatedProfileOwner()868     private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwner() {
869         assertNotLocked("isInstallerDeviceOwnerOrAffiliatedProfileOwner");
870         if (userId != UserHandle.getUserId(getInstallerUid())) {
871             return false;
872         }
873         DevicePolicyManagerInternal dpmi =
874                 LocalServices.getService(DevicePolicyManagerInternal.class);
875         // It may wait for a long time to finish {@code dpmi.canSilentlyInstallPackage}.
876         // Please don't acquire mLock before calling {@code dpmi.canSilentlyInstallPackage}.
877         return dpmi != null && dpmi.canSilentlyInstallPackage(
878                 getInstallSource().mInstallerPackageName, mInstallerUid);
879     }
880 
881     private static final int USER_ACTION_NOT_NEEDED = 0;
882     private static final int USER_ACTION_REQUIRED = 1;
883     private static final int USER_ACTION_PENDING_APK_PARSING = 2;
884     private static final int USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER = 3;
885 
886     @IntDef({
887             USER_ACTION_NOT_NEEDED,
888             USER_ACTION_REQUIRED,
889             USER_ACTION_PENDING_APK_PARSING,
890             USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER,
891     })
892     @interface UserActionRequirement {}
893 
894     /**
895      * Checks if the permissions still need to be confirmed.
896      *
897      * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
898      * installer might still {@link #transfer(String) change}.
899      *
900      * @return {@code true} iff we need to ask to confirm the permissions?
901      */
902     @UserActionRequirement
computeUserActionRequirement()903     private int computeUserActionRequirement() {
904         final String packageName;
905         final boolean hasDeviceAdminReceiver;
906         synchronized (mLock) {
907             if (mPermissionsManuallyAccepted) {
908                 return USER_ACTION_NOT_NEEDED;
909             }
910             // For pre-pappvoal case, the mPackageName would be null.
911             if (mPackageName != null) {
912                 packageName = mPackageName;
913             } else if (mPreapprovalRequested.get() && mPreapprovalDetails != null) {
914                 packageName = mPreapprovalDetails.getPackageName();
915             } else {
916                 packageName = null;
917             }
918             hasDeviceAdminReceiver = mHasDeviceAdminReceiver;
919         }
920 
921         // For the below cases, force user action prompt
922         // 1. installFlags includes INSTALL_FORCE_PERMISSION_PROMPT
923         // 2. params.requireUserAction is USER_ACTION_REQUIRED
924         final boolean forceUserActionPrompt =
925                 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0
926                         || params.requireUserAction == SessionParams.USER_ACTION_REQUIRED;
927         final int userActionNotTypicallyNeededResponse = forceUserActionPrompt
928                 ? USER_ACTION_REQUIRED
929                 : USER_ACTION_NOT_NEEDED;
930 
931         // It is safe to access mInstallerUid and mInstallSource without lock
932         // because they are immutable after sealing.
933         final Computer snapshot = mPm.snapshotComputer();
934         final boolean isInstallPermissionGranted =
935                 (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
936                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
937         final boolean isSelfUpdatePermissionGranted =
938                 (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
939                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
940         final boolean isUpdatePermissionGranted =
941                 (snapshot.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
942                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
943         final boolean isUpdateWithoutUserActionPermissionGranted = (snapshot.checkUidPermission(
944                 android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid)
945                 == PackageManager.PERMISSION_GRANTED);
946         final boolean isInstallDpcPackagesPermissionGranted = (snapshot.checkUidPermission(
947                 android.Manifest.permission.INSTALL_DPC_PACKAGES, mInstallerUid)
948                 == PackageManager.PERMISSION_GRANTED);
949         final int targetPackageUid = snapshot.getPackageUid(packageName, 0, userId);
950         final boolean isUpdate = targetPackageUid != -1 || isApexSession();
951         final InstallSourceInfo existingInstallSourceInfo = isUpdate
952                 ? snapshot.getInstallSourceInfo(packageName, userId)
953                 : null;
954         final String existingInstallerPackageName = existingInstallSourceInfo != null
955                 ? existingInstallSourceInfo.getInstallingPackageName()
956                 : null;
957         final String existingUpdateOwnerPackageName = existingInstallSourceInfo != null
958                 ? existingInstallSourceInfo.getUpdateOwnerPackageName()
959                 : null;
960         final boolean isInstallerOfRecord = isUpdate
961                 && Objects.equals(existingInstallerPackageName, getInstallerPackageName());
962         final boolean isUpdateOwner = TextUtils.equals(existingUpdateOwnerPackageName,
963                 getInstallerPackageName());
964         final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
965         final boolean isPermissionGranted = isInstallPermissionGranted
966                 || (isUpdatePermissionGranted && isUpdate)
967                 || (isSelfUpdatePermissionGranted && isSelfUpdate)
968                 || (isInstallDpcPackagesPermissionGranted && hasDeviceAdminReceiver);
969         final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
970         final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
971         final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
972         final boolean isFromManagedUserOrProfile =
973                 (params.installFlags & PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE) != 0;
974         final boolean isUpdateOwnershipEnforcementEnabled =
975                 mPm.isUpdateOwnershipEnforcementAvailable()
976                         && existingUpdateOwnerPackageName != null;
977 
978         // Device owners and affiliated profile owners are allowed to silently install packages, so
979         // the permission check is waived if the installer is the device owner.
980         final boolean noUserActionNecessary = isInstallerRoot || isInstallerSystem
981                 || isInstallerDeviceOwnerOrAffiliatedProfileOwner();
982 
983         if (noUserActionNecessary) {
984             return userActionNotTypicallyNeededResponse;
985         }
986 
987         if (isUpdateOwnershipEnforcementEnabled
988                 && !isApexSession()
989                 && !isUpdateOwner
990                 && !isInstallerShell
991                 // We don't enforce the update ownership for the managed user and profile.
992                 && !isFromManagedUserOrProfile) {
993             return USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER;
994         }
995 
996         if (isPermissionGranted) {
997             return userActionNotTypicallyNeededResponse;
998         }
999 
1000         if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid,
1001                 userId)) {
1002             // show the installer to account for device policy or unknown sources use cases
1003             return USER_ACTION_REQUIRED;
1004         }
1005 
1006         if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED
1007                 && isUpdateWithoutUserActionPermissionGranted
1008                 && ((isUpdateOwnershipEnforcementEnabled ? isUpdateOwner
1009                 : isInstallerOfRecord) || isSelfUpdate)) {
1010             return USER_ACTION_PENDING_APK_PARSING;
1011         }
1012 
1013         return USER_ACTION_REQUIRED;
1014     }
1015 
updateUserActionRequirement(int requirement)1016     private void updateUserActionRequirement(int requirement) {
1017         synchronized (mLock) {
1018             mUserActionRequirement = requirement;
1019         }
1020     }
1021 
1022     @SuppressWarnings("GuardedBy" /*mPm.mInstaller is {@code final} field*/)
PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, PackageSessionProvider sessionProvider, SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager, int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, SessionParams params, long createdMillis, long committedMillis, File stageDir, String stageCid, InstallationFile[] files, ArrayMap<String, PerFileChecksum> checksums, boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int sessionErrorCode, String sessionErrorMessage)1023     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
1024             Context context, PackageManagerService pm,
1025             PackageSessionProvider sessionProvider,
1026             SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager,
1027             int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
1028             SessionParams params, long createdMillis, long committedMillis,
1029             File stageDir, String stageCid, InstallationFile[] files,
1030             ArrayMap<String, PerFileChecksum> checksums,
1031             boolean prepared, boolean committed, boolean destroyed, boolean sealed,
1032             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
1033             boolean isFailed, boolean isApplied, int sessionErrorCode,
1034             String sessionErrorMessage) {
1035         mCallback = callback;
1036         mContext = context;
1037         mPm = pm;
1038         mInstaller = (mPm != null) ? mPm.mInstaller : null;
1039         mSessionProvider = sessionProvider;
1040         mSilentUpdatePolicy = silentUpdatePolicy;
1041         mHandler = new Handler(looper, mHandlerCallback);
1042         mStagingManager = stagingManager;
1043 
1044         this.sessionId = sessionId;
1045         this.userId = userId;
1046         mOriginalInstallerUid = installerUid;
1047         mInstallerUid = installerUid;
1048         mInstallSource = Objects.requireNonNull(installSource);
1049         mOriginalInstallerPackageName = mInstallSource.mInstallerPackageName;
1050         this.params = params;
1051         this.createdMillis = createdMillis;
1052         this.updatedMillis = createdMillis;
1053         this.committedMillis = committedMillis;
1054         this.stageDir = stageDir;
1055         this.stageCid = stageCid;
1056         this.mShouldBeSealed = sealed;
1057         if (childSessionIds != null) {
1058             for (int childSessionId : childSessionIds) {
1059                 // Null values will be resolved to actual object references in
1060                 // #onAfterSessionRead later.
1061                 mChildSessions.put(childSessionId, null);
1062             }
1063         }
1064         this.mParentSessionId = parentSessionId;
1065 
1066         if (files != null) {
1067             mFiles.ensureCapacity(files.length);
1068             for (int i = 0, size = files.length; i < size; ++i) {
1069                 InstallationFile file = files[i];
1070                 if (!mFiles.add(new FileEntry(i, file))) {
1071                     throw new IllegalArgumentException(
1072                             "Trying to add a duplicate installation file");
1073                 }
1074             }
1075         }
1076 
1077         if (checksums != null) {
1078             mChecksums.putAll(checksums);
1079         }
1080 
1081         if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
1082             throw new IllegalArgumentException(
1083                     "Exactly one of stageDir or stageCid stage must be set");
1084         }
1085 
1086         mPrepared = prepared;
1087         mCommitted.set(committed);
1088         mDestroyed = destroyed;
1089         mSessionReady = isReady;
1090         mSessionApplied = isApplied;
1091         mSessionFailed = isFailed;
1092         mSessionErrorCode = sessionErrorCode;
1093         mSessionErrorMessage =
1094                 sessionErrorMessage != null ? sessionErrorMessage : "";
1095         mStagedSession = params.isStaged ? new StagedSession() : null;
1096 
1097         if (isDataLoaderInstallation()) {
1098             if (isApexSession()) {
1099                 throw new IllegalArgumentException(
1100                         "DataLoader installation of APEX modules is not allowed.");
1101             }
1102 
1103             if (isSystemDataLoaderInstallation() && mContext.checkCallingOrSelfPermission(
1104                     Manifest.permission.USE_SYSTEM_DATA_LOADERS)
1105                     != PackageManager.PERMISSION_GRANTED) {
1106                 throw new SecurityException("You need the "
1107                         + "com.android.permission.USE_SYSTEM_DATA_LOADERS permission "
1108                         + "to use system data loaders");
1109             }
1110         }
1111 
1112         if (isIncrementalInstallation() && !IncrementalManager.isAllowed()) {
1113             throw new IllegalArgumentException("Incremental installation not allowed.");
1114         }
1115     }
1116 
1117     /**
1118      * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially
1119      * sensitive data scrubbed from its fields.
1120      *
1121      * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
1122      *                   need to be scrubbed
1123      */
shouldScrubData(int callingUid)1124     private boolean shouldScrubData(int callingUid) {
1125         return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid);
1126     }
1127 
1128     /**
1129      * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields
1130      * that may contain sensitive info being filtered.
1131      *
1132      * @param includeIcon true if the icon should be included in the object
1133      * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
1134      *                   need to be scrubbed
1135      * @see #shouldScrubData(int)
1136      */
generateInfoForCaller(boolean includeIcon, int callingUid)1137     public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) {
1138         return generateInfoInternal(includeIcon, shouldScrubData(callingUid));
1139     }
1140 
1141     /**
1142      * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields.
1143      *
1144      * @param includeIcon true if the icon should be included in the object
1145      * @see #generateInfoForCaller(boolean, int)
1146      */
generateInfoScrubbed(boolean includeIcon)1147     public SessionInfo generateInfoScrubbed(boolean includeIcon) {
1148         return generateInfoInternal(includeIcon, true /*scrubData*/);
1149     }
1150 
generateInfoInternal(boolean includeIcon, boolean scrubData)1151     private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) {
1152         final SessionInfo info = new SessionInfo();
1153         final float progress;
1154         synchronized (mProgressLock) {
1155             progress = mProgress;
1156         }
1157         synchronized (mLock) {
1158             info.sessionId = sessionId;
1159             info.userId = userId;
1160             info.installerPackageName = mInstallSource.mInstallerPackageName;
1161             info.installerAttributionTag = mInstallSource.mInstallerAttributionTag;
1162             info.resolvedBaseCodePath = null;
1163             if (mContext.checkCallingOrSelfPermission(
1164                     Manifest.permission.READ_INSTALLED_SESSION_PATHS)
1165                     == PackageManager.PERMISSION_GRANTED) {
1166                 File file = mResolvedBaseFile;
1167                 if (file == null) {
1168                     // Try to guess mResolvedBaseFile file.
1169                     final List<File> addedFiles = getAddedApksLocked();
1170                     if (addedFiles.size() > 0) {
1171                         file = addedFiles.get(0);
1172                     }
1173                 }
1174                 if (file != null) {
1175                     info.resolvedBaseCodePath = file.getAbsolutePath();
1176                 }
1177             }
1178             info.progress = progress;
1179             info.sealed = mSealed;
1180             info.isCommitted = isCommitted();
1181             info.isPreapprovalRequested = isPreapprovalRequested();
1182             info.active = mActiveCount.get() > 0;
1183 
1184             info.mode = params.mode;
1185             info.installReason = params.installReason;
1186             info.installScenario = params.installScenario;
1187             info.sizeBytes = params.sizeBytes;
1188             info.appPackageName = mPreapprovalDetails != null ? mPreapprovalDetails.getPackageName()
1189                     : mPackageName != null ? mPackageName : params.appPackageName;
1190             if (includeIcon) {
1191                 info.appIcon = mPreapprovalDetails != null && mPreapprovalDetails.getIcon() != null
1192                         ? mPreapprovalDetails.getIcon() : params.appIcon;
1193             }
1194             info.appLabel =
1195                     mPreapprovalDetails != null ? mPreapprovalDetails.getLabel() : params.appLabel;
1196 
1197             info.installLocation = params.installLocation;
1198             if (!scrubData) {
1199                 info.originatingUri = params.originatingUri;
1200             }
1201             info.originatingUid = params.originatingUid;
1202             if (!scrubData) {
1203                 info.referrerUri = params.referrerUri;
1204             }
1205             info.grantedRuntimePermissions = params.getLegacyGrantedRuntimePermissions();
1206             info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions;
1207             info.autoRevokePermissionsMode = params.autoRevokePermissionsMode;
1208             info.installFlags = params.installFlags;
1209             info.isMultiPackage = params.isMultiPackage;
1210             info.isStaged = params.isStaged;
1211             info.rollbackDataPolicy = params.rollbackDataPolicy;
1212             info.parentSessionId = mParentSessionId;
1213             info.childSessionIds = getChildSessionIdsLocked();
1214             info.isSessionApplied = mSessionApplied;
1215             info.isSessionReady = mSessionReady;
1216             info.isSessionFailed = mSessionFailed;
1217             info.setSessionErrorCode(mSessionErrorCode, mSessionErrorMessage);
1218             info.createdMillis = createdMillis;
1219             info.updatedMillis = updatedMillis;
1220             info.requireUserAction = params.requireUserAction;
1221             info.installerUid = mInstallerUid;
1222             info.packageSource = params.packageSource;
1223             info.applicationEnabledSettingPersistent = params.applicationEnabledSettingPersistent;
1224             info.pendingUserActionReason = userActionRequirementToReason(mUserActionRequirement);
1225         }
1226         return info;
1227     }
1228 
isPrepared()1229     public boolean isPrepared() {
1230         synchronized (mLock) {
1231             return mPrepared;
1232         }
1233     }
1234 
isSealed()1235     public boolean isSealed() {
1236         synchronized (mLock) {
1237             return mSealed;
1238         }
1239     }
1240 
1241     /** @hide */
isPreapprovalRequested()1242     boolean isPreapprovalRequested() {
1243         return mPreapprovalRequested.get();
1244     }
1245 
1246     /** {@hide} */
isCommitted()1247     boolean isCommitted() {
1248         return mCommitted.get();
1249     }
1250 
1251     /** {@hide} */
isDestroyed()1252     boolean isDestroyed() {
1253         synchronized (mLock) {
1254             return mDestroyed;
1255         }
1256     }
1257 
isInTerminalState()1258     private boolean isInTerminalState() {
1259         synchronized (mLock) {
1260             return mSessionApplied || mSessionFailed;
1261         }
1262     }
1263 
1264     /** Returns true if a staged session has reached a final state and can be forgotten about  */
isStagedAndInTerminalState()1265     public boolean isStagedAndInTerminalState() {
1266         return params.isStaged && isInTerminalState();
1267     }
1268 
assertNotLocked(String cookie)1269     private void assertNotLocked(String cookie) {
1270         if (Thread.holdsLock(mLock)) {
1271             throw new IllegalStateException(cookie + " is holding mLock");
1272         }
1273     }
1274 
assertSealed(String cookie)1275     private void assertSealed(String cookie) {
1276         if (!isSealed()) {
1277             throw new IllegalStateException(cookie + " before sealing");
1278         }
1279     }
1280 
1281     @GuardedBy("mLock")
assertPreparedAndNotPreapprovalRequestedLocked(String cookie)1282     private void assertPreparedAndNotPreapprovalRequestedLocked(String cookie) {
1283         assertPreparedAndNotSealedLocked(cookie);
1284         if (isPreapprovalRequested()) {
1285             throw new IllegalStateException(cookie + " not allowed after requesting");
1286         }
1287     }
1288 
1289     @GuardedBy("mLock")
assertPreparedAndNotSealedLocked(String cookie)1290     private void assertPreparedAndNotSealedLocked(String cookie) {
1291         assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
1292         if (mSealed) {
1293             throw new SecurityException(cookie + " not allowed after sealing");
1294         }
1295     }
1296 
1297     @GuardedBy("mLock")
assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)1298     private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
1299         assertPreparedAndNotDestroyedLocked(cookie);
1300         if (isCommitted()) {
1301             throw new SecurityException(cookie + " not allowed after commit");
1302         }
1303     }
1304 
1305     @GuardedBy("mLock")
assertPreparedAndNotDestroyedLocked(String cookie)1306     private void assertPreparedAndNotDestroyedLocked(String cookie) {
1307         if (!mPrepared) {
1308             throw new IllegalStateException(cookie + " before prepared");
1309         }
1310         if (mDestroyed) {
1311             throw new SecurityException(cookie + " not allowed after destruction");
1312         }
1313     }
1314 
1315     @GuardedBy("mProgressLock")
setClientProgressLocked(float progress)1316     private void setClientProgressLocked(float progress) {
1317         // Always publish first staging movement
1318         final boolean forcePublish = (mClientProgress == 0);
1319         mClientProgress = progress;
1320         computeProgressLocked(forcePublish);
1321     }
1322 
1323     @Override
setClientProgress(float progress)1324     public void setClientProgress(float progress) {
1325         assertCallerIsOwnerOrRoot();
1326         synchronized (mProgressLock) {
1327             setClientProgressLocked(progress);
1328         }
1329     }
1330 
1331     @Override
addClientProgress(float progress)1332     public void addClientProgress(float progress) {
1333         assertCallerIsOwnerOrRoot();
1334         synchronized (mProgressLock) {
1335             setClientProgressLocked(mClientProgress + progress);
1336         }
1337     }
1338 
1339     @GuardedBy("mProgressLock")
computeProgressLocked(boolean forcePublish)1340     private void computeProgressLocked(boolean forcePublish) {
1341         if (!isIncrementalInstallation() || !isCommitted()) {
1342             mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
1343                     + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
1344         } else {
1345             // For incremental, publish regular install progress before the session is committed,
1346             // but publish incremental progress afterwards.
1347             if (mIncrementalProgress - mProgress >= 0.01) {
1348                 // It takes some time for data loader to write to incremental file system, so at the
1349                 // beginning of the commit, the incremental progress might be very small.
1350                 // Wait till the incremental progress is larger than what's already displayed.
1351                 // This way we don't see the progress ring going backwards.
1352                 mProgress = mIncrementalProgress;
1353             }
1354         }
1355 
1356         // Only publish meaningful progress changes.
1357         if (forcePublish || (mProgress - mReportedProgress) >= 0.01) {
1358             mReportedProgress = mProgress;
1359             mCallback.onSessionProgressChanged(this, mProgress);
1360         }
1361     }
1362 
1363     @Override
getNames()1364     public String[] getNames() {
1365         assertCallerIsOwnerRootOrVerifier();
1366         synchronized (mLock) {
1367             assertPreparedAndNotDestroyedLocked("getNames");
1368             String[] names;
1369             if (!isCommitted()) {
1370                 names = getNamesLocked();
1371             } else {
1372                 names = getStageDirContentsLocked();
1373             }
1374             return ArrayUtils.removeString(names, APP_METADATA_FILE_NAME);
1375         }
1376     }
1377 
1378     @GuardedBy("mLock")
getStageDirContentsLocked()1379     private String[] getStageDirContentsLocked() {
1380         if (stageDir == null) {
1381             return EmptyArray.STRING;
1382         }
1383         String[] result = stageDir.list();
1384         if (result == null) {
1385             return EmptyArray.STRING;
1386         }
1387         return result;
1388     }
1389 
1390     @GuardedBy("mLock")
getNamesLocked()1391     private String[] getNamesLocked() {
1392         if (!isDataLoaderInstallation()) {
1393             return getStageDirContentsLocked();
1394         }
1395 
1396         InstallationFile[] files = getInstallationFilesLocked();
1397         String[] result = new String[files.length];
1398         for (int i = 0, size = files.length; i < size; ++i) {
1399             result[i] = files[i].getName();
1400         }
1401         return result;
1402     }
1403 
1404     @GuardedBy("mLock")
getInstallationFilesLocked()1405     private InstallationFile[] getInstallationFilesLocked() {
1406         final InstallationFile[] result = new InstallationFile[mFiles.size()];
1407         for (FileEntry fileEntry : mFiles) {
1408             result[fileEntry.getIndex()] = fileEntry.getFile();
1409         }
1410         return result;
1411     }
1412 
filterFiles(File parent, String[] names, FileFilter filter)1413     private static ArrayList<File> filterFiles(File parent, String[] names, FileFilter filter) {
1414         ArrayList<File> result = new ArrayList<>(names.length);
1415         for (String name : names) {
1416             File file = new File(parent, name);
1417             if (filter.accept(file)) {
1418                 result.add(file);
1419             }
1420         }
1421         return result;
1422     }
1423 
1424     @GuardedBy("mLock")
getAddedApksLocked()1425     private List<File> getAddedApksLocked() {
1426         String[] names = getNamesLocked();
1427         return filterFiles(stageDir, names, sAddedApkFilter);
1428     }
1429 
1430     @GuardedBy("mLock")
getRemovedFilesLocked()1431     private List<File> getRemovedFilesLocked() {
1432         String[] names = getNamesLocked();
1433         return filterFiles(stageDir, names, sRemovedFilter);
1434     }
1435 
1436     @Override
setChecksums(String name, @NonNull Checksum[] checksums, @Nullable byte[] signature)1437     public void setChecksums(String name, @NonNull Checksum[] checksums,
1438             @Nullable byte[] signature) {
1439         if (checksums.length == 0) {
1440             return;
1441         }
1442 
1443         final String initiatingPackageName = getInstallSource().mInitiatingPackageName;
1444         final String installerPackageName;
1445         if (!isInstalledByAdb(initiatingPackageName)) {
1446             installerPackageName = initiatingPackageName;
1447         } else {
1448             installerPackageName = getInstallSource().mInstallerPackageName;
1449         }
1450         if (TextUtils.isEmpty(installerPackageName)) {
1451             throw new IllegalStateException("Installer package is empty.");
1452         }
1453 
1454         final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
1455         appOps.checkPackage(Binder.getCallingUid(), installerPackageName);
1456 
1457         final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
1458         final AndroidPackage callingInstaller = pmi.getPackage(installerPackageName);
1459         if (callingInstaller == null) {
1460             throw new IllegalStateException("Can't obtain calling installer's package.");
1461         }
1462 
1463         if (signature != null && signature.length != 0) {
1464             try {
1465                 Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature);
1466             } catch (IOException | NoSuchAlgorithmException | SignatureException e) {
1467                 throw new IllegalArgumentException("Can't verify signature", e);
1468             }
1469         }
1470 
1471         assertCallerIsOwnerOrRoot();
1472         synchronized (mLock) {
1473             assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums");
1474 
1475             if (mChecksums.containsKey(name)) {
1476                 throw new IllegalStateException("Duplicate checksums.");
1477             }
1478 
1479             mChecksums.put(name, new PerFileChecksum(checksums, signature));
1480         }
1481     }
1482 
1483     @Override
requestChecksums(@onNull String name, @Checksum.TypeMask int optional, @Checksum.TypeMask int required, @Nullable List trustedInstallers, @NonNull IOnChecksumsReadyListener onChecksumsReadyListener)1484     public void requestChecksums(@NonNull String name, @Checksum.TypeMask int optional,
1485             @Checksum.TypeMask int required, @Nullable List trustedInstallers,
1486             @NonNull IOnChecksumsReadyListener onChecksumsReadyListener) {
1487         assertCallerIsOwnerRootOrVerifier();
1488         final File file = new File(stageDir, name);
1489         final String installerPackageName = PackageManagerServiceUtils.isInstalledByAdb(
1490                 getInstallSource().mInitiatingPackageName)
1491                 ? getInstallSource().mInstallerPackageName
1492                 : getInstallSource().mInitiatingPackageName;
1493         try {
1494             mPm.requestFileChecksums(file, installerPackageName, optional, required,
1495                     trustedInstallers, onChecksumsReadyListener);
1496         } catch (FileNotFoundException e) {
1497             throw new ParcelableException(e);
1498         }
1499     }
1500 
1501     @Override
removeSplit(String splitName)1502     public void removeSplit(String splitName) {
1503         if (isDataLoaderInstallation()) {
1504             throw new IllegalStateException(
1505                     "Cannot remove splits in a data loader installation session.");
1506         }
1507         if (TextUtils.isEmpty(params.appPackageName)) {
1508             throw new IllegalStateException("Must specify package name to remove a split");
1509         }
1510 
1511         assertCallerIsOwnerOrRoot();
1512         synchronized (mLock) {
1513             assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
1514 
1515             try {
1516                 createRemoveSplitMarkerLocked(splitName);
1517             } catch (IOException e) {
1518                 throw ExceptionUtils.wrap(e);
1519             }
1520         }
1521     }
1522 
getRemoveMarkerName(String name)1523     private static String getRemoveMarkerName(String name) {
1524         final String markerName = name + REMOVE_MARKER_EXTENSION;
1525         if (!FileUtils.isValidExtFilename(markerName)) {
1526             throw new IllegalArgumentException("Invalid marker: " + markerName);
1527         }
1528         return markerName;
1529     }
1530 
1531     @GuardedBy("mLock")
createRemoveSplitMarkerLocked(String splitName)1532     private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
1533         try {
1534             final File target = new File(stageDir, getRemoveMarkerName(splitName));
1535             target.createNewFile();
1536             Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
1537         } catch (ErrnoException e) {
1538             throw e.rethrowAsIOException();
1539         }
1540     }
1541 
assertShellOrSystemCalling(String operation)1542     private void assertShellOrSystemCalling(String operation) {
1543         switch (Binder.getCallingUid()) {
1544             case android.os.Process.SHELL_UID:
1545             case android.os.Process.ROOT_UID:
1546             case android.os.Process.SYSTEM_UID:
1547                 break;
1548             default:
1549                 throw new SecurityException(operation + " only supported from shell or system");
1550         }
1551     }
1552 
assertCanWrite(boolean reverseMode)1553     private void assertCanWrite(boolean reverseMode) {
1554         if (isDataLoaderInstallation()) {
1555             throw new IllegalStateException(
1556                     "Cannot write regular files in a data loader installation session.");
1557         }
1558         assertCallerIsOwnerOrRoot();
1559         synchronized (mLock) {
1560             assertPreparedAndNotSealedLocked("assertCanWrite");
1561         }
1562         if (reverseMode) {
1563             assertShellOrSystemCalling("Reverse mode");
1564         }
1565     }
1566 
getTmpAppMetadataFile()1567     private File getTmpAppMetadataFile() {
1568         return new File(Environment.getDataAppDirectory(params.volumeUuid),
1569                 sessionId + "-" + APP_METADATA_FILE_NAME);
1570     }
1571 
getStagedAppMetadataFile()1572     private File getStagedAppMetadataFile() {
1573         File file = new File(stageDir, APP_METADATA_FILE_NAME);
1574         return file.exists() ? file : null;
1575     }
1576 
isAppMetadata(String name)1577     private static boolean isAppMetadata(String name) {
1578         return name.endsWith(APP_METADATA_FILE_NAME);
1579     }
1580 
isAppMetadata(File file)1581     private static boolean isAppMetadata(File file) {
1582         return isAppMetadata(file.getName());
1583     }
1584 
1585     @Override
getAppMetadataFd()1586     public ParcelFileDescriptor getAppMetadataFd() {
1587         assertCallerIsOwnerOrRoot();
1588         synchronized (mLock) {
1589             assertPreparedAndNotCommittedOrDestroyedLocked("getAppMetadataFd");
1590             if (getStagedAppMetadataFile() == null) {
1591                 return null;
1592             }
1593             try {
1594                 return openReadInternalLocked(APP_METADATA_FILE_NAME);
1595             } catch (IOException e) {
1596                 throw ExceptionUtils.wrap(e);
1597             }
1598         }
1599     }
1600 
1601     @Override
removeAppMetadata()1602     public void removeAppMetadata() {
1603         File file = getStagedAppMetadataFile();
1604         if (file != null) {
1605             file.delete();
1606         }
1607     }
1608 
getAppMetadataSizeLimit()1609     private static long getAppMetadataSizeLimit() {
1610         final long token = Binder.clearCallingIdentity();
1611         try {
1612             return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
1613                     PROPERTY_APP_METADATA_BYTE_SIZE_LIMIT, DEFAULT_APP_METADATA_BYTE_SIZE_LIMIT);
1614         } finally {
1615             Binder.restoreCallingIdentity(token);
1616         }
1617     }
1618 
1619     @Override
openWriteAppMetadata()1620     public ParcelFileDescriptor openWriteAppMetadata() {
1621         assertCallerIsOwnerOrRoot();
1622         synchronized (mLock) {
1623             assertPreparedAndNotSealedLocked("openWriteAppMetadata");
1624         }
1625         try {
1626             return doWriteInternal(APP_METADATA_FILE_NAME, /* offsetBytes= */ 0,
1627                     /* lengthBytes= */ -1, null);
1628         } catch (IOException e) {
1629             throw ExceptionUtils.wrap(e);
1630         }
1631     }
1632 
1633     @Override
openWrite(String name, long offsetBytes, long lengthBytes)1634     public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
1635         assertCanWrite(false);
1636         try {
1637             return doWriteInternal(name, offsetBytes, lengthBytes, null);
1638         } catch (IOException e) {
1639             throw ExceptionUtils.wrap(e);
1640         }
1641     }
1642 
1643     @Override
write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)1644     public void write(String name, long offsetBytes, long lengthBytes,
1645             ParcelFileDescriptor fd) {
1646         assertCanWrite(fd != null);
1647         try {
1648             doWriteInternal(name, offsetBytes, lengthBytes, fd);
1649         } catch (IOException e) {
1650             throw ExceptionUtils.wrap(e);
1651         }
1652     }
1653 
1654     @Override
stageViaHardLink(String path)1655     public void stageViaHardLink(String path) {
1656         final int callingUid = Binder.getCallingUid();
1657         if (callingUid != Process.SYSTEM_UID) {
1658             throw new SecurityException("link() can only be run by the system");
1659         }
1660 
1661         final File target = new File(path);
1662         final File source = new File(stageDir, target.getName());
1663         var sourcePath = source.getAbsolutePath();
1664         try {
1665             try {
1666                 Os.link(path, sourcePath);
1667                 // Grant READ access for APK to be read successfully
1668                 Os.chmod(sourcePath, 0644);
1669             } catch (ErrnoException e) {
1670                 e.rethrowAsIOException();
1671             }
1672             if (!SELinux.restorecon(source)) {
1673                 throw new IOException("Can't relabel file: " + source);
1674             }
1675         } catch (IOException e) {
1676             try {
1677                 Os.unlink(sourcePath);
1678             } catch (Exception ignored) {
1679                 Slog.d(TAG, "Failed to unlink session file: " + sourcePath);
1680             }
1681 
1682             throw ExceptionUtils.wrap(e);
1683         }
1684     }
1685 
openTargetInternal(String path, int flags, int mode)1686     private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode)
1687             throws IOException, ErrnoException {
1688         // TODO: this should delegate to DCS so the system process avoids
1689         // holding open FDs into containers.
1690         final FileDescriptor fd = Os.open(path, flags, mode);
1691         return new ParcelFileDescriptor(fd);
1692     }
1693 
createRevocableFdInternal(RevocableFileDescriptor fd, ParcelFileDescriptor pfd)1694     private ParcelFileDescriptor createRevocableFdInternal(RevocableFileDescriptor fd,
1695             ParcelFileDescriptor pfd) throws IOException {
1696         int releasedFdInt = pfd.detachFd();
1697         FileDescriptor releasedFd = new FileDescriptor();
1698         releasedFd.setInt$(releasedFdInt);
1699         fd.init(mContext, releasedFd);
1700         return fd.getRevocableFileDescriptor();
1701     }
1702 
doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)1703     private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
1704             ParcelFileDescriptor incomingFd) throws IOException {
1705         // Quick validity check of state, and allocate a pipe for ourselves. We
1706         // then do heavy disk allocation outside the lock, but this open pipe
1707         // will block any attempted install transitions.
1708         final RevocableFileDescriptor fd;
1709         final FileBridge bridge;
1710         synchronized (mLock) {
1711             if (PackageInstaller.ENABLE_REVOCABLE_FD) {
1712                 fd = new RevocableFileDescriptor();
1713                 bridge = null;
1714                 mFds.add(fd);
1715             } else {
1716                 fd = null;
1717                 bridge = new FileBridge();
1718                 mBridges.add(bridge);
1719             }
1720         }
1721 
1722         try {
1723             // Use installer provided name for now; we always rename later
1724             if (!FileUtils.isValidExtFilename(name)) {
1725                 throw new IllegalArgumentException("Invalid name: " + name);
1726             }
1727             final File target;
1728             final long identity = Binder.clearCallingIdentity();
1729             try {
1730                 target = new File(stageDir, name);
1731             } finally {
1732                 Binder.restoreCallingIdentity(identity);
1733             }
1734 
1735             // If file is app metadata then set permission to 0640 to deny user read access since it
1736             // might contain sensitive information.
1737             int mode = name.equals(APP_METADATA_FILE_NAME) ? APP_METADATA_FILE_ACCESS_MODE : 0644;
1738             ParcelFileDescriptor targetPfd = openTargetInternal(target.getAbsolutePath(),
1739                     O_CREAT | O_WRONLY, mode);
1740             Os.chmod(target.getAbsolutePath(), mode);
1741 
1742             // If caller specified a total length, allocate it for them. Free up
1743             // cache space to grow, if needed.
1744             if (stageDir != null && lengthBytes > 0) {
1745                 mContext.getSystemService(StorageManager.class).allocateBytes(
1746                         targetPfd.getFileDescriptor(), lengthBytes,
1747                         InstallLocationUtils.translateAllocateFlags(params.installFlags));
1748             }
1749 
1750             if (offsetBytes > 0) {
1751                 Os.lseek(targetPfd.getFileDescriptor(), offsetBytes, OsConstants.SEEK_SET);
1752             }
1753 
1754             if (incomingFd != null) {
1755                 // In "reverse" mode, we're streaming data ourselves from the
1756                 // incoming FD, which means we never have to hand out our
1757                 // sensitive internal FD. We still rely on a "bridge" being
1758                 // inserted above to hold the session active.
1759                 try {
1760                     final Int64Ref last = new Int64Ref(0);
1761                     FileUtils.copy(incomingFd.getFileDescriptor(), targetPfd.getFileDescriptor(),
1762                             lengthBytes, null, Runnable::run,
1763                             (long progress) -> {
1764                                 if (params.sizeBytes > 0) {
1765                                     final long delta = progress - last.value;
1766                                     last.value = progress;
1767                                     synchronized (mProgressLock) {
1768                                         setClientProgressLocked(mClientProgress
1769                                                 + (float) delta / (float) params.sizeBytes);
1770                                     }
1771                                 }
1772                             });
1773                 } finally {
1774                     IoUtils.closeQuietly(targetPfd);
1775                     IoUtils.closeQuietly(incomingFd);
1776 
1777                     // We're done here, so remove the "bridge" that was holding
1778                     // the session active.
1779                     synchronized (mLock) {
1780                         if (PackageInstaller.ENABLE_REVOCABLE_FD) {
1781                             mFds.remove(fd);
1782                         } else {
1783                             bridge.forceClose();
1784                             mBridges.remove(bridge);
1785                         }
1786                     }
1787                 }
1788                 return null;
1789             } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
1790                 return createRevocableFdInternal(fd, targetPfd);
1791             } else {
1792                 bridge.setTargetFile(targetPfd);
1793                 bridge.start();
1794                 return bridge.getClientSocket();
1795             }
1796 
1797         } catch (ErrnoException e) {
1798             throw e.rethrowAsIOException();
1799         }
1800     }
1801 
1802     @Override
openRead(String name)1803     public ParcelFileDescriptor openRead(String name) {
1804         if (isDataLoaderInstallation()) {
1805             throw new IllegalStateException(
1806                     "Cannot read regular files in a data loader installation session.");
1807         }
1808         assertCallerIsOwnerOrRoot();
1809         synchronized (mLock) {
1810             assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
1811             try {
1812                 return openReadInternalLocked(name);
1813             } catch (IOException e) {
1814                 throw ExceptionUtils.wrap(e);
1815             }
1816         }
1817     }
1818 
1819     @GuardedBy("mLock")
openReadInternalLocked(String name)1820     private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
1821         try {
1822             if (!FileUtils.isValidExtFilename(name)) {
1823                 throw new IllegalArgumentException("Invalid name: " + name);
1824             }
1825             final File target = new File(stageDir, name);
1826             final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);
1827             return new ParcelFileDescriptor(targetFd);
1828         } catch (ErrnoException e) {
1829             throw e.rethrowAsIOException();
1830         }
1831     }
1832 
1833     /**
1834      * Check if the caller is the owner of this session or a verifier.
1835      * Otherwise throw a {@link SecurityException}.
1836      */
assertCallerIsOwnerRootOrVerifier()1837     private void assertCallerIsOwnerRootOrVerifier() {
1838         final int callingUid = Binder.getCallingUid();
1839         if (callingUid == Process.ROOT_UID || callingUid == mInstallerUid) {
1840             return;
1841         }
1842         if (isSealed() && mContext.checkCallingOrSelfPermission(
1843                 android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)
1844                 == PackageManager.PERMISSION_GRANTED) {
1845             return;
1846         }
1847         throw new SecurityException("Session does not belong to uid " + callingUid);
1848     }
1849 
1850     /**
1851      * Check if the caller is the owner of this session. Otherwise throw a
1852      * {@link SecurityException}.
1853      */
assertCallerIsOwnerOrRoot()1854     private void assertCallerIsOwnerOrRoot() {
1855         final int callingUid = Binder.getCallingUid();
1856         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
1857             throw new SecurityException("Session does not belong to uid " + callingUid);
1858         }
1859     }
1860 
1861     /**
1862      * Check if the caller is the owner of this session. Otherwise throw a
1863      * {@link SecurityException}.
1864      */
assertCallerIsOwnerOrRootOrSystem()1865     private void assertCallerIsOwnerOrRootOrSystem() {
1866         final int callingUid = Binder.getCallingUid();
1867         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid
1868                 && callingUid != Process.SYSTEM_UID) {
1869             throw new SecurityException("Session does not belong to uid " + callingUid);
1870         }
1871     }
1872 
1873     /**
1874      * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
1875      */
1876     @GuardedBy("mLock")
assertNoWriteFileTransfersOpenLocked()1877     private void assertNoWriteFileTransfersOpenLocked() {
1878         // Verify that all writers are hands-off
1879         for (RevocableFileDescriptor fd : mFds) {
1880             if (!fd.isRevoked()) {
1881                 throw new SecurityException("Files still open");
1882             }
1883         }
1884         for (FileBridge bridge : mBridges) {
1885             if (!bridge.isClosed()) {
1886                 throw new SecurityException("Files still open");
1887             }
1888         }
1889     }
1890 
1891     @Override
commit(@onNull IntentSender statusReceiver, boolean forTransfer)1892     public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
1893         assertNotChild("commit");
1894 
1895         if (!markAsSealed(statusReceiver, forTransfer)) {
1896             return;
1897         }
1898         if (isMultiPackage()) {
1899             synchronized (mLock) {
1900                 boolean sealFailed = false;
1901                 for (int i = mChildSessions.size() - 1; i >= 0; --i) {
1902                     // seal all children, regardless if any of them fail; we'll throw/return
1903                     // as appropriate once all children have been processed
1904                     if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) {
1905                         sealFailed = true;
1906                     }
1907                 }
1908                 if (sealFailed) {
1909                     return;
1910                 }
1911             }
1912         }
1913 
1914         File appMetadataFile = getStagedAppMetadataFile();
1915         if (appMetadataFile != null) {
1916             long sizeLimit = getAppMetadataSizeLimit();
1917             if (appMetadataFile.length() > sizeLimit) {
1918                 appMetadataFile.delete();
1919                 throw new IllegalArgumentException(
1920                         "App metadata size exceeds the maximum allowed limit of " + sizeLimit);
1921             }
1922             if (isIncrementalInstallation()) {
1923                 // Incremental requires stageDir to be empty so move the app metadata file to a
1924                 // temporary location and move back after commit.
1925                 appMetadataFile.renameTo(getTmpAppMetadataFile());
1926             }
1927         }
1928 
1929         dispatchSessionSealed();
1930     }
1931 
1932     @Override
seal()1933     public void seal() {
1934         assertNotChild("seal");
1935         assertCallerIsOwnerOrRoot();
1936         try {
1937             sealInternal();
1938             for (var child : getChildSessions()) {
1939                 child.sealInternal();
1940             }
1941         } catch (PackageManagerException e) {
1942             throw new IllegalStateException("Package is not valid", e);
1943         }
1944     }
1945 
sealInternal()1946     private void sealInternal() throws PackageManagerException {
1947         synchronized (mLock) {
1948             sealLocked();
1949         }
1950     }
1951 
1952     @Override
fetchPackageNames()1953     public List<String> fetchPackageNames() {
1954         assertNotChild("fetchPackageNames");
1955         assertCallerIsOwnerOrRoot();
1956         var sessions = getSelfOrChildSessions();
1957         var result = new ArrayList<String>(sessions.size());
1958         for (var s : sessions) {
1959             result.add(s.fetchPackageName());
1960         }
1961         return result;
1962     }
1963 
fetchPackageName()1964     private String fetchPackageName() {
1965         assertSealed("fetchPackageName");
1966         synchronized (mLock) {
1967             final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
1968             final List<File> addedFiles = getAddedApksLocked();
1969             for (File addedFile : addedFiles) {
1970                 final ParseResult<ApkLite> result =
1971                         ApkLiteParseUtils.parseApkLite(input.reset(), addedFile, 0);
1972                 if (result.isError()) {
1973                     throw new IllegalStateException(
1974                             "Can't parse package for session=" + sessionId, result.getException());
1975                 }
1976                 final ApkLite apk = result.getResult();
1977                 var packageName = apk.getPackageName();
1978                 if (packageName != null) {
1979                     return packageName;
1980                 }
1981             }
1982             throw new IllegalStateException("Can't fetch package name for session=" + sessionId);
1983         }
1984     }
1985 
1986     /**
1987      * Kicks off the install flow. The first step is to persist 'sealed' flags
1988      * to prevent mutations of hard links created later.
1989      */
dispatchSessionSealed()1990     private void dispatchSessionSealed() {
1991         mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
1992     }
1993 
handleSessionSealed()1994     private void handleSessionSealed() {
1995         assertSealed("dispatchSessionSealed");
1996         // Persist the fact that we've sealed ourselves to prevent
1997         // mutations of any hard links we create.
1998         mCallback.onSessionSealedBlocking(this);
1999         dispatchStreamValidateAndCommit();
2000     }
2001 
dispatchStreamValidateAndCommit()2002     private void dispatchStreamValidateAndCommit() {
2003         mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
2004     }
2005 
2006     @WorkerThread
handleStreamValidateAndCommit()2007     private void handleStreamValidateAndCommit() {
2008         try {
2009             // This will track whether the session and any children were validated and are ready to
2010             // progress to the next phase of install
2011             boolean allSessionsReady = true;
2012             for (PackageInstallerSession child : getChildSessions()) {
2013                 allSessionsReady &= child.streamValidateAndCommit();
2014             }
2015             if (allSessionsReady && streamValidateAndCommit()) {
2016                 mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
2017             }
2018         } catch (PackageManagerException e) {
2019             destroy();
2020             String msg = ExceptionUtils.getCompleteMessage(e);
2021             dispatchSessionFinished(e.error, msg, null);
2022             maybeFinishChildSessions(e.error, msg);
2023         }
2024     }
2025 
2026     @WorkerThread
handlePreapprovalRequest()2027     private void handlePreapprovalRequest() {
2028         /**
2029          * Stops the process if the session needs user action. When the user answers the yes,
2030          * {@link #setPermissionsResult(boolean)} is called and then
2031          * {@link #MSG_PRE_APPROVAL_REQUEST} is handled to come back here to check again.
2032          */
2033         if (sendPendingUserActionIntentIfNeeded()) {
2034             return;
2035         }
2036 
2037         dispatchSessionPreappoved();
2038     }
2039 
2040     private final class FileSystemConnector extends
2041             IPackageInstallerSessionFileSystemConnector.Stub {
2042         final Set<String> mAddedFiles = new ArraySet<>();
2043 
FileSystemConnector(List<InstallationFileParcel> addedFiles)2044         FileSystemConnector(List<InstallationFileParcel> addedFiles) {
2045             for (InstallationFileParcel file : addedFiles) {
2046                 mAddedFiles.add(file.name);
2047             }
2048         }
2049 
2050         @Override
writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)2051         public void writeData(String name, long offsetBytes, long lengthBytes,
2052                 ParcelFileDescriptor incomingFd) {
2053             if (incomingFd == null) {
2054                 throw new IllegalArgumentException("incomingFd can't be null");
2055             }
2056             if (!mAddedFiles.contains(name)) {
2057                 throw new SecurityException("File name is not in the list of added files.");
2058             }
2059             try {
2060                 doWriteInternal(name, offsetBytes, lengthBytes, incomingFd);
2061             } catch (IOException e) {
2062                 throw ExceptionUtils.wrap(e);
2063             }
2064         }
2065     }
2066 
2067     /**
2068      * Returns whether or not a package can be installed while Secure FRP is enabled.
2069      * <p>
2070      * Only callers with the INSTALL_PACKAGES permission are allowed to install. However,
2071      * prevent the package installer from installing anything because, while it has the
2072      * permission, it will allows packages to be installed from anywhere.
2073      */
isSecureFrpInstallAllowed(Context context, int callingUid)2074     private static boolean isSecureFrpInstallAllowed(Context context, int callingUid) {
2075         final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
2076         final String[] systemInstaller = pmi.getKnownPackageNames(
2077                 KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM);
2078         final AndroidPackage callingInstaller = pmi.getPackage(callingUid);
2079         if (callingInstaller != null
2080                 && ArrayUtils.contains(systemInstaller, callingInstaller.getPackageName())) {
2081             // don't allow the system package installer to install while under secure FRP
2082             return false;
2083         }
2084 
2085         // require caller to hold the INSTALL_PACKAGES permission
2086         return context.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
2087                 == PackageManager.PERMISSION_GRANTED;
2088     }
2089 
2090     /**
2091      * Checks if the package can be installed on IncFs.
2092      */
isIncrementalInstallationAllowed(String packageName)2093     private static boolean isIncrementalInstallationAllowed(String packageName) {
2094         final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
2095         final PackageStateInternal existingPkgSetting = pmi.getPackageStateInternal(packageName);
2096         if (existingPkgSetting == null || existingPkgSetting.getPkg() == null) {
2097             return true;
2098         }
2099 
2100         return !existingPkgSetting.isSystem()
2101                 && !existingPkgSetting.isUpdatedSystemApp();
2102     }
2103 
2104     /**
2105      * If this was not already called, the session will be sealed.
2106      *
2107      * This method may be called multiple times to update the status receiver validate caller
2108      * permissions.
2109      */
markAsSealed(@ullable IntentSender statusReceiver, boolean forTransfer)2110     private boolean markAsSealed(@Nullable IntentSender statusReceiver, boolean forTransfer) {
2111         Preconditions.checkState(statusReceiver != null || hasParentSessionId(),
2112                 "statusReceiver can't be null for the root session");
2113         assertCallerIsOwnerOrRoot();
2114 
2115         synchronized (mLock) {
2116             assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
2117             assertNoWriteFileTransfersOpenLocked();
2118 
2119             final boolean isSecureFrpEnabled =
2120                     Global.getInt(mContext.getContentResolver(), Global.SECURE_FRP_MODE, 0) == 1;
2121 
2122             if (isSecureFrpEnabled
2123                     && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
2124                 throw new SecurityException("Can't install packages while in secure FRP");
2125             }
2126 
2127             if (forTransfer) {
2128                 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
2129                 if (mInstallerUid == mOriginalInstallerUid) {
2130                     throw new IllegalArgumentException("Session has not been transferred");
2131                 }
2132             } else {
2133                 if (mInstallerUid != mOriginalInstallerUid) {
2134                     throw new IllegalArgumentException("Session has been transferred");
2135                 }
2136             }
2137 
2138             setRemoteStatusReceiver(statusReceiver);
2139 
2140             // After updating the observer, we can skip re-sealing.
2141             if (mSealed) {
2142                 return true;
2143             }
2144 
2145             try {
2146                 sealLocked();
2147             } catch (PackageManagerException e) {
2148                 return false;
2149             }
2150         }
2151 
2152         return true;
2153     }
2154 
2155     /**
2156      * Returns true if the session is successfully validated and committed. Returns false if the
2157      * dataloader could not be prepared. This can be called multiple times so long as no
2158      * exception is thrown.
2159      * @throws PackageManagerException on an unrecoverable error.
2160      */
2161     @WorkerThread
streamValidateAndCommit()2162     private boolean streamValidateAndCommit() throws PackageManagerException {
2163         // TODO(patb): since the work done here for a parent session in a multi-package install is
2164         //             mostly superficial, consider splitting this method for the parent and
2165         //             single / child sessions.
2166         try {
2167             synchronized (mLock) {
2168                 if (isCommitted()) {
2169                     return true;
2170                 }
2171                 // Read transfers from the original owner stay open, but as the session's data
2172                 // cannot be modified anymore, there is no leak of information. For staged sessions,
2173                 // further validation is performed by the staging manager.
2174                 if (!params.isMultiPackage) {
2175                     if (!prepareDataLoaderLocked()) {
2176                         return false;
2177                     }
2178 
2179                     if (isApexSession()) {
2180                         validateApexInstallLocked();
2181                     } else {
2182                         validateApkInstallLocked();
2183                     }
2184                 }
2185                 if (mDestroyed) {
2186                     throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2187                             "Session destroyed");
2188                 }
2189                 if (!isIncrementalInstallation()) {
2190                     synchronized (mProgressLock) {
2191                         // For non-incremental installs, client staging is fully done at this point
2192                         mClientProgress = 1f;
2193                         computeProgressLocked(true);
2194                     }
2195                 }
2196 
2197                 // This ongoing commit should keep session active, even though client
2198                 // will probably close their end.
2199                 mActiveCount.incrementAndGet();
2200 
2201                 if (!mCommitted.compareAndSet(false /*expect*/, true /*update*/)) {
2202                     throw new PackageManagerException(
2203                             INSTALL_FAILED_INTERNAL_ERROR,
2204                             TextUtils.formatSimple(
2205                                     "The mCommitted of session %d should be false originally",
2206                                     sessionId));
2207                 }
2208                 committedMillis = System.currentTimeMillis();
2209             }
2210             return true;
2211         } catch (PackageManagerException e) {
2212             throw e;
2213         } catch (Throwable e) {
2214             // Convert all exceptions into package manager exceptions as only those are handled
2215             // in the code above.
2216             throw new PackageManagerException(e);
2217         }
2218     }
2219 
2220     @GuardedBy("mLock")
getChildSessionsLocked()2221     private @NonNull List<PackageInstallerSession> getChildSessionsLocked() {
2222         List<PackageInstallerSession> childSessions = Collections.EMPTY_LIST;
2223         if (isMultiPackage()) {
2224             int size = mChildSessions.size();
2225             childSessions = new ArrayList<>(size);
2226             for (int i = 0; i < size; ++i) {
2227                 childSessions.add(mChildSessions.valueAt(i));
2228             }
2229         }
2230         return childSessions;
2231     }
2232 
getChildSessions()2233     @NonNull List<PackageInstallerSession> getChildSessions() {
2234         synchronized (mLock) {
2235             return getChildSessionsLocked();
2236         }
2237     }
2238 
2239     @NonNull
getSelfOrChildSessions()2240     private List<PackageInstallerSession> getSelfOrChildSessions() {
2241         return isMultiPackage() ? getChildSessions() : Collections.singletonList(this);
2242     }
2243 
2244     /**
2245      * Seal the session to prevent further modification.
2246      *
2247      * <p>The session will be sealed after calling this method even if it failed.
2248      *
2249      * @throws PackageManagerException if the session was sealed but something went wrong. If the
2250      *                                 session was sealed this is the only possible exception.
2251      */
2252     @GuardedBy("mLock")
sealLocked()2253     private void sealLocked()
2254             throws PackageManagerException {
2255         try {
2256             assertNoWriteFileTransfersOpenLocked();
2257             assertPreparedAndNotDestroyedLocked("sealing of session " + sessionId);
2258             mSealed = true;
2259         } catch (Throwable e) {
2260             // Convert all exceptions into package manager exceptions as only those are handled
2261             // in the code above.
2262             throw onSessionValidationFailure(new PackageManagerException(e));
2263         }
2264     }
2265 
onSessionValidationFailure(PackageManagerException e)2266     private PackageManagerException onSessionValidationFailure(PackageManagerException e) {
2267         onSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
2268         return e;
2269     }
2270 
onSessionValidationFailure(int error, String detailMessage)2271     private void onSessionValidationFailure(int error, String detailMessage) {
2272         // Session is sealed but could not be validated, we need to destroy it.
2273         destroyInternal();
2274         // Dispatch message to remove session from PackageInstallerService.
2275         dispatchSessionFinished(error, detailMessage, null);
2276     }
2277 
onSessionVerificationFailure(int error, String msg)2278     private void onSessionVerificationFailure(int error, String msg) {
2279         Slog.e(TAG, "Failed to verify session " + sessionId);
2280         // Dispatch message to remove session from PackageInstallerService.
2281         dispatchSessionFinished(error, msg, null);
2282         maybeFinishChildSessions(error, msg);
2283     }
2284 
onSystemDataLoaderUnrecoverable()2285     private void onSystemDataLoaderUnrecoverable() {
2286         final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
2287         final String packageName = getPackageName();
2288         if (TextUtils.isEmpty(packageName)) {
2289             // The package has not been installed.
2290             return;
2291         }
2292         mHandler.post(() -> {
2293             if (deletePackageHelper.deletePackageX(packageName,
2294                     PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
2295                     PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/)
2296                     != PackageManager.DELETE_SUCCEEDED) {
2297                 Slog.e(TAG, "Failed to uninstall package with failed dataloader: " + packageName);
2298             }
2299         });
2300     }
2301 
2302     /**
2303      * If session should be sealed, then it's sealed to prevent further modification.
2304      * If the session can't be sealed then it's destroyed.
2305      *
2306      * Additionally for staged APEX/APK sessions read+validate the package and populate req'd
2307      * fields.
2308      *
2309      * <p> This is meant to be called after all of the sessions are loaded and added to
2310      * PackageInstallerService
2311      *
2312      * @param allSessions All sessions loaded by PackageInstallerService, guaranteed to be
2313      *                    immutable by the caller during the method call. Used to resolve child
2314      *                    sessions Ids to actual object reference.
2315      */
2316     @AnyThread
onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions)2317     void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
2318         synchronized (mLock) {
2319             // Resolve null values to actual object references
2320             for (int i = mChildSessions.size() - 1; i >= 0; --i) {
2321                 int childSessionId = mChildSessions.keyAt(i);
2322                 PackageInstallerSession childSession = allSessions.get(childSessionId);
2323                 if (childSession != null) {
2324                     mChildSessions.setValueAt(i, childSession);
2325                 } else {
2326                     Slog.e(TAG, "Child session not existed: " + childSessionId);
2327                     mChildSessions.removeAt(i);
2328                 }
2329             }
2330 
2331             if (!mShouldBeSealed || isStagedAndInTerminalState()) {
2332                 return;
2333             }
2334             try {
2335                 sealLocked();
2336 
2337                 // Session that are staged, committed and not multi package will be installed or
2338                 // restart verification during this boot. As such, we need populate all the fields
2339                 // for successful installation.
2340                 if (isMultiPackage() || !isStaged() || !isCommitted()) {
2341                     return;
2342                 }
2343                 final PackageInstallerSession root = hasParentSessionId()
2344                         ? allSessions.get(getParentSessionId())
2345                         : this;
2346                 if (root != null && !root.isStagedAndInTerminalState()) {
2347                     if (isApexSession()) {
2348                         validateApexInstallLocked();
2349                     } else {
2350                         validateApkInstallLocked();
2351                     }
2352                 }
2353             } catch (PackageManagerException e) {
2354                 Slog.e(TAG, "Package not valid", e);
2355             }
2356         }
2357     }
2358 
2359     /** Update the timestamp of when the staged session last changed state */
markUpdated()2360     public void markUpdated() {
2361         synchronized (mLock) {
2362             this.updatedMillis = System.currentTimeMillis();
2363         }
2364     }
2365 
2366     @Override
transfer(String packageName)2367     public void transfer(String packageName) {
2368         Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
2369         final Computer snapshot = mPm.snapshotComputer();
2370         ApplicationInfo newOwnerAppInfo = snapshot.getApplicationInfo(packageName, 0, userId);
2371         if (newOwnerAppInfo == null) {
2372             throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
2373         }
2374 
2375         if (PackageManager.PERMISSION_GRANTED != snapshot.checkUidPermission(
2376                 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
2377             throw new SecurityException("Destination package " + packageName + " does not have "
2378                     + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
2379         }
2380 
2381         // Only install flags that can be verified by the app the session is transferred to are
2382         // allowed. The parameters can be read via PackageInstaller.SessionInfo.
2383         if (!params.areHiddenOptionsSet()) {
2384             throw new SecurityException("Can only transfer sessions that use public options");
2385         }
2386 
2387         synchronized (mLock) {
2388             assertCallerIsOwnerOrRoot();
2389             assertPreparedAndNotSealedLocked("transfer");
2390 
2391             try {
2392                 sealLocked();
2393             } catch (PackageManagerException e) {
2394                 throw new IllegalStateException("Package is not valid", e);
2395             }
2396 
2397             mInstallerUid = newOwnerAppInfo.uid;
2398             mInstallSource = InstallSource.create(packageName, null /* originatingPackageName */,
2399                     packageName, mInstallerUid, packageName, null /* installerAttributionTag */,
2400                     params.packageSource);
2401         }
2402     }
2403 
2404     @WorkerThread
checkUserActionRequirement( PackageInstallerSession session, IntentSender target)2405     private static boolean checkUserActionRequirement(
2406             PackageInstallerSession session, IntentSender target) {
2407         if (session.isMultiPackage()) {
2408             return false;
2409         }
2410 
2411         @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED;
2412         // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
2413         userActionRequirement = session.computeUserActionRequirement();
2414         session.updateUserActionRequirement(userActionRequirement);
2415         if (userActionRequirement == USER_ACTION_REQUIRED
2416                 || userActionRequirement == USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER) {
2417             session.sendPendingUserActionIntent(target);
2418             return true;
2419         }
2420 
2421         if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
2422             if (!isTargetSdkConditionSatisfied(session)) {
2423                 session.sendPendingUserActionIntent(target);
2424                 return true;
2425             }
2426 
2427             if (session.params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
2428                 if (!session.mSilentUpdatePolicy.isSilentUpdateAllowed(
2429                         session.getInstallerPackageName(), session.getPackageName())) {
2430                     // Fall back to the non-silent update if a repeated installation is invoked
2431                     // within the throttle time.
2432                     session.sendPendingUserActionIntent(target);
2433                     return true;
2434                 }
2435                 session.mSilentUpdatePolicy.track(session.getInstallerPackageName(),
2436                         session.getPackageName());
2437             }
2438         }
2439 
2440         return false;
2441     }
2442 
2443     /**
2444      * Checks if the app being installed has a targetSdk more than the minimum required for a
2445      * silent install. See {@link SessionParams#setRequireUserAction(int)} for details about the
2446      * targetSdk requirement.
2447      * @param session Current install session
2448      * @return true if the targetSdk of the app being installed is more than the minimum required,
2449      *          resulting in a silent install, false otherwise.
2450      */
isTargetSdkConditionSatisfied(PackageInstallerSession session)2451     private static boolean isTargetSdkConditionSatisfied(PackageInstallerSession session) {
2452         final int validatedTargetSdk;
2453         final String packageName;
2454         synchronized (session.mLock) {
2455             validatedTargetSdk = session.mValidatedTargetSdk;
2456             packageName = session.mPackageName;
2457         }
2458 
2459         ApplicationInfo appInfo = new ApplicationInfo();
2460         appInfo.packageName = packageName;
2461         appInfo.targetSdkVersion = validatedTargetSdk;
2462 
2463         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
2464                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
2465         try {
2466             // Using manually constructed AppInfo to check if a change is enabled may not work
2467             // in the future.
2468             return validatedTargetSdk != INVALID_TARGET_SDK_VERSION
2469                     && platformCompat.isChangeEnabled(SILENT_INSTALL_ALLOWED, appInfo);
2470         } catch (RemoteException e) {
2471             Log.e(TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
2472             return false;
2473         }
2474     }
2475 
userActionRequirementToReason( @serActionRequirement int requirement)2476     private static @UserActionReason int userActionRequirementToReason(
2477             @UserActionRequirement int requirement) {
2478         switch (requirement) {
2479             case USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER:
2480                 return PackageInstaller.REASON_REMIND_OWNERSHIP;
2481             default:
2482                 return PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE;
2483         }
2484     }
2485 
2486     /**
2487      * Find out any session needs user action.
2488      *
2489      * @return true if the session set requires user action for the installation, otherwise false.
2490      */
2491     @WorkerThread
sendPendingUserActionIntentIfNeeded()2492     private boolean sendPendingUserActionIntentIfNeeded() {
2493         // To support pre-approval request of atomic install, we allow child session to handle
2494         // the result by itself since it has the status receiver.
2495         if (isCommitted()) {
2496             assertNotChild("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
2497         }
2498 
2499         final IntentSender statusReceiver = getRemoteStatusReceiver();
2500         return sessionContains(s -> checkUserActionRequirement(s, statusReceiver));
2501     }
2502 
2503     @WorkerThread
handleInstall()2504     private void handleInstall() {
2505         if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
2506             DevicePolicyEventLogger
2507                     .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
2508                     .setAdmin(getInstallSource().mInstallerPackageName)
2509                     .write();
2510         }
2511 
2512         /**
2513          * Stops the installation of the whole session set if one session needs user action
2514          * in its belong session set. When the user answers the yes,
2515          * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
2516          * handled to come back here to check again.
2517          *
2518          * {@code mUserActionRequired} is used to track when user action is required for an
2519          * install. Since control may come back here more than 1 time, we must ensure that it's
2520          * value is not overwritten.
2521          */
2522         boolean wasUserActionIntentSent = sendPendingUserActionIntentIfNeeded();
2523         if (mUserActionRequired == null) {
2524             mUserActionRequired = wasUserActionIntentSent;
2525         }
2526         if (wasUserActionIntentSent) {
2527             // Commit was keeping session marked as active until now; release
2528             // that extra refcount so session appears idle.
2529             deactivate();
2530             return;
2531         } else if (mUserActionRequired) {
2532             // If user action is required, control comes back here when the user allows
2533             // the installation. At this point, the session is marked active once again,
2534             // since installation is in progress.
2535             activate();
2536         }
2537 
2538         if (mVerificationInProgress) {
2539             Slog.w(TAG, "Verification is already in progress for session " + sessionId);
2540             return;
2541         }
2542         mVerificationInProgress = true;
2543 
2544         if (params.isStaged) {
2545             mStagedSession.verifySession();
2546         } else {
2547             verify();
2548         }
2549     }
2550 
verify()2551     private void verify() {
2552         try {
2553             List<PackageInstallerSession> children = getChildSessions();
2554             if (isMultiPackage()) {
2555                 for (PackageInstallerSession child : children) {
2556                     child.prepareInheritedFiles();
2557                     child.parseApkAndExtractNativeLibraries();
2558                 }
2559             } else {
2560                 prepareInheritedFiles();
2561                 parseApkAndExtractNativeLibraries();
2562             }
2563             verifyNonStaged();
2564         } catch (PackageManagerException e) {
2565             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
2566             final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
2567             setSessionFailed(e.error, errorMsg);
2568             onSessionVerificationFailure(e.error, errorMsg);
2569         }
2570     }
2571 
getRemoteStatusReceiver()2572     private IntentSender getRemoteStatusReceiver() {
2573         synchronized (mLock) {
2574             return mRemoteStatusReceiver;
2575         }
2576     }
2577 
setRemoteStatusReceiver(IntentSender remoteStatusReceiver)2578     private void setRemoteStatusReceiver(IntentSender remoteStatusReceiver) {
2579         synchronized (mLock) {
2580             mRemoteStatusReceiver = remoteStatusReceiver;
2581         }
2582     }
2583 
2584     /**
2585      * Prepares staged directory with any inherited APKs.
2586      */
prepareInheritedFiles()2587     private void prepareInheritedFiles() throws PackageManagerException {
2588         if (isApexSession() || params.mode != SessionParams.MODE_INHERIT_EXISTING) {
2589             return;
2590         }
2591         synchronized (mLock) {
2592             if (mStageDirInUse) {
2593                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2594                         "Session files in use");
2595             }
2596             if (mDestroyed) {
2597                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2598                         "Session destroyed");
2599             }
2600             if (!mSealed) {
2601                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2602                         "Session not sealed");
2603             }
2604             // Inherit any packages and native libraries from existing install that
2605             // haven't been overridden.
2606             try {
2607                 final List<File> fromFiles = mResolvedInheritedFiles;
2608                 final File toDir = stageDir;
2609                 final String tempPackageName = toDir.getName();
2610 
2611                 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
2612                 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
2613                     throw new IllegalStateException("mInheritedFilesBase == null");
2614                 }
2615 
2616                 if (isLinkPossible(fromFiles, toDir)) {
2617                     if (!mResolvedInstructionSets.isEmpty()) {
2618                         final File oatDir = new File(toDir, "oat");
2619                         createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
2620                     }
2621                     // pre-create lib dirs for linking if necessary
2622                     if (!mResolvedNativeLibPaths.isEmpty()) {
2623                         for (String libPath : mResolvedNativeLibPaths) {
2624                             // "/lib/arm64" -> ["lib", "arm64"]
2625                             final int splitIndex = libPath.lastIndexOf('/');
2626                             if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
2627                                 Slog.e(TAG,
2628                                         "Skipping native library creation for linking due"
2629                                                 + " to invalid path: " + libPath);
2630                                 continue;
2631                             }
2632                             final String libDirPath = libPath.substring(1, splitIndex);
2633                             final File libDir = new File(toDir, libDirPath);
2634                             if (!libDir.exists()) {
2635                                 NativeLibraryHelper.createNativeLibrarySubdir(libDir);
2636                             }
2637                             final String archDirPath = libPath.substring(splitIndex + 1);
2638                             NativeLibraryHelper.createNativeLibrarySubdir(
2639                                     new File(libDir, archDirPath));
2640                         }
2641                     }
2642                     linkFiles(tempPackageName, fromFiles, toDir, mInheritedFilesBase);
2643                 } else {
2644                     // TODO: this should delegate to DCS so the system process
2645                     // avoids holding open FDs into containers.
2646                     copyFiles(fromFiles, toDir);
2647                 }
2648             } catch (IOException e) {
2649                 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
2650                         "Failed to inherit existing install", e);
2651             }
2652         }
2653     }
2654 
2655     @GuardedBy("mLock")
markStageDirInUseLocked()2656     private void markStageDirInUseLocked() throws PackageManagerException {
2657         if (mDestroyed) {
2658             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2659                     "Session destroyed");
2660         }
2661         // Set this flag to prevent abandon() from deleting staging files when verification or
2662         // installation is about to start.
2663         mStageDirInUse = true;
2664     }
2665 
parseApkAndExtractNativeLibraries()2666     private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
2667         synchronized (mLock) {
2668             if (mStageDirInUse) {
2669                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2670                         "Session files in use");
2671             }
2672             if (mDestroyed) {
2673                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2674                         "Session destroyed");
2675             }
2676             if (!mSealed) {
2677                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2678                         "Session not sealed");
2679             }
2680             Objects.requireNonNull(mPackageName);
2681             Objects.requireNonNull(mSigningDetails);
2682             Objects.requireNonNull(mResolvedBaseFile);
2683             final PackageLite result;
2684             if (!isApexSession()) {
2685                 // For mode inherit existing, it would link/copy existing files to stage dir in
2686                 // prepareInheritedFiles(). Therefore, we need to parse the complete package in
2687                 // stage dir here.
2688                 // Besides, PackageLite may be null for staged sessions that don't complete
2689                 // pre-reboot verification.
2690                 result = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
2691             } else {
2692                 result = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
2693             }
2694             if (result != null) {
2695                 mPackageLite = result;
2696                 if (!isApexSession()) {
2697                     synchronized (mProgressLock) {
2698                         mInternalProgress = 0.5f;
2699                         computeProgressLocked(true);
2700                     }
2701                     extractNativeLibraries(
2702                             mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
2703                 }
2704             }
2705         }
2706     }
2707 
verifyNonStaged()2708     private void verifyNonStaged()
2709             throws PackageManagerException {
2710         synchronized (mLock) {
2711             markStageDirInUseLocked();
2712         }
2713         mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
2714             mHandler.post(() -> {
2715                 if (dispatchPendingAbandonCallback()) {
2716                     // No need to continue if abandoned
2717                     return;
2718                 }
2719                 if (error == INSTALL_SUCCEEDED) {
2720                     onVerificationComplete();
2721                 } else {
2722                     onSessionVerificationFailure(error, msg);
2723                 }
2724             });
2725         });
2726     }
2727 
2728     private static class InstallResult {
2729         public final PackageInstallerSession session;
2730         public final Bundle extras;
InstallResult(PackageInstallerSession session, Bundle extras)2731         InstallResult(PackageInstallerSession session, Bundle extras) {
2732             this.session = session;
2733             this.extras = extras;
2734         }
2735     }
2736 
2737     /**
2738      * Stages installs and do cleanup accordingly depending on whether the installation is
2739      * successful or not.
2740      *
2741      * @return a future that will be completed when the whole process is completed.
2742      */
install()2743     private CompletableFuture<Void> install() {
2744         List<CompletableFuture<InstallResult>> futures = installNonStaged();
2745         CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
2746         return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
2747             if (t == null) {
2748                 setSessionApplied();
2749                 for (CompletableFuture<InstallResult> f : futures) {
2750                     InstallResult result = f.join();
2751                     result.session.dispatchSessionFinished(
2752                             INSTALL_SUCCEEDED, "Session installed", result.extras);
2753                 }
2754             } else {
2755                 PackageManagerException e = (PackageManagerException) t.getCause();
2756                 setSessionFailed(e.error,
2757                         PackageManager.installStatusToString(e.error, e.getMessage()));
2758                 dispatchSessionFinished(e.error, e.getMessage(), null);
2759                 maybeFinishChildSessions(e.error, e.getMessage());
2760             }
2761         });
2762     }
2763 
2764     /**
2765      * Stages sessions (including child sessions if any) for install.
2766      *
2767      * @return a list of futures to indicate the install results of each session.
2768      */
2769     private List<CompletableFuture<InstallResult>> installNonStaged() {
2770         try {
2771             List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
2772             CompletableFuture<InstallResult> future = new CompletableFuture<>();
2773             futures.add(future);
2774             final InstallingSession installingSession = createInstallingSession(future);
2775             if (isMultiPackage()) {
2776                 final List<PackageInstallerSession> childSessions = getChildSessions();
2777                 List<InstallingSession> installingChildSessions =
2778                         new ArrayList<>(childSessions.size());
2779                 for (int i = 0; i < childSessions.size(); ++i) {
2780                     final PackageInstallerSession session = childSessions.get(i);
2781                     future = new CompletableFuture<>();
2782                     futures.add(future);
2783                     final InstallingSession installingChildSession =
2784                             session.createInstallingSession(future);
2785                     if (installingChildSession != null) {
2786                         installingChildSessions.add(installingChildSession);
2787                     }
2788                 }
2789                 if (!installingChildSessions.isEmpty()) {
2790                     Objects.requireNonNull(installingSession).installStage(installingChildSessions);
2791                 }
2792             } else if (installingSession != null) {
2793                 installingSession.installStage();
2794             }
2795 
2796             return futures;
2797         } catch (PackageManagerException e) {
2798             List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
2799             futures.add(CompletableFuture.failedFuture(e));
2800             return futures;
2801         }
2802     }
2803 
2804     private void sendPendingUserActionIntent(IntentSender target) {
2805         // User needs to confirm installation;
2806         // give installer an intent they can use to involve
2807         // user.
2808         final boolean isPreapproval = isPreapprovalRequested() && !isCommitted();
2809         final Intent intent = new Intent(
2810                 isPreapproval ? PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL
2811                         : PackageInstaller.ACTION_CONFIRM_INSTALL);
2812         intent.setPackage(mPm.getPackageInstallerPackageName());
2813         intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
2814         sendOnUserActionRequired(mContext, target, sessionId, intent);
2815     }
2816 
2817     @WorkerThread
2818     private void onVerificationComplete() {
2819         if (isStaged()) {
2820             mStagingManager.commitSession(mStagedSession);
2821             sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
2822             return;
2823         }
2824         install();
2825     }
2826 
2827     /**
2828      * Stages this session for install and returns a
2829      * {@link InstallingSession} representing this new staged state.
2830      *
2831      * @param future a future that will be completed when this session is completed.
2832      */
2833     @Nullable
2834     private InstallingSession createInstallingSession(CompletableFuture<InstallResult> future)
2835             throws PackageManagerException {
2836         synchronized (mLock) {
2837             if (!mSealed) {
2838                 throw new PackageManagerException(
2839                         INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
2840             }
2841             markStageDirInUseLocked();
2842         }
2843 
2844         if (isMultiPackage()) {
2845             // Always treat parent session as success for it has nothing to install
2846             future.complete(new InstallResult(this, null));
2847         } else if (isApexSession() && params.isStaged) {
2848             // Staged apex sessions have been handled by apexd
2849             future.complete(new InstallResult(this, null));
2850             return null;
2851         }
2852 
2853         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
2854             @Override
2855             public void onUserActionRequired(Intent intent) {
2856                 throw new IllegalStateException();
2857             }
2858 
2859             @Override
2860             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
2861                     Bundle extras) {
2862                 if (returnCode == INSTALL_SUCCEEDED) {
2863                     future.complete(new InstallResult(PackageInstallerSession.this, extras));
2864                 } else {
2865                     future.completeExceptionally(new PackageManagerException(returnCode, msg));
2866                 }
2867             }
2868         };
2869 
2870         final UserHandle user;
2871         if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
2872             user = UserHandle.ALL;
2873         } else {
2874             user = new UserHandle(userId);
2875         }
2876 
2877         if (params.isStaged) {
2878             params.installFlags |= INSTALL_STAGED;
2879         }
2880 
2881         if (!isMultiPackage() && !isApexSession()) {
2882             synchronized (mLock) {
2883                 // This shouldn't be null, but have this code path just in case.
2884                 if (mPackageLite == null) {
2885                     Slog.wtf(TAG, "Session: " + sessionId + ". Don't have a valid PackageLite.");
2886                 }
2887                 mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
2888             }
2889         }
2890 
2891         synchronized (mLock) {
2892             return new InstallingSession(sessionId, stageDir, localObserver, params, mInstallSource,
2893                     user, mSigningDetails, mInstallerUid, mPackageLite, mPm);
2894         }
2895     }
2896 
2897     @GuardedBy("mLock")
2898     private PackageLite getOrParsePackageLiteLocked(File packageFile, int flags)
2899             throws PackageManagerException {
2900         if (mPackageLite != null) {
2901             return mPackageLite;
2902         }
2903 
2904         final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
2905         final ParseResult<PackageLite> result =
2906                 ApkLiteParseUtils.parsePackageLite(input, packageFile, flags);
2907         if (result.isError()) {
2908             throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
2909                     result.getErrorMessage(), result.getException());
2910         }
2911         return result.getResult();
2912     }
2913 
2914     private static void maybeRenameFile(File from, File to) throws PackageManagerException {
2915         if (!from.equals(to)) {
2916             if (!from.renameTo(to)) {
2917                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2918                         "Could not rename file " + from + " to " + to);
2919             }
2920         }
2921     }
2922 
2923     private void logDataLoaderInstallationSession(int returnCode) {
2924         // Skip logging the side-loaded app installations, as those are private and aren't reported
2925         // anywhere; app stores already have a record of the installation and that's why reporting
2926         // it here is fine
2927         final String packageName = getPackageName();
2928         final String packageNameToLog =
2929                 (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? packageName : "";
2930         final long currentTimestamp = System.currentTimeMillis();
2931         final int packageUid;
2932         if (returnCode != INSTALL_SUCCEEDED) {
2933             // Package didn't install; no valid uid
2934             packageUid = INVALID_UID;
2935         } else {
2936             packageUid = mPm.snapshotComputer().getPackageUid(packageName, 0, userId);
2937         }
2938         FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
2939                 isIncrementalInstallation(),
2940                 packageNameToLog,
2941                 currentTimestamp - createdMillis,
2942                 returnCode,
2943                 getApksSize(packageName),
2944                 packageUid);
2945     }
2946 
2947     private long getApksSize(String packageName) {
2948         final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
2949         final PackageStateInternal ps = pmi.getPackageStateInternal(packageName);
2950         if (ps == null) {
2951             return 0;
2952         }
2953         final File apkDirOrPath = ps.getPath();
2954         if (apkDirOrPath == null) {
2955             return 0;
2956         }
2957         if (apkDirOrPath.isFile() && apkDirOrPath.getName().toLowerCase().endsWith(".apk")) {
2958             return apkDirOrPath.length();
2959         }
2960         if (!apkDirOrPath.isDirectory()) {
2961             return 0;
2962         }
2963         final File[] files = apkDirOrPath.listFiles();
2964         long apksSize = 0;
2965         for (int i = 0; i < files.length; i++) {
2966             if (files[i].getName().toLowerCase().endsWith(".apk")) {
2967                 apksSize += files[i].length();
2968             }
2969         }
2970         return apksSize;
2971     }
2972 
2973     /**
2974      * Returns true if the session should attempt to inherit any existing native libraries already
2975      * extracted at the current install location. This is necessary to prevent double loading of
2976      * native libraries already loaded by the running app.
2977      */
2978     private boolean mayInheritNativeLibs() {
2979         return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) &&
2980                 params.mode == SessionParams.MODE_INHERIT_EXISTING &&
2981                 (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
2982     }
2983 
2984     /**
2985      * Returns true if the session is installing an APEX package.
2986      */
2987     boolean isApexSession() {
2988         return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
2989     }
2990 
2991     boolean sessionContains(Predicate<PackageInstallerSession> filter) {
2992         if (!isMultiPackage()) {
2993             return filter.test(this);
2994         }
2995         final List<PackageInstallerSession> childSessions;
2996         synchronized (mLock) {
2997             childSessions = getChildSessionsLocked();
2998         }
2999         for (PackageInstallerSession child: childSessions) {
3000             if (filter.test(child)) {
3001                 return true;
3002             }
3003         }
3004         return false;
3005     }
3006 
3007     boolean containsApkSession() {
3008         return sessionContains((s) -> !s.isApexSession());
3009     }
3010 
3011     /**
3012      * Validate apex install.
3013      * <p>
3014      * Sets {@link #mResolvedBaseFile} for RollbackManager to use. Sets {@link #mPackageName} for
3015      * StagingManager to use.
3016      */
3017     @GuardedBy("mLock")
3018     private void validateApexInstallLocked()
3019             throws PackageManagerException {
3020         final List<File> addedFiles = getAddedApksLocked();
3021         if (addedFiles.isEmpty()) {
3022             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3023                     TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId,
3024                           stageDir.getAbsolutePath()));
3025         }
3026 
3027         if (ArrayUtils.size(addedFiles) > 1) {
3028             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3029                     "Too many files for apex install");
3030         }
3031 
3032         File addedFile = addedFiles.get(0); // there is only one file
3033 
3034         // Ensure file name has proper suffix
3035         final String sourceName = addedFile.getName();
3036         final String targetName = sourceName.endsWith(APEX_FILE_EXTENSION)
3037                 ? sourceName
3038                 : sourceName + APEX_FILE_EXTENSION;
3039         if (!FileUtils.isValidExtFilename(targetName)) {
3040             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3041                     "Invalid filename: " + targetName);
3042         }
3043 
3044         final File targetFile = new File(stageDir, targetName);
3045         resolveAndStageFileLocked(addedFile, targetFile, null);
3046         mResolvedBaseFile = targetFile;
3047 
3048         // Populate package name of the apex session
3049         mPackageName = null;
3050         final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
3051         final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(),
3052                 mResolvedBaseFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
3053         if (ret.isError()) {
3054             throw new PackageManagerException(ret.getErrorCode(), ret.getErrorMessage(),
3055                     ret.getException());
3056         }
3057         final ApkLite apk = ret.getResult();
3058 
3059         if (mPackageName == null) {
3060             mPackageName = apk.getPackageName();
3061             mVersionCode = apk.getLongVersionCode();
3062         }
3063 
3064         mSigningDetails = apk.getSigningDetails();
3065         mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver();
3066     }
3067 
3068     /**
3069      * Validate install by confirming that all application packages are have
3070      * consistent package name, version code, and signing certificates.
3071      * <p>
3072      * Clears and populates {@link #mResolvedBaseFile},
3073      * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
3074      * <p>
3075      * Renames package files in stage to match split names defined inside.
3076      * <p>
3077      * Note that upgrade compatibility is still performed by
3078      * {@link PackageManagerService}.
3079      * @return a {@link PackageLite} representation of the validated APK(s).
3080      */
3081     @GuardedBy("mLock")
3082     private PackageLite validateApkInstallLocked() throws PackageManagerException {
3083         ApkLite baseApk = null;
3084         final PackageLite packageLite;
3085         mPackageLite = null;
3086         mPackageName = null;
3087         mVersionCode = -1;
3088         mSigningDetails = SigningDetails.UNKNOWN;
3089 
3090         mResolvedBaseFile = null;
3091         mResolvedStagedFiles.clear();
3092         mResolvedInheritedFiles.clear();
3093 
3094         final PackageInfo pkgInfo = mPm.snapshotComputer().getPackageInfo(
3095                 params.appPackageName, PackageManager.GET_SIGNATURES
3096                         | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES /*flags*/, userId);
3097 
3098         // Partial installs must be consistent with existing install
3099         if (params.mode == SessionParams.MODE_INHERIT_EXISTING
3100                 && (pkgInfo == null || pkgInfo.applicationInfo == null)) {
3101             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3102                     "Missing existing base package");
3103         }
3104 
3105         // Default to require only if existing base apk has fs-verity signature.
3106         mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled()
3107                 && params.mode == SessionParams.MODE_INHERIT_EXISTING
3108                 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath())
3109                 && (new File(VerityUtils.getFsveritySignatureFilePath(
3110                         pkgInfo.applicationInfo.getBaseCodePath()))).exists();
3111 
3112         final List<File> removedFiles = getRemovedFilesLocked();
3113         final List<String> removeSplitList = new ArrayList<>();
3114         if (!removedFiles.isEmpty()) {
3115             for (File removedFile : removedFiles) {
3116                 final String fileName = removedFile.getName();
3117                 final String splitName = fileName.substring(
3118                         0, fileName.length() - REMOVE_MARKER_EXTENSION.length());
3119                 removeSplitList.add(splitName);
3120             }
3121         }
3122 
3123         final List<File> addedFiles = getAddedApksLocked();
3124         if (addedFiles.isEmpty()
3125                 && (removeSplitList.size() == 0 || getStagedAppMetadataFile() != null)) {
3126             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3127                     TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId,
3128                           stageDir.getAbsolutePath()));
3129         }
3130 
3131         // Verify that all staged packages are internally consistent
3132         final ArraySet<String> stagedSplits = new ArraySet<>();
3133         final ArraySet<String> stagedSplitTypes = new ArraySet<>();
3134         final ArraySet<String> requiredSplitTypes = new ArraySet<>();
3135         final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>();
3136         final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
3137         for (File addedFile : addedFiles) {
3138             final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
3139                     addedFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
3140             if (result.isError()) {
3141                 throw new PackageManagerException(result.getErrorCode(),
3142                         result.getErrorMessage(), result.getException());
3143             }
3144 
3145             final ApkLite apk = result.getResult();
3146             if (!stagedSplits.add(apk.getSplitName())) {
3147                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3148                         "Split " + apk.getSplitName() + " was defined multiple times");
3149             }
3150 
3151             // Use first package to define unknown values
3152             if (mPackageName == null) {
3153                 mPackageName = apk.getPackageName();
3154                 mVersionCode = apk.getLongVersionCode();
3155             }
3156             if (mSigningDetails == SigningDetails.UNKNOWN) {
3157                 mSigningDetails = apk.getSigningDetails();
3158             }
3159             mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver();
3160 
3161             assertApkConsistentLocked(String.valueOf(addedFile), apk);
3162 
3163             // Take this opportunity to enforce uniform naming
3164             final String targetName = ApkLiteParseUtils.splitNameToFileName(apk);
3165             if (!FileUtils.isValidExtFilename(targetName)) {
3166                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3167                         "Invalid filename: " + targetName);
3168             }
3169 
3170             // Yell loudly if installers drop attribute installLocation when apps explicitly set.
3171             if (apk.getInstallLocation() != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
3172                 final String installerPackageName = getInstallerPackageName();
3173                 if (installerPackageName != null
3174                         && (params.installLocation != apk.getInstallLocation())) {
3175                     Slog.wtf(TAG, installerPackageName
3176                             + " drops manifest attribute android:installLocation in " + targetName
3177                             + " for " + mPackageName);
3178                 }
3179             }
3180 
3181             final File targetFile = new File(stageDir, targetName);
3182             resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName());
3183 
3184             // Base is coming from session
3185             if (apk.getSplitName() == null) {
3186                 mResolvedBaseFile = targetFile;
3187                 baseApk = apk;
3188             } else {
3189                 splitApks.put(apk.getSplitName(), apk);
3190             }
3191 
3192             // Collect the requiredSplitTypes and staged splitTypes
3193             CollectionUtils.addAll(requiredSplitTypes, apk.getRequiredSplitTypes());
3194             CollectionUtils.addAll(stagedSplitTypes, apk.getSplitTypes());
3195         }
3196 
3197         if (removeSplitList.size() > 0) {
3198             if (pkgInfo == null) {
3199                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3200                         "Missing existing base package for " + mPackageName);
3201             }
3202 
3203             // validate split names marked for removal
3204             for (String splitName : removeSplitList) {
3205                 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
3206                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3207                             "Split not found: " + splitName);
3208                 }
3209             }
3210 
3211             // ensure we've got appropriate package name, version code and signatures
3212             if (mPackageName == null) {
3213                 mPackageName = pkgInfo.packageName;
3214                 mVersionCode = pkgInfo.getLongVersionCode();
3215             }
3216             if (mSigningDetails == SigningDetails.UNKNOWN) {
3217                 mSigningDetails = unsafeGetCertsWithoutVerification(
3218                         pkgInfo.applicationInfo.sourceDir);
3219             }
3220         }
3221 
3222         if (isIncrementalInstallation()) {
3223             if (!isIncrementalInstallationAllowed(mPackageName)) {
3224                 throw new PackageManagerException(
3225                         PackageManager.INSTALL_FAILED_SESSION_INVALID,
3226                         "Incremental installation of this package is not allowed.");
3227             }
3228             // Since we moved the staged app metadata file so that incfs can be initialized, lets
3229             // now move it back.
3230             File appMetadataFile = getTmpAppMetadataFile();
3231             if (appMetadataFile.exists()) {
3232                 final IncrementalFileStorages incrementalFileStorages =
3233                         getIncrementalFileStorages();
3234                 try {
3235                     incrementalFileStorages.makeFile(APP_METADATA_FILE_NAME,
3236                             Files.readAllBytes(appMetadataFile.toPath()),
3237                             APP_METADATA_FILE_ACCESS_MODE);
3238                 } catch (IOException e) {
3239                     Slog.e(TAG, "Failed to write app metadata to incremental storage", e);
3240                 } finally {
3241                     appMetadataFile.delete();
3242                 }
3243             }
3244         }
3245 
3246         if (mInstallerUid != mOriginalInstallerUid) {
3247             // Session has been transferred, check package name.
3248             if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals(
3249                     mOriginalInstallerPackageName)) {
3250                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
3251                         "Can only transfer sessions that update the original installer");
3252             }
3253         }
3254 
3255         if (!mChecksums.isEmpty()) {
3256             throw new PackageManagerException(
3257                     PackageManager.INSTALL_FAILED_SESSION_INVALID,
3258                     "Invalid checksum name(s): " + String.join(",", mChecksums.keySet()));
3259         }
3260 
3261         if (params.mode == SessionParams.MODE_FULL_INSTALL) {
3262             // Full installs must include a base package
3263             if (!stagedSplits.contains(null)) {
3264                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3265                         "Full install must include a base package");
3266             } else if ((params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
3267                 EventLog.writeEvent(0x534e4554, "219044664");
3268 
3269                 // Installing base.apk. Make sure the app is restarted.
3270                 params.setDontKillApp(false);
3271             }
3272             if (baseApk.isSplitRequired() && (stagedSplits.size() <= 1
3273                     || !stagedSplitTypes.containsAll(requiredSplitTypes))) {
3274                 throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
3275                         "Missing split for " + mPackageName);
3276             }
3277             // For mode full install, we compose package lite for future usage instead of
3278             // re-parsing it again and again.
3279             final ParseResult<PackageLite> pkgLiteResult =
3280                     ApkLiteParseUtils.composePackageLiteFromApks(input.reset(), stageDir, baseApk,
3281                             splitApks, true);
3282             if (pkgLiteResult.isError()) {
3283                 throw new PackageManagerException(pkgLiteResult.getErrorCode(),
3284                         pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
3285             }
3286             mPackageLite = pkgLiteResult.getResult();
3287             packageLite = mPackageLite;
3288         } else {
3289             final ApplicationInfo appInfo = pkgInfo.applicationInfo;
3290             ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite(
3291                     input.reset(), new File(appInfo.getCodePath()), 0);
3292             if (pkgLiteResult.isError()) {
3293                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
3294                         pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
3295             }
3296             final PackageLite existing = pkgLiteResult.getResult();
3297             packageLite = existing;
3298             assertPackageConsistentLocked("Existing", existing.getPackageName(),
3299                     existing.getLongVersionCode());
3300             final SigningDetails signingDetails =
3301                     unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
3302             if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
3303                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3304                         "Existing signatures are inconsistent");
3305             }
3306 
3307             // Inherit base if not overridden.
3308             if (mResolvedBaseFile == null) {
3309                 mResolvedBaseFile = new File(appInfo.getBaseCodePath());
3310                 inheritFileLocked(mResolvedBaseFile);
3311                 // Collect the requiredSplitTypes from base
3312                 CollectionUtils.addAll(requiredSplitTypes, existing.getBaseRequiredSplitTypes());
3313             } else if ((params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
3314                 EventLog.writeEvent(0x534e4554, "219044664");
3315 
3316                 // Installing base.apk. Make sure the app is restarted.
3317                 params.setDontKillApp(false);
3318             }
3319 
3320             // Inherit splits if not overridden.
3321             if (!ArrayUtils.isEmpty(existing.getSplitNames())) {
3322                 for (int i = 0; i < existing.getSplitNames().length; i++) {
3323                     final String splitName = existing.getSplitNames()[i];
3324                     final File splitFile = new File(existing.getSplitApkPaths()[i]);
3325                     final boolean splitRemoved = removeSplitList.contains(splitName);
3326                     if (!stagedSplits.contains(splitName) && !splitRemoved) {
3327                         inheritFileLocked(splitFile);
3328                         // Collect the requiredSplitTypes and staged splitTypes from splits
3329                         CollectionUtils.addAll(requiredSplitTypes,
3330                                 existing.getRequiredSplitTypes()[i]);
3331                         CollectionUtils.addAll(stagedSplitTypes, existing.getSplitTypes()[i]);
3332                     }
3333                 }
3334             }
3335 
3336             // Inherit compiled oat directory.
3337             final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
3338             mInheritedFilesBase = packageInstallDir;
3339             final File oatDir = new File(packageInstallDir, "oat");
3340             if (oatDir.exists()) {
3341                 final File[] archSubdirs = oatDir.listFiles();
3342 
3343                 // Keep track of all instruction sets we've seen compiled output for.
3344                 // If we're linking (and not copying) inherited files, we can recreate the
3345                 // instruction set hierarchy and link compiled output.
3346                 if (archSubdirs != null && archSubdirs.length > 0) {
3347                     final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
3348                     for (File archSubDir : archSubdirs) {
3349                         // Skip any directory that isn't an ISA subdir.
3350                         if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
3351                             continue;
3352                         }
3353 
3354                         File[] files = archSubDir.listFiles();
3355                         if (files == null || files.length == 0) {
3356                             continue;
3357                         }
3358 
3359                         mResolvedInstructionSets.add(archSubDir.getName());
3360                         mResolvedInheritedFiles.addAll(Arrays.asList(files));
3361                     }
3362                 }
3363             }
3364 
3365             // Inherit native libraries for DONT_KILL sessions.
3366             if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
3367                 File[] libDirs = new File[]{
3368                         new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME),
3369                         new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)};
3370                 for (File libDir : libDirs) {
3371                     if (!libDir.exists() || !libDir.isDirectory()) {
3372                         continue;
3373                     }
3374                     final List<String> libDirsToInherit = new ArrayList<>();
3375                     final List<File> libFilesToInherit = new ArrayList<>();
3376                     for (File archSubDir : libDir.listFiles()) {
3377                         if (!archSubDir.isDirectory()) {
3378                             continue;
3379                         }
3380                         String relLibPath;
3381                         try {
3382                             relLibPath = getRelativePath(archSubDir, packageInstallDir);
3383                         } catch (IOException e) {
3384                             Slog.e(TAG, "Skipping linking of native library directory!", e);
3385                             // shouldn't be possible, but let's avoid inheriting these to be safe
3386                             libDirsToInherit.clear();
3387                             libFilesToInherit.clear();
3388                             break;
3389                         }
3390 
3391                         File[] files = archSubDir.listFiles();
3392                         if (files == null || files.length == 0) {
3393                             continue;
3394                         }
3395 
3396                         libDirsToInherit.add(relLibPath);
3397                         libFilesToInherit.addAll(Arrays.asList(files));
3398                     }
3399                     for (String subDir : libDirsToInherit) {
3400                         if (!mResolvedNativeLibPaths.contains(subDir)) {
3401                             mResolvedNativeLibPaths.add(subDir);
3402                         }
3403                     }
3404                     mResolvedInheritedFiles.addAll(libFilesToInherit);
3405                 }
3406             }
3407             // For the case of split required, failed if no splits existed
3408             if (packageLite.isSplitRequired()) {
3409                 final int existingSplits = ArrayUtils.size(existing.getSplitNames());
3410                 final boolean allSplitsRemoved = (existingSplits == removeSplitList.size());
3411                 final boolean onlyBaseFileStaged = (stagedSplits.size() == 1
3412                         && stagedSplits.contains(null));
3413                 if ((allSplitsRemoved && (stagedSplits.isEmpty() || onlyBaseFileStaged))
3414                         || !stagedSplitTypes.containsAll(requiredSplitTypes)) {
3415                     throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
3416                             "Missing split for " + mPackageName);
3417                 }
3418             }
3419         }
3420 
3421         assertPreapprovalDetailsConsistentIfNeededLocked(packageLite, pkgInfo);
3422 
3423         if (packageLite.isUseEmbeddedDex()) {
3424             for (File file : mResolvedStagedFiles) {
3425                 if (file.getName().endsWith(".apk")
3426                         && !DexManager.auditUncompressedDexInApk(file.getPath())) {
3427                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3428                             "Some dex are not uncompressed and aligned correctly for "
3429                             + mPackageName);
3430                 }
3431             }
3432         }
3433 
3434         final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
3435         if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
3436             if (!packageLite.isDebuggable() && !packageLite.isProfileableByShell()) {
3437                 mIncrementalFileStorages.disallowReadLogs();
3438             }
3439         }
3440 
3441         // {@link #sendPendingUserActionIntentIfNeeded} needs to use
3442         // {@link PackageLite#getTargetSdk()}
3443         mValidatedTargetSdk = packageLite.getTargetSdk();
3444 
3445         return packageLite;
3446     }
3447 
3448     @GuardedBy("mLock")
3449     private void stageFileLocked(File origFile, File targetFile)
3450             throws PackageManagerException {
3451         mResolvedStagedFiles.add(targetFile);
3452         maybeRenameFile(origFile, targetFile);
3453     }
3454 
3455     @GuardedBy("mLock")
3456     private void maybeStageFsveritySignatureLocked(File origFile, File targetFile,
3457             boolean fsVerityRequired) throws PackageManagerException {
3458         final File originalSignature = new File(
3459                 VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
3460         if (originalSignature.exists()) {
3461             final File stagedSignature = new File(
3462                     VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
3463             stageFileLocked(originalSignature, stagedSignature);
3464         } else if (fsVerityRequired) {
3465             throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
3466                     "Missing corresponding fs-verity signature to " + origFile);
3467         }
3468     }
3469 
3470     @GuardedBy("mLock")
3471     private void maybeStageDexMetadataLocked(File origFile, File targetFile)
3472             throws PackageManagerException {
3473         final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(origFile);
3474         if (dexMetadataFile == null) {
3475             return;
3476         }
3477 
3478         if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
3479             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3480                     "Invalid filename: " + dexMetadataFile);
3481         }
3482         final File targetDexMetadataFile = new File(stageDir,
3483                 DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName()));
3484 
3485         stageFileLocked(dexMetadataFile, targetDexMetadataFile);
3486 
3487         // Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on
3488         // supported on older devices.
3489         maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile,
3490                 DexMetadataHelper.isFsVerityRequired());
3491     }
3492 
3493     private IncrementalFileStorages getIncrementalFileStorages() {
3494         synchronized (mLock) {
3495             return mIncrementalFileStorages;
3496         }
3497     }
3498 
3499     private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
3500             final byte[] bytes) throws IOException {
3501         final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages();
3502         if (!isIncrementalInstallation() || incrementalFileStorages == null) {
3503             FileUtils.bytesToFile(absolutePath, bytes);
3504         } else {
3505             incrementalFileStorages.makeFile(localPath, bytes, 0777);
3506         }
3507     }
3508 
3509     @GuardedBy("mLock")
3510     private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName)
3511             throws PackageManagerException {
3512         final PerFileChecksum perFileChecksum = mChecksums.get(origFile.getName());
3513         if (perFileChecksum == null) {
3514             return;
3515         }
3516         mChecksums.remove(origFile.getName());
3517 
3518         final Checksum[] checksums = perFileChecksum.getChecksums();
3519         if (checksums.length == 0) {
3520             return;
3521         }
3522 
3523         final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
3524         final File targetDigestsFile = new File(stageDir, targetDigestsPath);
3525         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
3526             ApkChecksums.writeChecksums(os, checksums);
3527 
3528             final byte[] signature = perFileChecksum.getSignature();
3529             if (signature != null && signature.length > 0) {
3530                 Certificate[] ignored = ApkChecksums.verifySignature(checksums, signature);
3531             }
3532 
3533             // Storing and staging checksums.
3534             storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(),
3535                     os.toByteArray());
3536             stageFileLocked(targetDigestsFile, targetDigestsFile);
3537 
3538             // Storing and staging signature.
3539             if (signature == null || signature.length == 0) {
3540                 return;
3541             }
3542 
3543             final String targetDigestsSignaturePath = ApkChecksums.buildSignaturePathForDigests(
3544                     targetDigestsPath);
3545             final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath);
3546             storeBytesToInstallationFile(targetDigestsSignaturePath,
3547                     targetDigestsSignatureFile.getAbsolutePath(), signature);
3548             stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile);
3549         } catch (IOException e) {
3550             throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
3551                     "Failed to store digests for " + mPackageName, e);
3552         } catch (NoSuchAlgorithmException | SignatureException e) {
3553             throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
3554                     "Failed to verify digests' signature for " + mPackageName, e);
3555         }
3556     }
3557 
3558     @GuardedBy("mLock")
3559     private boolean isFsVerityRequiredForApk(File origFile, File targetFile)
3560             throws PackageManagerException {
3561         if (mVerityFoundForApks) {
3562             return true;
3563         }
3564 
3565         // We haven't seen .fsv_sig for any APKs. Treat it as not required until we see one.
3566         final File originalSignature = new File(
3567                 VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
3568         if (!originalSignature.exists()) {
3569             return false;
3570         }
3571         mVerityFoundForApks = true;
3572 
3573         // When a signature is found, also check any previous staged APKs since they also need to
3574         // have fs-verity signature consistently.
3575         for (File file : mResolvedStagedFiles) {
3576             if (!file.getName().endsWith(".apk")) {
3577                 continue;
3578             }
3579             // Ignore the current targeting file.
3580             if (targetFile.getName().equals(file.getName())) {
3581                 continue;
3582             }
3583             throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
3584                     "Previously staged apk is missing fs-verity signature");
3585         }
3586         return true;
3587     }
3588 
3589     @GuardedBy("mLock")
3590     private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName)
3591             throws PackageManagerException {
3592         stageFileLocked(origFile, targetFile);
3593 
3594         // Stage APK's fs-verity signature if present.
3595         maybeStageFsveritySignatureLocked(origFile, targetFile,
3596                 isFsVerityRequiredForApk(origFile, targetFile));
3597         // Stage dex metadata (.dm) and corresponding fs-verity signature if present.
3598         maybeStageDexMetadataLocked(origFile, targetFile);
3599         // Stage checksums (.digests) if present.
3600         maybeStageDigestsLocked(origFile, targetFile, splitName);
3601     }
3602 
3603     @GuardedBy("mLock")
3604     private void maybeInheritFsveritySignatureLocked(File origFile) {
3605         // Inherit the fsverity signature file if present.
3606         final File fsveritySignatureFile = new File(
3607                 VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
3608         if (fsveritySignatureFile.exists()) {
3609             mResolvedInheritedFiles.add(fsveritySignatureFile);
3610         }
3611     }
3612 
3613     @GuardedBy("mLock")
3614     private void inheritFileLocked(File origFile) {
3615         mResolvedInheritedFiles.add(origFile);
3616 
3617         maybeInheritFsveritySignatureLocked(origFile);
3618 
3619         // Inherit the dex metadata if present.
3620         final File dexMetadataFile =
3621                 DexMetadataHelper.findDexMetadataForFile(origFile);
3622         if (dexMetadataFile != null) {
3623             mResolvedInheritedFiles.add(dexMetadataFile);
3624             maybeInheritFsveritySignatureLocked(dexMetadataFile);
3625         }
3626         // Inherit the digests if present.
3627         final File digestsFile = ApkChecksums.findDigestsForFile(origFile);
3628         if (digestsFile != null) {
3629             mResolvedInheritedFiles.add(digestsFile);
3630 
3631             final File signatureFile = ApkChecksums.findSignatureForDigests(digestsFile);
3632             if (signatureFile != null) {
3633                 mResolvedInheritedFiles.add(signatureFile);
3634             }
3635         }
3636     }
3637 
3638     @GuardedBy("mLock")
3639     private void assertApkConsistentLocked(String tag, ApkLite apk)
3640             throws PackageManagerException {
3641         assertPackageConsistentLocked(tag, apk.getPackageName(), apk.getLongVersionCode());
3642         if (!mSigningDetails.signaturesMatchExactly(apk.getSigningDetails())) {
3643             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3644                     tag + " signatures are inconsistent");
3645         }
3646     }
3647 
3648     @GuardedBy("mLock")
3649     private void assertPackageConsistentLocked(String tag, String packageName,
3650             long versionCode) throws PackageManagerException {
3651         if (!mPackageName.equals(packageName)) {
3652             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
3653                     + packageName + " inconsistent with " + mPackageName);
3654         }
3655         if (params.appPackageName != null && !params.appPackageName.equals(packageName)) {
3656             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
3657                     + " specified package " + params.appPackageName
3658                     + " inconsistent with " + packageName);
3659         }
3660         if (mVersionCode != versionCode) {
3661             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
3662                     + " version code " + versionCode + " inconsistent with "
3663                     + mVersionCode);
3664         }
3665     }
3666 
3667     @GuardedBy("mLock")
3668     private void assertPreapprovalDetailsConsistentIfNeededLocked(@NonNull PackageLite packageLite,
3669             @Nullable PackageInfo info) throws PackageManagerException {
3670         if (mPreapprovalDetails == null || !isPreapprovalRequested()) {
3671             return;
3672         }
3673 
3674         if (!TextUtils.equals(mPackageName, mPreapprovalDetails.getPackageName())) {
3675             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
3676                     mPreapprovalDetails + " inconsistent with " + mPackageName);
3677         }
3678 
3679         final PackageManager packageManager = mContext.getPackageManager();
3680         // The given info isn't null only when params.appPackageName is set.
3681         final PackageInfo existingPackageInfo =
3682                 info != null ? info : mPm.snapshotComputer().getPackageInfo(mPackageName,
3683                         0 /* flags */, userId);
3684         // If the app label in PreapprovalDetails matches the existing one, we treat it as valid.
3685         final CharSequence appLabel = mPreapprovalDetails.getLabel();
3686         if (existingPackageInfo != null) {
3687             final ApplicationInfo existingAppInfo = existingPackageInfo.applicationInfo;
3688             final CharSequence existingAppLabel = packageManager.getApplicationLabel(
3689                     existingAppInfo);
3690             if (TextUtils.equals(appLabel, existingAppLabel)) {
3691                 return;
3692             }
3693         }
3694 
3695         final PackageInfo packageInfoFromApk = packageManager.getPackageArchiveInfo(
3696                         packageLite.getPath(), PackageInfoFlags.of(0));
3697         if (packageInfoFromApk == null) {
3698             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3699                     "Failure to obtain package info from APK files.");
3700         }
3701 
3702         // In case the app label in PreapprovalDetails from different locale in split APK,
3703         // we check all APK files to find the app label.
3704         final List<String> filePaths = packageLite.getAllApkPaths();
3705         final ULocale appLocale = mPreapprovalDetails.getLocale();
3706         final ApplicationInfo appInfo = packageInfoFromApk.applicationInfo;
3707         boolean appLabelMatched = false;
3708         for (int i = filePaths.size() - 1; i >= 0 && !appLabelMatched; i--) {
3709             appLabelMatched |= TextUtils.equals(getAppLabel(filePaths.get(i), appLocale, appInfo),
3710                     appLabel);
3711         }
3712         if (!appLabelMatched) {
3713             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
3714                     mPreapprovalDetails + " inconsistent with app label");
3715         }
3716     }
3717 
3718     private CharSequence getAppLabel(String path, ULocale locale, ApplicationInfo appInfo)
3719             throws PackageManagerException {
3720         final Resources pRes = mContext.getResources();
3721         final AssetManager assetManager = new AssetManager();
3722         final Configuration config = new Configuration(pRes.getConfiguration());
3723         final ApkAssets apkAssets;
3724         try {
3725             apkAssets = ApkAssets.loadFromPath(path);
3726         } catch (IOException e) {
3727             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3728                     "Failure to get resources from package archive " + path);
3729         }
3730         assetManager.setApkAssets(new ApkAssets[]{apkAssets}, false /* invalidateCaches */);
3731         config.setLocale(locale.toLocale());
3732         final Resources res = new Resources(assetManager, pRes.getDisplayMetrics(), config);
3733         return TextUtils.trimToSize(tryLoadingAppLabel(res, appInfo), MAX_SAFE_LABEL_LENGTH);
3734     }
3735 
3736     private CharSequence tryLoadingAppLabel(@NonNull Resources res, @NonNull ApplicationInfo info) {
3737         CharSequence label = null;
3738         // Try to load the label from the package's resources. If an app has not explicitly
3739         // specified any label, just use the package name.
3740         if (info.labelRes != 0) {
3741             try {
3742                 label = res.getText(info.labelRes).toString().trim();
3743             } catch (Resources.NotFoundException ignore) {
3744             }
3745         }
3746         if (label == null) {
3747             label = (info.nonLocalizedLabel != null)
3748                     ? info.nonLocalizedLabel : info.packageName;
3749         }
3750 
3751         return label;
3752     }
3753 
3754     private SigningDetails unsafeGetCertsWithoutVerification(String path)
3755             throws PackageManagerException {
3756         final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
3757         final ParseResult<SigningDetails> result =
3758                 ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
3759                         input, path, SigningDetails.SignatureSchemeVersion.JAR);
3760         if (result.isError()) {
3761             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
3762                     "Couldn't obtain signatures from APK : " + path);
3763         }
3764         return result.getResult();
3765     }
3766 
3767     /**
3768      * Determine if creating hard links between source and destination is
3769      * possible. That is, do they all live on the same underlying device.
3770      */
3771     private static boolean isLinkPossible(List<File> fromFiles, File toDir) {
3772         try {
3773             final StructStat toStat = Os.stat(toDir.getAbsolutePath());
3774             for (File fromFile : fromFiles) {
3775                 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
3776                 if (fromStat.st_dev != toStat.st_dev) {
3777                     return false;
3778                 }
3779             }
3780         } catch (ErrnoException e) {
3781             Slog.w(TAG, "Failed to detect if linking possible: " + e);
3782             return false;
3783         }
3784         return true;
3785     }
3786 
3787     /**
3788      * @return the uid of the owner this session
3789      */
3790     public int getInstallerUid() {
3791         synchronized (mLock) {
3792             return mInstallerUid;
3793         }
3794     }
3795 
3796     /**
3797      * @return the package name of this session
3798      */
3799     @VisibleForTesting(visibility = PACKAGE)
3800     public String getPackageName() {
3801         synchronized (mLock) {
3802             return mPackageName;
3803         }
3804     }
3805 
3806     /**
3807      * @return the timestamp of when this session last changed state
3808      */
3809     public long getUpdatedMillis() {
3810         synchronized (mLock) {
3811             return updatedMillis;
3812         }
3813     }
3814 
3815     long getCommittedMillis() {
3816         synchronized (mLock) {
3817             return committedMillis;
3818         }
3819     }
3820 
3821     String getInstallerPackageName() {
3822         return getInstallSource().mInstallerPackageName;
3823     }
3824 
3825     String getInstallerAttributionTag() {
3826         return getInstallSource().mInstallerAttributionTag;
3827     }
3828 
3829     InstallSource getInstallSource() {
3830         synchronized (mLock) {
3831             return mInstallSource;
3832         }
3833     }
3834 
3835     SigningDetails getSigningDetails() {
3836         synchronized (mLock) {
3837             return mSigningDetails;
3838         }
3839     }
3840 
3841     PackageLite getPackageLite() {
3842         synchronized (mLock) {
3843             return mPackageLite;
3844         }
3845     }
3846 
3847     /**
3848      * @return a boolean value indicating whether user action was requested for the install.
3849      * Returns {@code false} if {@code mUserActionRequired} is {@code null}
3850      */
3851     public boolean getUserActionRequired() {
3852         if (mUserActionRequired != null) {
3853             return mUserActionRequired.booleanValue();
3854         }
3855         Slog.wtf(TAG, "mUserActionRequired should not be null.");
3856         return false;
3857     }
3858 
3859     private static String getRelativePath(File file, File base) throws IOException {
3860         final String pathStr = file.getAbsolutePath();
3861         final String baseStr = base.getAbsolutePath();
3862         // Don't allow relative paths.
3863         if (pathStr.contains("/.") ) {
3864             throw new IOException("Invalid path (was relative) : " + pathStr);
3865         }
3866 
3867         if (pathStr.startsWith(baseStr)) {
3868             return pathStr.substring(baseStr.length());
3869         }
3870 
3871         throw new IOException("File: " + pathStr + " outside base: " + baseStr);
3872     }
3873 
3874     private void createOatDirs(String packageName, List<String> instructionSets, File fromDir)
3875             throws PackageManagerException {
3876         for (String instructionSet : instructionSets) {
3877             try {
3878                 mInstaller.createOatDir(packageName, fromDir.getAbsolutePath(), instructionSet);
3879             } catch (InstallerException e) {
3880                 throw PackageManagerException.from(e);
3881             }
3882         }
3883     }
3884 
3885     private void linkFile(String packageName, String relativePath, String fromBase, String toBase)
3886             throws IOException {
3887         try {
3888             // Try
3889             final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages();
3890             if (incrementalFileStorages != null && incrementalFileStorages.makeLink(relativePath,
3891                     fromBase, toBase)) {
3892                 return;
3893             }
3894             mInstaller.linkFile(packageName, relativePath, fromBase, toBase);
3895         } catch (InstallerException | IOException e) {
3896             throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
3897                     + fromBase + ", " + toBase + ")", e);
3898         }
3899     }
3900 
3901     private void linkFiles(String packageName, List<File> fromFiles, File toDir, File fromDir)
3902             throws IOException {
3903         for (File fromFile : fromFiles) {
3904             final String relativePath = getRelativePath(fromFile, fromDir);
3905             final String fromBase = fromDir.getAbsolutePath();
3906             final String toBase = toDir.getAbsolutePath();
3907 
3908             linkFile(packageName, relativePath, fromBase, toBase);
3909         }
3910 
3911         Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
3912     }
3913 
3914     private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
3915         // Remove any partial files from previous attempt
3916         for (File file : toDir.listFiles()) {
3917             if (file.getName().endsWith(".tmp")) {
3918                 file.delete();
3919             }
3920         }
3921 
3922         for (File fromFile : fromFiles) {
3923             final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
3924             if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
3925             if (!FileUtils.copyFile(fromFile, tmpFile)) {
3926                 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
3927             }
3928             try {
3929                 Os.chmod(tmpFile.getAbsolutePath(), 0644);
3930             } catch (ErrnoException e) {
3931                 throw new IOException("Failed to chmod " + tmpFile);
3932             }
3933             final File toFile = new File(toDir, fromFile.getName());
3934             if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
3935             if (!tmpFile.renameTo(toFile)) {
3936                 throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
3937             }
3938         }
3939         Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
3940     }
3941 
3942     private void extractNativeLibraries(PackageLite packageLite, File packageDir,
3943             String abiOverride, boolean inherit)
3944             throws PackageManagerException {
3945         Objects.requireNonNull(packageLite);
3946         final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
3947         if (!inherit) {
3948             // Start from a clean slate
3949             NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
3950         }
3951 
3952         NativeLibraryHelper.Handle handle = null;
3953         try {
3954             handle = NativeLibraryHelper.Handle.create(packageLite);
3955             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
3956                     abiOverride, isIncrementalInstallation());
3957             if (res != INSTALL_SUCCEEDED) {
3958                 throw new PackageManagerException(res,
3959                         "Failed to extract native libraries, res=" + res);
3960             }
3961         } catch (IOException e) {
3962             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
3963                     "Failed to extract native libraries", e);
3964         } finally {
3965             IoUtils.closeQuietly(handle);
3966         }
3967     }
3968 
3969     void setPermissionsResult(boolean accepted) {
3970         if (!isSealed() && !isPreapprovalRequested()) {
3971             throw new SecurityException("Must be sealed to accept permissions");
3972         }
3973 
3974         // To support pre-approval request of atomic install, we allow child session to handle
3975         // the result by itself since it has the status receiver.
3976         final PackageInstallerSession root = hasParentSessionId() && isCommitted()
3977                 ? mSessionProvider.getSession(getParentSessionId()) : this;
3978 
3979         if (accepted) {
3980             // Mark and kick off another install pass
3981             synchronized (mLock) {
3982                 mPermissionsManuallyAccepted = true;
3983             }
3984             root.mHandler.obtainMessage(
3985                     isCommitted() ? MSG_INSTALL : MSG_PRE_APPROVAL_REQUEST).sendToTarget();
3986         } else {
3987             root.destroy();
3988             root.dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
3989             root.maybeFinishChildSessions(INSTALL_FAILED_ABORTED, "User rejected permissions");
3990         }
3991     }
3992 
3993     public void open() throws IOException {
3994         activate();
3995         boolean wasPrepared;
3996         synchronized (mLock) {
3997             wasPrepared = mPrepared;
3998             if (!mPrepared) {
3999                 if (stageDir != null) {
4000                     prepareStageDir(stageDir);
4001                 } else if (params.isMultiPackage) {
4002                     // it's all ok
4003                 } else {
4004                     throw new IllegalArgumentException("stageDir must be set");
4005                 }
4006 
4007                 mPrepared = true;
4008             }
4009         }
4010 
4011         if (!wasPrepared) {
4012             mCallback.onSessionPrepared(this);
4013         }
4014     }
4015 
4016     private void activate() {
4017         if (mActiveCount.getAndIncrement() == 0) {
4018             mCallback.onSessionActiveChanged(this, true);
4019         }
4020     }
4021 
4022     @Override
4023     public void close() {
4024         closeInternal(true);
4025     }
4026 
4027     private void closeInternal(boolean checkCaller) {
4028         synchronized (mLock) {
4029             if (checkCaller) {
4030                 assertCallerIsOwnerOrRoot();
4031             }
4032         }
4033         deactivate();
4034     }
4035 
4036     private void deactivate() {
4037         int activeCount;
4038         synchronized (mLock) {
4039             activeCount = mActiveCount.decrementAndGet();
4040         }
4041         if (activeCount == 0) {
4042             mCallback.onSessionActiveChanged(this, false);
4043         }
4044     }
4045 
4046     /**
4047      * Calls dispatchSessionFinished() on all child sessions with the given error code and
4048      * error message to prevent orphaned child sessions.
4049      */
4050     private void maybeFinishChildSessions(int returnCode, String msg) {
4051         for (PackageInstallerSession child : getChildSessions()) {
4052             child.dispatchSessionFinished(returnCode, msg, null);
4053         }
4054     }
4055 
4056     private void assertNotChild(String cookie) {
4057         if (hasParentSessionId()) {
4058             throw new IllegalStateException(cookie + " can't be called on a child session, id="
4059                     + sessionId + " parentId=" + getParentSessionId());
4060         }
4061     }
4062 
4063     /**
4064      * Called when verification has completed. Now it is safe to clean up the session
4065      * if {@link #abandon()} has been called previously.
4066      *
4067      * @return True if this session has been abandoned.
4068      */
4069     private boolean dispatchPendingAbandonCallback() {
4070         final Runnable callback;
4071         synchronized (mLock) {
4072             if (!mStageDirInUse) {
4073                 return false;
4074             }
4075             mStageDirInUse = false;
4076             callback = mPendingAbandonCallback;
4077             mPendingAbandonCallback = null;
4078         }
4079         if (callback != null) {
4080             callback.run();
4081             return true;
4082         }
4083         return false;
4084     }
4085 
4086     @Override
4087     public void abandon() {
4088         final Runnable r;
4089         synchronized (mLock) {
4090             assertNotChild("abandon");
4091             assertCallerIsOwnerOrRootOrSystem();
4092             if (isInTerminalState()) {
4093                 // Finalized sessions have been properly cleaned up. No need to abandon them.
4094                 return;
4095             }
4096             mDestroyed = true;
4097             r = () -> {
4098                 assertNotLocked("abandonStaged");
4099                 if (isStaged() && isCommitted()) {
4100                     mStagingManager.abortCommittedSession(mStagedSession);
4101                 }
4102                 destroy();
4103                 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
4104                 maybeFinishChildSessions(INSTALL_FAILED_ABORTED,
4105                         "Session was abandoned because the parent session is abandoned");
4106             };
4107             if (mStageDirInUse) {
4108                 // Verification is ongoing, not safe to clean up the session yet.
4109                 mPendingAbandonCallback = r;
4110                 mCallback.onSessionChanged(this);
4111                 return;
4112             }
4113         }
4114 
4115         final long token = Binder.clearCallingIdentity();
4116         try {
4117             // This will call into StagingManager which might trigger external callbacks
4118             r.run();
4119         } finally {
4120             Binder.restoreCallingIdentity(token);
4121         }
4122     }
4123 
4124     @Override
4125     public boolean isMultiPackage() {
4126         return params.isMultiPackage;
4127     }
4128 
4129     @Override
4130     public boolean isStaged() {
4131         return params.isStaged;
4132     }
4133 
4134     @Override
4135     public int getInstallFlags() {
4136         return params.installFlags;
4137     }
4138 
4139     @Override
4140     public DataLoaderParamsParcel getDataLoaderParams() {
4141         mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
4142         return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
4143     }
4144 
4145     @Override
4146     public void addFile(int location, String name, long lengthBytes, byte[] metadata,
4147             byte[] signature) {
4148         mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
4149         if (!isDataLoaderInstallation()) {
4150             throw new IllegalStateException(
4151                     "Cannot add files to non-data loader installation session.");
4152         }
4153         if (isStreamingInstallation()) {
4154             if (location != LOCATION_DATA_APP) {
4155                 throw new IllegalArgumentException(
4156                         "Non-incremental installation only supports /data/app placement: " + name);
4157             }
4158         }
4159         if (metadata == null) {
4160             throw new IllegalArgumentException(
4161                     "DataLoader installation requires valid metadata: " + name);
4162         }
4163         // Use installer provided name for now; we always rename later
4164         if (!FileUtils.isValidExtFilename(name)) {
4165             throw new IllegalArgumentException("Invalid name: " + name);
4166         }
4167 
4168         synchronized (mLock) {
4169             assertCallerIsOwnerOrRoot();
4170             assertPreparedAndNotSealedLocked("addFile");
4171 
4172             if (!mFiles.add(new FileEntry(mFiles.size(),
4173                     new InstallationFile(location, name, lengthBytes, metadata, signature)))) {
4174                 throw new IllegalArgumentException("File already added: " + name);
4175             }
4176         }
4177     }
4178 
4179     @Override
4180     public void removeFile(int location, String name) {
4181         mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
4182         if (!isDataLoaderInstallation()) {
4183             throw new IllegalStateException(
4184                     "Cannot add files to non-data loader installation session.");
4185         }
4186         if (TextUtils.isEmpty(params.appPackageName)) {
4187             throw new IllegalStateException("Must specify package name to remove a split");
4188         }
4189 
4190         synchronized (mLock) {
4191             assertCallerIsOwnerOrRoot();
4192             assertPreparedAndNotSealedLocked("removeFile");
4193 
4194             if (!mFiles.add(new FileEntry(mFiles.size(),
4195                     new InstallationFile(location, getRemoveMarkerName(name), -1, null, null)))) {
4196                 throw new IllegalArgumentException("File already removed: " + name);
4197             }
4198         }
4199     }
4200 
4201     /**
4202      * Makes sure files are present in staging location.
4203      * @return if the image is ready for installation
4204      */
4205     @GuardedBy("mLock")
4206     private boolean prepareDataLoaderLocked()
4207             throws PackageManagerException {
4208         if (!isDataLoaderInstallation()) {
4209             return true;
4210         }
4211         if (mDataLoaderFinished) {
4212             return true;
4213         }
4214 
4215         final List<InstallationFileParcel> addedFiles = new ArrayList<>();
4216         final List<String> removedFiles = new ArrayList<>();
4217 
4218         final InstallationFile[] files = getInstallationFilesLocked();
4219         for (InstallationFile file : files) {
4220             if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
4221                 addedFiles.add(file.getData());
4222                 continue;
4223             }
4224             if (sRemovedFilter.accept(new File(this.stageDir, file.getName()))) {
4225                 String name = file.getName().substring(
4226                         0, file.getName().length() - REMOVE_MARKER_EXTENSION.length());
4227                 removedFiles.add(name);
4228             }
4229         }
4230 
4231         final DataLoaderParams params = this.params.dataLoaderParams;
4232         final boolean manualStartAndDestroy = !isIncrementalInstallation();
4233         final boolean systemDataLoader = isSystemDataLoaderInstallation();
4234         final IDataLoaderStatusListener statusListener = new IDataLoaderStatusListener.Stub() {
4235             @Override
4236             public void onStatusChanged(int dataLoaderId, int status) {
4237                 switch (status) {
4238                     case IDataLoaderStatusListener.DATA_LOADER_BINDING:
4239                     case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
4240                     case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
4241                         return;
4242                 }
4243 
4244                 if (mDestroyed || mDataLoaderFinished) {
4245                     switch (status) {
4246                         case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
4247                             if (systemDataLoader) {
4248                                 onSystemDataLoaderUnrecoverable();
4249                             }
4250                             return;
4251                     }
4252                     return;
4253                 }
4254                 try {
4255                     switch (status) {
4256                         case IDataLoaderStatusListener.DATA_LOADER_BOUND: {
4257                             if (manualStartAndDestroy) {
4258                                 FileSystemControlParcel control = new FileSystemControlParcel();
4259                                 control.callback = new FileSystemConnector(addedFiles);
4260                                 getDataLoader(dataLoaderId).create(dataLoaderId, params.getData(),
4261                                         control, this);
4262                             }
4263 
4264                             break;
4265                         }
4266                         case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
4267                             if (manualStartAndDestroy) {
4268                                 // IncrementalFileStorages will call start after all files are
4269                                 // created in IncFS.
4270                                 getDataLoader(dataLoaderId).start(dataLoaderId);
4271                             }
4272                             break;
4273                         }
4274                         case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
4275                             getDataLoader(dataLoaderId).prepareImage(
4276                                     dataLoaderId,
4277                                     addedFiles.toArray(
4278                                             new InstallationFileParcel[addedFiles.size()]),
4279                                     removedFiles.toArray(new String[removedFiles.size()]));
4280                             break;
4281                         }
4282                         case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
4283                             mDataLoaderFinished = true;
4284                             if (hasParentSessionId()) {
4285                                 mSessionProvider.getSession(
4286                                         getParentSessionId()).dispatchSessionSealed();
4287                             } else {
4288                                 dispatchSessionSealed();
4289                             }
4290                             if (manualStartAndDestroy) {
4291                                 getDataLoader(dataLoaderId).destroy(dataLoaderId);
4292                             }
4293                             break;
4294                         }
4295                         case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
4296                             mDataLoaderFinished = true;
4297                             dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
4298                                     "Failed to prepare image.");
4299                             if (manualStartAndDestroy) {
4300                                 getDataLoader(dataLoaderId).destroy(dataLoaderId);
4301                             }
4302                             break;
4303                         }
4304                         case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
4305                             // Don't fail or commit the session. Allow caller to commit again.
4306                             sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
4307                                     "DataLoader unavailable");
4308                             break;
4309                         }
4310                         case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
4311                             throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
4312                                     "DataLoader reported unrecoverable failure.");
4313                     }
4314                 } catch (PackageManagerException e) {
4315                     mDataLoaderFinished = true;
4316                     dispatchSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
4317                 } catch (RemoteException e) {
4318                     // In case of streaming failure we don't want to fail or commit the session.
4319                     // Just return from this method and allow caller to commit again.
4320                     sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
4321                             e.getMessage());
4322                 }
4323             }
4324         };
4325 
4326         if (!manualStartAndDestroy) {
4327             final PerUidReadTimeouts[] perUidReadTimeouts =
4328                     mPm.getPerUidReadTimeouts(mPm.snapshotComputer());
4329 
4330             final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams();
4331             healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
4332             healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
4333             healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
4334 
4335             final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
4336                 @Override
4337                 public void onHealthStatus(int storageId, int status) {
4338                     if (mDestroyed || mDataLoaderFinished) {
4339                         return;
4340                     }
4341 
4342                     switch (status) {
4343                         case IStorageHealthListener.HEALTH_STATUS_OK:
4344                             break;
4345                         case IStorageHealthListener.HEALTH_STATUS_READS_PENDING:
4346                         case IStorageHealthListener.HEALTH_STATUS_BLOCKED:
4347                             if (systemDataLoader) {
4348                                 // It's OK for ADB data loader to wait for pages.
4349                                 break;
4350                             }
4351                             // fallthrough
4352                         case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
4353                             // Even ADB installation can't wait for missing pages for too long.
4354                             mDataLoaderFinished = true;
4355                             dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
4356                                     "Image is missing pages required for installation.");
4357                             break;
4358                     }
4359                 }
4360             };
4361 
4362             try {
4363                 final PackageInfo pkgInfo = mPm.snapshotComputer()
4364                         .getPackageInfo(this.params.appPackageName, 0, userId);
4365                 final File inheritedDir =
4366                         (pkgInfo != null && pkgInfo.applicationInfo != null) ? new File(
4367                                 pkgInfo.applicationInfo.getCodePath()).getParentFile() : null;
4368 
4369                 if (mIncrementalFileStorages == null) {
4370                     mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
4371                             stageDir, inheritedDir, params, statusListener, healthCheckParams,
4372                             healthListener, addedFiles, perUidReadTimeouts,
4373                             new IPackageLoadingProgressCallback.Stub() {
4374                                 @Override
4375                                 public void onPackageLoadingProgressChanged(float progress) {
4376                                     synchronized (mProgressLock) {
4377                                         mIncrementalProgress = progress;
4378                                         computeProgressLocked(true);
4379                                     }
4380                                 }
4381                             });
4382                 } else {
4383                     // Retrying commit.
4384                     mIncrementalFileStorages.startLoading(params, statusListener, healthCheckParams,
4385                             healthListener, perUidReadTimeouts);
4386                 }
4387                 return false;
4388             } catch (IOException e) {
4389                 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
4390                         e.getCause());
4391             }
4392         }
4393 
4394         final long bindDelayMs = 0;
4395         if (!getDataLoaderManager().bindToDataLoader(sessionId, params.getData(), bindDelayMs,
4396                 statusListener)) {
4397             throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
4398                     "Failed to initialize data loader");
4399         }
4400 
4401         return false;
4402     }
4403 
4404     private DataLoaderManager getDataLoaderManager() throws PackageManagerException {
4405         DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
4406         if (dataLoaderManager == null) {
4407             throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
4408                     "Failed to find data loader manager service");
4409         }
4410         return dataLoaderManager;
4411     }
4412 
4413     private IDataLoader getDataLoader(int dataLoaderId) throws PackageManagerException {
4414         IDataLoader dataLoader = getDataLoaderManager().getDataLoader(dataLoaderId);
4415         if (dataLoader == null) {
4416             throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
4417                     "Failure to obtain data loader");
4418         }
4419         return dataLoader;
4420     }
4421 
4422     private void dispatchSessionValidationFailure(int error, String detailMessage) {
4423         mHandler.obtainMessage(MSG_SESSION_VALIDATION_FAILURE, error, -1,
4424                 detailMessage).sendToTarget();
4425     }
4426 
4427     @GuardedBy("mLock")
4428     private int[] getChildSessionIdsLocked() {
4429         int size = mChildSessions.size();
4430         if (size == 0) {
4431             return EMPTY_CHILD_SESSION_ARRAY;
4432         }
4433         final int[] childSessionIds = new int[size];
4434         for (int i = 0; i < size; ++i) {
4435             childSessionIds[i] = mChildSessions.keyAt(i);
4436         }
4437         return childSessionIds;
4438     }
4439 
4440     @Override
4441     public int[] getChildSessionIds() {
4442         synchronized (mLock) {
4443             return getChildSessionIdsLocked();
4444         }
4445     }
4446 
4447     private boolean canBeAddedAsChild(int parentCandidate) {
4448         synchronized (mLock) {
4449             return (!hasParentSessionId() || mParentSessionId == parentCandidate)
4450                     && !isCommitted()
4451                     && !mDestroyed;
4452         }
4453     }
4454 
4455     private void acquireTransactionLock() {
4456         if (!mTransactionLock.compareAndSet(false, true)) {
4457             throw new UnsupportedOperationException("Concurrent access not supported");
4458         }
4459     }
4460 
4461     private void releaseTransactionLock() {
4462         mTransactionLock.compareAndSet(true, false);
4463     }
4464 
4465     @Override
4466     public void addChildSessionId(int childSessionId) {
4467         if (!params.isMultiPackage) {
4468             throw new IllegalStateException("Single-session " + sessionId + " can't have child.");
4469         }
4470 
4471         final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
4472         if (childSession == null) {
4473             throw new IllegalStateException("Unable to add child session " + childSessionId
4474                     + " as it does not exist.");
4475         }
4476         if (childSession.params.isMultiPackage) {
4477             throw new IllegalStateException("Multi-session " + childSessionId
4478                     + " can't be a child.");
4479         }
4480         if (params.isStaged != childSession.params.isStaged) {
4481             throw new IllegalStateException("Multipackage Inconsistency: session "
4482                     + childSession.sessionId + " and session " + sessionId
4483                     + " have inconsistent staged settings");
4484         }
4485         if (params.getEnableRollback() != childSession.params.getEnableRollback()) {
4486             throw new IllegalStateException("Multipackage Inconsistency: session "
4487                     + childSession.sessionId + " and session " + sessionId
4488                     + " have inconsistent rollback settings");
4489         }
4490         boolean hasAPK = containsApkSession() || !childSession.isApexSession();
4491         boolean hasAPEX = sessionContains(s -> s.isApexSession()) || childSession.isApexSession();
4492         if (!params.isStaged && hasAPK && hasAPEX) {
4493             throw new IllegalStateException("Mix of APK and APEX is not supported for "
4494                     + "non-staged multi-package session");
4495         }
4496 
4497         try {
4498             acquireTransactionLock();
4499             childSession.acquireTransactionLock();
4500 
4501             if (!childSession.canBeAddedAsChild(sessionId)) {
4502                 throw new IllegalStateException("Unable to add child session " + childSessionId
4503                         + " as it is in an invalid state.");
4504             }
4505             synchronized (mLock) {
4506                 assertCallerIsOwnerOrRoot();
4507                 assertPreparedAndNotSealedLocked("addChildSessionId");
4508 
4509                 final int indexOfSession = mChildSessions.indexOfKey(childSessionId);
4510                 if (indexOfSession >= 0) {
4511                     return;
4512                 }
4513                 childSession.setParentSessionId(this.sessionId);
4514                 mChildSessions.put(childSessionId, childSession);
4515             }
4516         } finally {
4517             releaseTransactionLock();
4518             childSession.releaseTransactionLock();
4519         }
4520     }
4521 
4522     @Override
4523     public void removeChildSessionId(int sessionId) {
4524         synchronized (mLock) {
4525             assertCallerIsOwnerOrRoot();
4526             assertPreparedAndNotSealedLocked("removeChildSessionId");
4527 
4528             final int indexOfSession = mChildSessions.indexOfKey(sessionId);
4529             if (indexOfSession < 0) {
4530                 // not added in the first place; no-op
4531                 return;
4532             }
4533             PackageInstallerSession session = mChildSessions.valueAt(indexOfSession);
4534             try {
4535                 acquireTransactionLock();
4536                 session.acquireTransactionLock();
4537                 session.setParentSessionId(SessionInfo.INVALID_ID);
4538                 mChildSessions.removeAt(indexOfSession);
4539             } finally {
4540                 releaseTransactionLock();
4541                 session.releaseTransactionLock();
4542             }
4543         }
4544     }
4545 
4546     /**
4547      * Sets the parent session ID if not already set.
4548      * If {@link SessionInfo#INVALID_ID} is passed, it will be unset.
4549      */
4550     void setParentSessionId(int parentSessionId) {
4551         synchronized (mLock) {
4552             if (parentSessionId != SessionInfo.INVALID_ID
4553                     && mParentSessionId != SessionInfo.INVALID_ID) {
4554                 throw new IllegalStateException("The parent of " + sessionId + " is" + " already"
4555                         + "set to " + mParentSessionId);
4556             }
4557             this.mParentSessionId = parentSessionId;
4558         }
4559     }
4560 
4561     boolean hasParentSessionId() {
4562         synchronized (mLock) {
4563             return mParentSessionId != SessionInfo.INVALID_ID;
4564         }
4565     }
4566 
4567     @Override
4568     public int getParentSessionId() {
4569         synchronized (mLock) {
4570             return mParentSessionId;
4571         }
4572     }
4573 
4574     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
4575         sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
4576 
4577         synchronized (mLock) {
4578             mFinalStatus = returnCode;
4579             mFinalMessage = msg;
4580         }
4581 
4582         final boolean success = (returnCode == INSTALL_SUCCEEDED);
4583 
4584         // Send broadcast to default launcher only if it's a new install
4585         // TODO(b/144270665): Secure the usage of this broadcast.
4586         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
4587         if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
4588             mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);
4589         }
4590 
4591         mCallback.onSessionFinished(this, success);
4592         if (isDataLoaderInstallation()) {
4593             logDataLoaderInstallationSession(returnCode);
4594         }
4595     }
4596 
4597     private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
4598         final IntentSender statusReceiver = getRemoteStatusReceiver();
4599         if (statusReceiver != null) {
4600             // Execute observer.onPackageInstalled on different thread as we don't want callers
4601             // inside the system server have to worry about catching the callbacks while they are
4602             // calling into the session
4603             final SomeArgs args = SomeArgs.obtain();
4604             args.arg1 = getPackageName();
4605             args.arg2 = msg;
4606             args.arg3 = extras;
4607             args.arg4 = statusReceiver;
4608             args.argi1 = returnCode;
4609             args.argi2 = isPreapprovalRequested() && !isCommitted() ? 1 : 0;
4610             mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
4611         }
4612     }
4613 
4614     private void dispatchSessionPreappoved() {
4615         final IntentSender target = getRemoteStatusReceiver();
4616         final Intent intent = new Intent();
4617         intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
4618         intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
4619         intent.putExtra(PackageInstaller.EXTRA_PRE_APPROVAL, true);
4620         try {
4621             final BroadcastOptions options = BroadcastOptions.makeBasic();
4622             options.setPendingIntentBackgroundActivityLaunchAllowed(false);
4623             target.sendIntent(mContext, 0 /* code */, intent, null /* onFinished */,
4624                     null /* handler */, null /* requiredPermission */, options.toBundle());
4625         } catch (IntentSender.SendIntentException ignored) {
4626         }
4627     }
4628 
4629     @Override
4630     public void requestUserPreapproval(@NonNull PreapprovalDetails details,
4631             @NonNull IntentSender statusReceiver) {
4632         validatePreapprovalRequest(details, statusReceiver);
4633 
4634         if (!mPm.isPreapprovalRequestAvailable()) {
4635             sendUpdateToRemoteStatusReceiver(INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE,
4636                     "Request user pre-approval is currently not available.", null /* extras */);
4637             return;
4638         }
4639 
4640         dispatchPreapprovalRequest();
4641     }
4642 
4643     /**
4644      * Validates whether the necessary information (e.g., PreapprovalDetails) are provided.
4645      */
4646     private void validatePreapprovalRequest(@NonNull PreapprovalDetails details,
4647             @NonNull IntentSender statusReceiver) {
4648         assertCallerIsOwnerOrRoot();
4649         if (isMultiPackage()) {
4650             throw new IllegalStateException(
4651                     "Session " + sessionId + " is a parent of multi-package session and "
4652                             + "requestUserPreapproval on the parent session isn't supported.");
4653         }
4654 
4655         synchronized (mLock) {
4656             assertPreparedAndNotSealedLocked("request of session " + sessionId);
4657             mPreapprovalDetails = details;
4658             setRemoteStatusReceiver(statusReceiver);
4659         }
4660     }
4661 
4662     private void dispatchPreapprovalRequest() {
4663         synchronized (mLock) {
4664             assertPreparedAndNotPreapprovalRequestedLocked("dispatchPreapprovalRequest");
4665         }
4666 
4667         // Mark this session are pre-approval requested, and ready to progress to the next phase.
4668         markAsPreapprovalRequested();
4669 
4670         mHandler.obtainMessage(MSG_PRE_APPROVAL_REQUEST).sendToTarget();
4671     }
4672 
4673     /**
4674      * Marks this session as pre-approval requested, and prevents further related modification.
4675      */
4676     private void markAsPreapprovalRequested() {
4677         mPreapprovalRequested.set(true);
4678     }
4679 
4680     @Override
4681     public boolean isApplicationEnabledSettingPersistent() {
4682         return params.applicationEnabledSettingPersistent;
4683     }
4684 
4685     @Override
4686     public boolean isRequestUpdateOwnership() {
4687         return (params.installFlags & PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP) != 0;
4688     }
4689 
4690     void setSessionReady() {
4691         synchronized (mLock) {
4692             // Do not allow destroyed/failed session to change state
4693             if (mDestroyed || mSessionFailed) return;
4694             mSessionReady = true;
4695             mSessionApplied = false;
4696             mSessionFailed = false;
4697             mSessionErrorCode = PackageManager.INSTALL_UNKNOWN;
4698             mSessionErrorMessage = "";
4699         }
4700         mCallback.onSessionChanged(this);
4701     }
4702 
4703     void setSessionFailed(int errorCode, String errorMessage) {
4704         synchronized (mLock) {
4705             // Do not allow destroyed/failed session to change state
4706             if (mDestroyed || mSessionFailed) return;
4707             mSessionReady = false;
4708             mSessionApplied = false;
4709             mSessionFailed = true;
4710             mSessionErrorCode = errorCode;
4711             mSessionErrorMessage = errorMessage;
4712             Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
4713         }
4714         destroy();
4715         mCallback.onSessionChanged(this);
4716     }
4717 
4718     private void setSessionApplied() {
4719         synchronized (mLock) {
4720             // Do not allow destroyed/failed session to change state
4721             if (mDestroyed || mSessionFailed) return;
4722             mSessionReady = false;
4723             mSessionApplied = true;
4724             mSessionFailed = false;
4725             mSessionErrorCode = INSTALL_SUCCEEDED;
4726             mSessionErrorMessage = "";
4727             Slog.d(TAG, "Marking session " + sessionId + " as applied");
4728         }
4729         destroy();
4730         mCallback.onSessionChanged(this);
4731     }
4732 
4733     /** {@hide} */
4734     boolean isSessionReady() {
4735         synchronized (mLock) {
4736             return mSessionReady;
4737         }
4738     }
4739 
4740     /** {@hide} */
4741     boolean isSessionApplied() {
4742         synchronized (mLock) {
4743             return mSessionApplied;
4744         }
4745     }
4746 
4747     /** {@hide} */
4748     boolean isSessionFailed() {
4749         synchronized (mLock) {
4750             return mSessionFailed;
4751         }
4752     }
4753 
4754     /** {@hide} */
4755     int getSessionErrorCode() {
4756         synchronized (mLock) {
4757             return mSessionErrorCode;
4758         }
4759     }
4760 
4761     /** {@hide} */
4762     String getSessionErrorMessage() {
4763         synchronized (mLock) {
4764             return mSessionErrorMessage;
4765         }
4766     }
4767 
4768     /**
4769      * Free up storage used by this session and its children.
4770      * Must not be called on a child session.
4771      */
4772     private void destroy() {
4773         // TODO(b/173194203): destroy() is called indirectly by
4774         //  PackageInstallerService#restoreAndApplyStagedSessionIfNeeded on an orphan child session.
4775         //  Enable this assertion when we figure out a better way to clean up orphan sessions.
4776         // assertNotChild("destroy");
4777 
4778         // TODO(b/173194203): destroyInternal() should be used by destroy() only.
4779         //  For the sake of consistency, a session should be destroyed as a whole. The caller
4780         //  should always call destroy() for cleanup without knowing it has child sessions or not.
4781         destroyInternal();
4782         for (PackageInstallerSession child : getChildSessions()) {
4783             child.destroyInternal();
4784         }
4785     }
4786 
4787     /**
4788      * Free up storage used by this session.
4789      */
4790     private void destroyInternal() {
4791         final IncrementalFileStorages incrementalFileStorages;
4792         synchronized (mLock) {
4793             mSealed = true;
4794             if (!params.isStaged) {
4795                 mDestroyed = true;
4796             }
4797             // Force shut down all bridges
4798             for (RevocableFileDescriptor fd : mFds) {
4799                 fd.revoke();
4800             }
4801             for (FileBridge bridge : mBridges) {
4802                 bridge.forceClose();
4803             }
4804             incrementalFileStorages = mIncrementalFileStorages;
4805             mIncrementalFileStorages = null;
4806         }
4807         try {
4808             if (incrementalFileStorages != null) {
4809                 incrementalFileStorages.cleanUpAndMarkComplete();
4810             }
4811             if (stageDir != null) {
4812                 final String tempPackageName = stageDir.getName();
4813                 mInstaller.rmPackageDir(tempPackageName, stageDir.getAbsolutePath());
4814             }
4815         } catch (InstallerException ignored) {
4816         }
4817     }
4818 
4819     void dump(IndentingPrintWriter pw) {
4820         synchronized (mLock) {
4821             dumpLocked(pw);
4822         }
4823     }
4824 
4825     @GuardedBy("mLock")
4826     private void dumpLocked(IndentingPrintWriter pw) {
4827         pw.println("Session " + sessionId + ":");
4828         pw.increaseIndent();
4829 
4830         pw.printPair("userId", userId);
4831         pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
4832         pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName);
4833         pw.printPair("installerPackageName", mInstallSource.mInstallerPackageName);
4834         pw.printPair("installInitiatingPackageName", mInstallSource.mInitiatingPackageName);
4835         pw.printPair("installOriginatingPackageName", mInstallSource.mOriginatingPackageName);
4836         pw.printPair("mInstallerUid", mInstallerUid);
4837         pw.printPair("createdMillis", createdMillis);
4838         pw.printPair("updatedMillis", updatedMillis);
4839         pw.printPair("committedMillis", committedMillis);
4840         pw.printPair("stageDir", stageDir);
4841         pw.printPair("stageCid", stageCid);
4842         pw.println();
4843 
4844         params.dump(pw);
4845 
4846         final float clientProgress;
4847         final float progress;
4848         synchronized (mProgressLock) {
4849             clientProgress = mClientProgress;
4850             progress = mProgress;
4851         }
4852         pw.printPair("mClientProgress", clientProgress);
4853         pw.printPair("mProgress", progress);
4854         pw.printPair("mCommitted", mCommitted);
4855         pw.printPair("mPreapprovalRequested", mPreapprovalRequested);
4856         pw.printPair("mSealed", mSealed);
4857         pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
4858         pw.printPair("mStageDirInUse", mStageDirInUse);
4859         pw.printPair("mDestroyed", mDestroyed);
4860         pw.printPair("mFds", mFds.size());
4861         pw.printPair("mBridges", mBridges.size());
4862         pw.printPair("mFinalStatus", mFinalStatus);
4863         pw.printPair("mFinalMessage", mFinalMessage);
4864         pw.printPair("params.isMultiPackage", params.isMultiPackage);
4865         pw.printPair("params.isStaged", params.isStaged);
4866         pw.printPair("mParentSessionId", mParentSessionId);
4867         pw.printPair("mChildSessionIds", getChildSessionIdsLocked());
4868         pw.printPair("mSessionApplied", mSessionApplied);
4869         pw.printPair("mSessionFailed", mSessionFailed);
4870         pw.printPair("mSessionReady", mSessionReady);
4871         pw.printPair("mSessionErrorCode", mSessionErrorCode);
4872         pw.printPair("mSessionErrorMessage", mSessionErrorMessage);
4873         pw.printPair("mPreapprovalDetails", mPreapprovalDetails);
4874         pw.println();
4875 
4876         pw.decreaseIndent();
4877     }
4878 
4879     /**
4880      * This method doesn't change internal states and is safe to call outside the lock.
4881      */
4882     private static void sendOnUserActionRequired(Context context, IntentSender target,
4883             int sessionId, Intent intent) {
4884         final Intent fillIn = new Intent();
4885         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
4886         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
4887         fillIn.putExtra(PackageInstaller.EXTRA_PRE_APPROVAL,
4888                 PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction()));
4889         fillIn.putExtra(Intent.EXTRA_INTENT, intent);
4890         try {
4891             final BroadcastOptions options = BroadcastOptions.makeBasic();
4892             options.setPendingIntentBackgroundActivityLaunchAllowed(false);
4893             target.sendIntent(context, 0, fillIn, null /* onFinished */,
4894                     null /* handler */, null /* requiredPermission */, options.toBundle());
4895         } catch (IntentSender.SendIntentException ignored) {
4896         }
4897     }
4898 
4899     /**
4900      * This method doesn't change internal states and is safe to call outside the lock.
4901      */
4902     private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
4903             boolean showNotification, int userId, String basePackageName, int returnCode,
4904             boolean isPreapproval, String msg, Bundle extras) {
4905         if (INSTALL_SUCCEEDED == returnCode && showNotification) {
4906             boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
4907             Notification notification = PackageInstallerService.buildSuccessNotification(context,
4908                     getDeviceOwnerInstalledPackageMsg(context, update),
4909                     basePackageName,
4910                     userId);
4911             if (notification != null) {
4912                 NotificationManager notificationManager = (NotificationManager)
4913                         context.getSystemService(Context.NOTIFICATION_SERVICE);
4914                 notificationManager.notify(basePackageName,
4915                         SystemMessageProto.SystemMessage.NOTE_PACKAGE_STATE,
4916                         notification);
4917             }
4918         }
4919         final Intent fillIn = new Intent();
4920         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
4921         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
4922         fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
4923                 PackageManager.installStatusToPublicStatus(returnCode));
4924         fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
4925                 PackageManager.installStatusToString(returnCode, msg));
4926         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
4927         fillIn.putExtra(PackageInstaller.EXTRA_PRE_APPROVAL, isPreapproval);
4928         if (extras != null) {
4929             final String existing = extras.getString(
4930                     PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
4931             if (!TextUtils.isEmpty(existing)) {
4932                 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
4933             }
4934         }
4935         try {
4936             final BroadcastOptions options = BroadcastOptions.makeBasic();
4937             options.setPendingIntentBackgroundActivityLaunchAllowed(false);
4938             target.sendIntent(context, 0, fillIn, null /* onFinished */,
4939                     null /* handler */, null /* requiredPermission */, options.toBundle());
4940         } catch (IntentSender.SendIntentException ignored) {
4941         }
4942     }
4943 
4944     private static String getDeviceOwnerInstalledPackageMsg(Context context, boolean update) {
4945         DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
4946         return update
4947                 ? dpm.getResources().getString(PACKAGE_UPDATED_BY_DO,
4948                     () -> context.getString(R.string.package_updated_device_owner))
4949                 : dpm.getResources().getString(PACKAGE_INSTALLED_BY_DO,
4950                     () -> context.getString(R.string.package_installed_device_owner));
4951     }
4952 
4953     /**
4954      * This method doesn't change internal states and is safe to call outside the lock.
4955      */
4956     private static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
4957             @Nullable String cause) {
4958         if (target == null) {
4959             Slog.e(TAG, "Missing receiver for pending streaming status.");
4960             return;
4961         }
4962 
4963         final Intent intent = new Intent();
4964         intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
4965         intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING);
4966         if (!TextUtils.isEmpty(cause)) {
4967             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
4968                     "Staging Image Not Ready [" + cause + "]");
4969         } else {
4970             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
4971         }
4972         try {
4973             final BroadcastOptions options = BroadcastOptions.makeBasic();
4974             options.setPendingIntentBackgroundActivityLaunchAllowed(false);
4975             target.sendIntent(context, 0, intent, null /* onFinished */,
4976                     null /* handler */, null /* requiredPermission */, options.toBundle());
4977         } catch (IntentSender.SendIntentException ignored) {
4978         }
4979     }
4980 
4981     private static void writePermissionsLocked(@NonNull TypedXmlSerializer out,
4982             @NonNull SessionParams params) throws IOException {
4983         var permissionStates = params.getPermissionStates();
4984         for (int index = 0; index < permissionStates.size(); index++) {
4985             var permissionName = permissionStates.keyAt(index);
4986             var state = permissionStates.valueAt(index);
4987             String tag = state == SessionParams.PERMISSION_STATE_GRANTED ? TAG_GRANT_PERMISSION
4988                     : TAG_DENY_PERMISSION;
4989             out.startTag(null, tag);
4990             writeStringAttribute(out, ATTR_NAME, permissionName);
4991             out.endTag(null, tag);
4992         }
4993     }
4994 
4995     private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull TypedXmlSerializer out,
4996             @Nullable List<String> whitelistedRestrictedPermissions) throws IOException {
4997         if (whitelistedRestrictedPermissions != null) {
4998             final int permissionCount = whitelistedRestrictedPermissions.size();
4999             for (int i = 0; i < permissionCount; i++) {
5000                 out.startTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION);
5001                 writeStringAttribute(out, ATTR_NAME, whitelistedRestrictedPermissions.get(i));
5002                 out.endTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION);
5003             }
5004         }
5005     }
5006 
5007     private static void writeAutoRevokePermissionsMode(@NonNull TypedXmlSerializer out, int mode)
5008             throws IOException {
5009         out.startTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
5010         out.attributeInt(null, ATTR_MODE, mode);
5011         out.endTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
5012     }
5013 
5014 
5015     private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
5016         return new File(sessionsDir, "app_icon." + sessionId + ".png");
5017     }
5018 
5019     /**
5020      * Write this session to a {@link TypedXmlSerializer}.
5021      *
5022      * @param out Where to write the session to
5023      * @param sessionsDir The directory containing the sessions
5024      */
5025     void write(@NonNull TypedXmlSerializer out, @NonNull File sessionsDir) throws IOException {
5026         synchronized (mLock) {
5027             if (mDestroyed && !params.isStaged) {
5028                 return;
5029             }
5030 
5031             out.startTag(null, TAG_SESSION);
5032 
5033             out.attributeInt(null, ATTR_SESSION_ID, sessionId);
5034             out.attributeInt(null, ATTR_USER_ID, userId);
5035             writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
5036                     mInstallSource.mInstallerPackageName);
5037             out.attributeInt(null, ATTR_INSTALLER_PACKAGE_UID, mInstallSource.mInstallerPackageUid);
5038             writeStringAttribute(out, ATTR_UPDATE_OWNER_PACKAGE_NAME,
5039                     mInstallSource.mUpdateOwnerPackageName);
5040             writeStringAttribute(out, ATTR_INSTALLER_ATTRIBUTION_TAG,
5041                     mInstallSource.mInstallerAttributionTag);
5042             out.attributeInt(null, ATTR_INSTALLER_UID, mInstallerUid);
5043             writeStringAttribute(out, ATTR_INITIATING_PACKAGE_NAME,
5044                     mInstallSource.mInitiatingPackageName);
5045             writeStringAttribute(out, ATTR_ORIGINATING_PACKAGE_NAME,
5046                     mInstallSource.mOriginatingPackageName);
5047             out.attributeLong(null, ATTR_CREATED_MILLIS, createdMillis);
5048             out.attributeLong(null, ATTR_UPDATED_MILLIS, updatedMillis);
5049             out.attributeLong(null, ATTR_COMMITTED_MILLIS, committedMillis);
5050             if (stageDir != null) {
5051                 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
5052                         stageDir.getAbsolutePath());
5053             }
5054             if (stageCid != null) {
5055                 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
5056             }
5057             writeBooleanAttribute(out, ATTR_PREPARED, mPrepared);
5058             writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
5059             writeBooleanAttribute(out, ATTR_DESTROYED, mDestroyed);
5060             writeBooleanAttribute(out, ATTR_SEALED, mSealed);
5061 
5062             writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
5063             writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
5064             writeBooleanAttribute(out, ATTR_IS_READY, mSessionReady);
5065             writeBooleanAttribute(out, ATTR_IS_FAILED, mSessionFailed);
5066             writeBooleanAttribute(out, ATTR_IS_APPLIED, mSessionApplied);
5067             out.attributeInt(null, ATTR_PACKAGE_SOURCE, params.packageSource);
5068             out.attributeInt(null, ATTR_SESSION_ERROR_CODE, mSessionErrorCode);
5069             writeStringAttribute(out, ATTR_SESSION_ERROR_MESSAGE, mSessionErrorMessage);
5070             // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
5071             //                       we've read all sessions.
5072             out.attributeInt(null, ATTR_PARENT_SESSION_ID, mParentSessionId);
5073             out.attributeInt(null, ATTR_MODE, params.mode);
5074             out.attributeInt(null, ATTR_INSTALL_FLAGS, params.installFlags);
5075             out.attributeInt(null, ATTR_INSTALL_LOCATION, params.installLocation);
5076             out.attributeLong(null, ATTR_SIZE_BYTES, params.sizeBytes);
5077             writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
5078             writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
5079             writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
5080             out.attributeInt(null, ATTR_ORIGINATING_UID, params.originatingUid);
5081             writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
5082             writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
5083             writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
5084             out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason);
5085             writeBooleanAttribute(out, ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT,
5086                     params.applicationEnabledSettingPersistent);
5087 
5088             final boolean isDataLoader = params.dataLoaderParams != null;
5089             writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader);
5090             if (isDataLoader) {
5091                 out.attributeInt(null, ATTR_DATALOADER_TYPE, params.dataLoaderParams.getType());
5092                 writeStringAttribute(out, ATTR_DATALOADER_PACKAGE_NAME,
5093                         params.dataLoaderParams.getComponentName().getPackageName());
5094                 writeStringAttribute(out, ATTR_DATALOADER_CLASS_NAME,
5095                         params.dataLoaderParams.getComponentName().getClassName());
5096                 writeStringAttribute(out, ATTR_DATALOADER_ARGUMENTS,
5097                         params.dataLoaderParams.getArguments());
5098             }
5099 
5100             writePermissionsLocked(out, params);
5101             writeWhitelistedRestrictedPermissionsLocked(out,
5102                     params.whitelistedRestrictedPermissions);
5103             writeAutoRevokePermissionsMode(out, params.autoRevokePermissionsMode);
5104 
5105             // Persist app icon if changed since last written
5106             File appIconFile = buildAppIconFile(sessionId, sessionsDir);
5107             if (params.appIcon == null && appIconFile.exists()) {
5108                 appIconFile.delete();
5109             } else if (params.appIcon != null
5110                     && appIconFile.lastModified() != params.appIconLastModified) {
5111                 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
5112                 FileOutputStream os = null;
5113                 try {
5114                     os = new FileOutputStream(appIconFile);
5115                     params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
5116                 } catch (IOException e) {
5117                     Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
5118                 } finally {
5119                     IoUtils.closeQuietly(os);
5120                 }
5121 
5122                 params.appIconLastModified = appIconFile.lastModified();
5123             }
5124             final int[] childSessionIds = getChildSessionIdsLocked();
5125             for (int childSessionId : childSessionIds) {
5126                 out.startTag(null, TAG_CHILD_SESSION);
5127                 out.attributeInt(null, ATTR_SESSION_ID, childSessionId);
5128                 out.endTag(null, TAG_CHILD_SESSION);
5129             }
5130 
5131             final InstallationFile[] files = getInstallationFilesLocked();
5132             for (InstallationFile file : files) {
5133                 out.startTag(null, TAG_SESSION_FILE);
5134                 out.attributeInt(null, ATTR_LOCATION, file.getLocation());
5135                 writeStringAttribute(out, ATTR_NAME, file.getName());
5136                 out.attributeLong(null, ATTR_LENGTH_BYTES, file.getLengthBytes());
5137                 writeByteArrayAttribute(out, ATTR_METADATA, file.getMetadata());
5138                 writeByteArrayAttribute(out, ATTR_SIGNATURE, file.getSignature());
5139                 out.endTag(null, TAG_SESSION_FILE);
5140             }
5141 
5142             for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
5143                 final String fileName = mChecksums.keyAt(i);
5144                 final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
5145                 final Checksum[] checksums = perFileChecksum.getChecksums();
5146                 for (Checksum checksum : checksums) {
5147                     out.startTag(null, TAG_SESSION_CHECKSUM);
5148                     writeStringAttribute(out, ATTR_NAME, fileName);
5149                     out.attributeInt(null, ATTR_CHECKSUM_KIND, checksum.getType());
5150                     writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE, checksum.getValue());
5151                     out.endTag(null, TAG_SESSION_CHECKSUM);
5152                 }
5153             }
5154             for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
5155                 final String fileName = mChecksums.keyAt(i);
5156                 final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
5157                 final byte[] signature = perFileChecksum.getSignature();
5158                 if (signature == null || signature.length == 0) {
5159                     continue;
5160                 }
5161                 out.startTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
5162                 writeStringAttribute(out, ATTR_NAME, fileName);
5163                 writeByteArrayAttribute(out, ATTR_SIGNATURE, signature);
5164                 out.endTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
5165             }
5166 
5167         }
5168 
5169         out.endTag(null, TAG_SESSION);
5170     }
5171 
5172     // Validity check to be performed when the session is restored from an external file. Only one
5173     // of the session states should be true, or none of them.
5174     private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
5175                                                      boolean isFailed) {
5176         return (!isReady && !isApplied && !isFailed)
5177                 || (isReady && !isApplied && !isFailed)
5178                 || (!isReady && isApplied && !isFailed)
5179                 || (!isReady && !isApplied && isFailed);
5180     }
5181 
5182     /**
5183      * Read new session from a {@link TypedXmlPullParser xml description} and create it.
5184      *
5185      * @param in The source of the description
5186      * @param callback Callback the session uses to notify about changes of it's state
5187      * @param context Context to be used by the session
5188      * @param pm PackageManager to use by the session
5189      * @param installerThread Thread to be used for callbacks of this session
5190      * @param sessionsDir The directory the sessions are stored in
5191      *
5192      * @param sessionProvider to get the other PackageInstallerSession instance by sessionId.
5193      * @return The newly created session
5194      */
5195     public static PackageInstallerSession readFromXml(@NonNull TypedXmlPullParser in,
5196             @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
5197             @NonNull PackageManagerService pm, Looper installerThread,
5198             @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
5199             @NonNull PackageSessionProvider sessionProvider,
5200             @NonNull SilentUpdatePolicy silentUpdatePolicy)
5201             throws IOException, XmlPullParserException {
5202         final int sessionId = in.getAttributeInt(null, ATTR_SESSION_ID);
5203         final int userId = in.getAttributeInt(null, ATTR_USER_ID);
5204         final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
5205         final int installPackageUid = in.getAttributeInt(null, ATTR_INSTALLER_PACKAGE_UID,
5206                 INVALID_UID);
5207         final String updateOwnerPackageName = readStringAttribute(in,
5208                 ATTR_UPDATE_OWNER_PACKAGE_NAME);
5209         final String installerAttributionTag = readStringAttribute(in,
5210                 ATTR_INSTALLER_ATTRIBUTION_TAG);
5211         final int installerUid = in.getAttributeInt(null, ATTR_INSTALLER_UID, pm.snapshotComputer()
5212                 .getPackageUid(installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
5213                         userId));
5214         final String installInitiatingPackageName =
5215                 readStringAttribute(in, ATTR_INITIATING_PACKAGE_NAME);
5216         final String installOriginatingPackageName =
5217                 readStringAttribute(in, ATTR_ORIGINATING_PACKAGE_NAME);
5218         final long createdMillis = in.getAttributeLong(null, ATTR_CREATED_MILLIS);
5219         long updatedMillis = in.getAttributeLong(null, ATTR_UPDATED_MILLIS);
5220         final long committedMillis = in.getAttributeLong(null, ATTR_COMMITTED_MILLIS, 0L);
5221         final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
5222         final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
5223         final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
5224         final boolean prepared = in.getAttributeBoolean(null, ATTR_PREPARED, true);
5225         final boolean committed = in.getAttributeBoolean(null, ATTR_COMMITTED, false);
5226         final boolean destroyed = in.getAttributeBoolean(null, ATTR_DESTROYED, false);
5227         final boolean sealed = in.getAttributeBoolean(null, ATTR_SEALED, false);
5228         final int parentSessionId = in.getAttributeInt(null, ATTR_PARENT_SESSION_ID,
5229                 SessionInfo.INVALID_ID);
5230 
5231         final SessionParams params = new SessionParams(
5232                 SessionParams.MODE_INVALID);
5233         params.isMultiPackage = in.getAttributeBoolean(null, ATTR_MULTI_PACKAGE, false);
5234         params.isStaged = in.getAttributeBoolean(null, ATTR_STAGED_SESSION, false);
5235         params.mode = in.getAttributeInt(null, ATTR_MODE);
5236         params.installFlags = in.getAttributeInt(null, ATTR_INSTALL_FLAGS);
5237         params.installLocation = in.getAttributeInt(null, ATTR_INSTALL_LOCATION);
5238         params.sizeBytes = in.getAttributeLong(null, ATTR_SIZE_BYTES);
5239         params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
5240         params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
5241         params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
5242         params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
5243         params.originatingUid =
5244                 in.getAttributeInt(null, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
5245         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
5246         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
5247         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
5248         params.installReason = in.getAttributeInt(null, ATTR_INSTALL_REASON);
5249         params.packageSource = in.getAttributeInt(null, ATTR_PACKAGE_SOURCE);
5250         params.applicationEnabledSettingPersistent = in.getAttributeBoolean(null,
5251                 ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT, false);
5252 
5253         if (in.getAttributeBoolean(null, ATTR_IS_DATALOADER, false)) {
5254             params.dataLoaderParams = new DataLoaderParams(
5255                     in.getAttributeInt(null, ATTR_DATALOADER_TYPE),
5256                     new ComponentName(
5257                             readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME),
5258                             readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)),
5259                     readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS));
5260         }
5261 
5262         final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
5263         if (appIconFile.exists()) {
5264             params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
5265             params.appIconLastModified = appIconFile.lastModified();
5266         }
5267         final boolean isReady = in.getAttributeBoolean(null, ATTR_IS_READY, false);
5268         final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false);
5269         final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false);
5270         final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE,
5271                 PackageManager.INSTALL_UNKNOWN);
5272         final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE);
5273 
5274         if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
5275             throw new IllegalArgumentException("Can't restore staged session with invalid state.");
5276         }
5277 
5278         // Parse sub tags of this session, typically used for repeated values / arrays.
5279         // Sub tags can come in any order, therefore we need to keep track of what we find while
5280         // parsing and only set the right values at the end.
5281 
5282         // Store the current depth. We should stop parsing when we reach an end tag at the same
5283         // depth.
5284         List<String> legacyGrantedRuntimePermissions = new ArrayList<>();
5285         ArraySet<String> grantPermissions = new ArraySet<>();
5286         ArraySet<String> denyPermissions = new ArraySet<>();
5287         List<String> whitelistedRestrictedPermissions = new ArrayList<>();
5288         int autoRevokePermissionsMode = MODE_DEFAULT;
5289         IntArray childSessionIds = new IntArray();
5290         List<InstallationFile> files = new ArrayList<>();
5291         ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>();
5292         ArrayMap<String, byte[]> signatures = new ArrayMap<>();
5293         int outerDepth = in.getDepth();
5294         int type;
5295         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
5296                 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
5297             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
5298                 continue;
5299             }
5300             switch (in.getName()) {
5301                 case TAG_GRANTED_RUNTIME_PERMISSION:
5302                     legacyGrantedRuntimePermissions.add(readStringAttribute(in, ATTR_NAME));
5303                     break;
5304                 case TAG_GRANT_PERMISSION:
5305                     grantPermissions.add(readStringAttribute(in, ATTR_NAME));
5306                     break;
5307                 case TAG_DENY_PERMISSION:
5308                     denyPermissions.add(readStringAttribute(in, ATTR_NAME));
5309                     break;
5310                 case TAG_WHITELISTED_RESTRICTED_PERMISSION:
5311                     whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME));
5312                     break;
5313                 case TAG_AUTO_REVOKE_PERMISSIONS_MODE:
5314                     autoRevokePermissionsMode = in.getAttributeInt(null, ATTR_MODE);
5315                     break;
5316                 case TAG_CHILD_SESSION:
5317                     childSessionIds.add(in.getAttributeInt(null, ATTR_SESSION_ID,
5318                             SessionInfo.INVALID_ID));
5319                     break;
5320                 case TAG_SESSION_FILE:
5321                     files.add(new InstallationFile(
5322                             in.getAttributeInt(null, ATTR_LOCATION, 0),
5323                             readStringAttribute(in, ATTR_NAME),
5324                             in.getAttributeLong(null, ATTR_LENGTH_BYTES, -1),
5325                             readByteArrayAttribute(in, ATTR_METADATA),
5326                             readByteArrayAttribute(in, ATTR_SIGNATURE)));
5327                     break;
5328                 case TAG_SESSION_CHECKSUM:
5329                     final String fileName = readStringAttribute(in, ATTR_NAME);
5330                     final Checksum checksum = new Checksum(
5331                             in.getAttributeInt(null, ATTR_CHECKSUM_KIND, 0),
5332                             readByteArrayAttribute(in, ATTR_CHECKSUM_VALUE));
5333 
5334                     List<Checksum> fileChecksums = checksums.get(fileName);
5335                     if (fileChecksums == null) {
5336                         fileChecksums = new ArrayList<>();
5337                         checksums.put(fileName, fileChecksums);
5338                     }
5339                     fileChecksums.add(checksum);
5340                     break;
5341                 case TAG_SESSION_CHECKSUM_SIGNATURE:
5342                     final String fileName1 = readStringAttribute(in, ATTR_NAME);
5343                     final byte[] signature = readByteArrayAttribute(in, ATTR_SIGNATURE);
5344                     signatures.put(fileName1, signature);
5345                     break;
5346             }
5347         }
5348 
5349         if (legacyGrantedRuntimePermissions.size() > 0) {
5350             params.setPermissionStates(legacyGrantedRuntimePermissions, Collections.emptyList());
5351         } else {
5352             params.setPermissionStates(grantPermissions, denyPermissions);
5353         }
5354 
5355         if (whitelistedRestrictedPermissions.size() > 0) {
5356             params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
5357         }
5358 
5359         params.autoRevokePermissionsMode = autoRevokePermissionsMode;
5360 
5361         int[] childSessionIdsArray;
5362         if (childSessionIds.size() > 0) {
5363             childSessionIdsArray = new int[childSessionIds.size()];
5364             for (int i = 0, size = childSessionIds.size(); i < size; ++i) {
5365                 childSessionIdsArray[i] = childSessionIds.get(i);
5366             }
5367         } else {
5368             childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
5369         }
5370 
5371         InstallationFile[] fileArray = null;
5372         if (!files.isEmpty()) {
5373             fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY);
5374         }
5375 
5376         ArrayMap<String, PerFileChecksum> checksumsMap = null;
5377         if (!checksums.isEmpty()) {
5378             checksumsMap = new ArrayMap<>(checksums.size());
5379             for (int i = 0, isize = checksums.size(); i < isize; ++i) {
5380                 final String fileName = checksums.keyAt(i);
5381                 final List<Checksum> perFileChecksum = checksums.valueAt(i);
5382                 final byte[] perFileSignature = signatures.get(fileName);
5383                 checksumsMap.put(fileName, new PerFileChecksum(
5384                         perFileChecksum.toArray(new Checksum[perFileChecksum.size()]),
5385                         perFileSignature));
5386             }
5387         }
5388 
5389         InstallSource installSource = InstallSource.create(installInitiatingPackageName,
5390                 installOriginatingPackageName, installerPackageName, installPackageUid,
5391                 updateOwnerPackageName, installerAttributionTag, params.packageSource);
5392         return new PackageInstallerSession(callback, context, pm, sessionProvider,
5393                 silentUpdatePolicy, installerThread, stagingManager, sessionId, userId,
5394                 installerUid, installSource, params, createdMillis, committedMillis, stageDir,
5395                 stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
5396                 childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
5397                 sessionErrorCode, sessionErrorMessage);
5398     }
5399 }
5400