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