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