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