• 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.PackageParser.APEX_FILE_EXTENSION;
31 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
32 import static android.system.OsConstants.O_CREAT;
33 import static android.system.OsConstants.O_RDONLY;
34 import static android.system.OsConstants.O_WRONLY;
35 
36 import static com.android.internal.util.XmlUtils.readBitmapAttribute;
37 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
38 import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
39 import static com.android.internal.util.XmlUtils.readIntAttribute;
40 import static com.android.internal.util.XmlUtils.readLongAttribute;
41 import static com.android.internal.util.XmlUtils.readStringAttribute;
42 import static com.android.internal.util.XmlUtils.readUriAttribute;
43 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
44 import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
45 import static com.android.internal.util.XmlUtils.writeIntAttribute;
46 import static com.android.internal.util.XmlUtils.writeLongAttribute;
47 import static com.android.internal.util.XmlUtils.writeStringAttribute;
48 import static com.android.internal.util.XmlUtils.writeUriAttribute;
49 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
50 
51 import android.Manifest;
52 import android.annotation.NonNull;
53 import android.annotation.Nullable;
54 import android.app.Notification;
55 import android.app.NotificationManager;
56 import android.app.admin.DevicePolicyEventLogger;
57 import android.app.admin.DevicePolicyManagerInternal;
58 import android.content.ComponentName;
59 import android.content.Context;
60 import android.content.IIntentReceiver;
61 import android.content.IIntentSender;
62 import android.content.Intent;
63 import android.content.IntentSender;
64 import android.content.pm.ApplicationInfo;
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.InstallationFile;
75 import android.content.pm.InstallationFileParcel;
76 import android.content.pm.PackageInfo;
77 import android.content.pm.PackageInstaller;
78 import android.content.pm.PackageInstaller.SessionInfo;
79 import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
80 import android.content.pm.PackageInstaller.SessionParams;
81 import android.content.pm.PackageManager;
82 import android.content.pm.PackageManagerInternal;
83 import android.content.pm.PackageParser;
84 import android.content.pm.PackageParser.ApkLite;
85 import android.content.pm.PackageParser.PackageLite;
86 import android.content.pm.PackageParser.PackageParserException;
87 import android.content.pm.dex.DexMetadataHelper;
88 import android.content.pm.parsing.ApkLiteParseUtils;
89 import android.content.pm.parsing.result.ParseResult;
90 import android.content.pm.parsing.result.ParseTypeImpl;
91 import android.graphics.Bitmap;
92 import android.graphics.BitmapFactory;
93 import android.os.Binder;
94 import android.os.Bundle;
95 import android.os.FileBridge;
96 import android.os.FileUtils;
97 import android.os.Handler;
98 import android.os.IBinder;
99 import android.os.Looper;
100 import android.os.Message;
101 import android.os.ParcelFileDescriptor;
102 import android.os.ParcelableException;
103 import android.os.Process;
104 import android.os.RemoteException;
105 import android.os.RevocableFileDescriptor;
106 import android.os.SystemProperties;
107 import android.os.UserHandle;
108 import android.os.incremental.IStorageHealthListener;
109 import android.os.incremental.IncrementalFileStorages;
110 import android.os.incremental.IncrementalManager;
111 import android.os.incremental.StorageHealthCheckParams;
112 import android.os.storage.StorageManager;
113 import android.provider.Settings.Secure;
114 import android.stats.devicepolicy.DevicePolicyEnums;
115 import android.system.ErrnoException;
116 import android.system.Int64Ref;
117 import android.system.Os;
118 import android.system.OsConstants;
119 import android.system.StructStat;
120 import android.text.TextUtils;
121 import android.util.ArraySet;
122 import android.util.ExceptionUtils;
123 import android.util.MathUtils;
124 import android.util.Slog;
125 import android.util.SparseIntArray;
126 import android.util.apk.ApkSignatureVerifier;
127 
128 import com.android.internal.R;
129 import com.android.internal.annotations.GuardedBy;
130 import com.android.internal.content.NativeLibraryHelper;
131 import com.android.internal.content.PackageHelper;
132 import com.android.internal.messages.nano.SystemMessageProto;
133 import com.android.internal.os.SomeArgs;
134 import com.android.internal.util.ArrayUtils;
135 import com.android.internal.util.FrameworkStatsLog;
136 import com.android.internal.util.IndentingPrintWriter;
137 import com.android.server.LocalServices;
138 import com.android.server.pm.Installer.InstallerException;
139 import com.android.server.pm.dex.DexManager;
140 import com.android.server.pm.parsing.pkg.AndroidPackage;
141 import com.android.server.security.VerityUtils;
142 
143 import libcore.io.IoUtils;
144 import libcore.util.EmptyArray;
145 
146 import org.xmlpull.v1.XmlPullParser;
147 import org.xmlpull.v1.XmlPullParserException;
148 import org.xmlpull.v1.XmlSerializer;
149 
150 import java.io.File;
151 import java.io.FileDescriptor;
152 import java.io.FileFilter;
153 import java.io.FileOutputStream;
154 import java.io.IOException;
155 import java.util.ArrayList;
156 import java.util.Arrays;
157 import java.util.List;
158 import java.util.Objects;
159 import java.util.Set;
160 import java.util.concurrent.atomic.AtomicInteger;
161 
162 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
163     private static final String TAG = "PackageInstallerSession";
164     private static final boolean LOGD = true;
165     private static final String REMOVE_MARKER_EXTENSION = ".removed";
166 
167     private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 1;
168     private static final int MSG_INSTALL = 2;
169     private static final int MSG_ON_PACKAGE_INSTALLED = 3;
170     private static final int MSG_SESSION_VERIFICATION_FAILURE = 4;
171 
172     /** XML constants used for persisting a session */
173     static final String TAG_SESSION = "session";
174     static final String TAG_CHILD_SESSION = "childSession";
175     static final String TAG_SESSION_FILE = "sessionFile";
176     private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
177     private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
178             "whitelisted-restricted-permission";
179     private static final String TAG_AUTO_REVOKE_PERMISSIONS_MODE =
180             "auto-revoke-permissions-mode";
181     private static final String ATTR_SESSION_ID = "sessionId";
182     private static final String ATTR_USER_ID = "userId";
183     private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
184     private static final String ATTR_INSTALLER_UID = "installerUid";
185     private static final String ATTR_INITIATING_PACKAGE_NAME =
186             "installInitiatingPackageName";
187     private static final String ATTR_ORIGINATING_PACKAGE_NAME =
188             "installOriginatingPackageName";
189     private static final String ATTR_CREATED_MILLIS = "createdMillis";
190     private static final String ATTR_UPDATED_MILLIS = "updatedMillis";
191     private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
192     private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
193     private static final String ATTR_PREPARED = "prepared";
194     private static final String ATTR_COMMITTED = "committed";
195     private static final String ATTR_DESTROYED = "destroyed";
196     private static final String ATTR_SEALED = "sealed";
197     private static final String ATTR_MULTI_PACKAGE = "multiPackage";
198     private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
199     private static final String ATTR_STAGED_SESSION = "stagedSession";
200     private static final String ATTR_IS_READY = "isReady";
201     private static final String ATTR_IS_FAILED = "isFailed";
202     private static final String ATTR_IS_APPLIED = "isApplied";
203     private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode";
204     private static final String ATTR_STAGED_SESSION_ERROR_MESSAGE = "errorMessage";
205     private static final String ATTR_MODE = "mode";
206     private static final String ATTR_INSTALL_FLAGS = "installFlags";
207     private static final String ATTR_INSTALL_LOCATION = "installLocation";
208     private static final String ATTR_SIZE_BYTES = "sizeBytes";
209     private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
210     @Deprecated
211     private static final String ATTR_APP_ICON = "appIcon";
212     private static final String ATTR_APP_LABEL = "appLabel";
213     private static final String ATTR_ORIGINATING_URI = "originatingUri";
214     private static final String ATTR_ORIGINATING_UID = "originatingUid";
215     private static final String ATTR_REFERRER_URI = "referrerUri";
216     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
217     private static final String ATTR_VOLUME_UUID = "volumeUuid";
218     private static final String ATTR_NAME = "name";
219     private static final String ATTR_INSTALL_REASON = "installRason";
220     private static final String ATTR_IS_DATALOADER = "isDataLoader";
221     private static final String ATTR_DATALOADER_TYPE = "dataLoaderType";
222     private static final String ATTR_DATALOADER_PACKAGE_NAME = "dataLoaderPackageName";
223     private static final String ATTR_DATALOADER_CLASS_NAME = "dataLoaderClassName";
224     private static final String ATTR_DATALOADER_ARGUMENTS = "dataLoaderArguments";
225     private static final String ATTR_LOCATION = "location";
226     private static final String ATTR_LENGTH_BYTES = "lengthBytes";
227     private static final String ATTR_METADATA = "metadata";
228     private static final String ATTR_SIGNATURE = "signature";
229 
230     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
231     private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
232     private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {};
233 
234     private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
235 
236     private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000;
237     private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
238     private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
239 
240     // TODO: enforce INSTALL_ALLOW_TEST
241     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
242 
243     private final PackageInstallerService.InternalCallback mCallback;
244     private final Context mContext;
245     private final PackageManagerService mPm;
246     private final Handler mHandler;
247     private final PackageSessionProvider mSessionProvider;
248     private final StagingManager mStagingManager;
249 
250     final int sessionId;
251     final int userId;
252     final SessionParams params;
253     final long createdMillis;
254 
255     /** Staging location where client data is written. */
256     final File stageDir;
257     final String stageCid;
258 
259     private final AtomicInteger mActiveCount = new AtomicInteger();
260 
261     private final Object mLock = new Object();
262 
263     /** Timestamp of the last time this session changed state  */
264     @GuardedBy("mLock")
265     private long updatedMillis;
266 
267     /** Uid of the creator of this session. */
268     private final int mOriginalInstallerUid;
269 
270     /** Package name of the app that created the installation session. */
271     private final String mOriginalInstallerPackageName;
272 
273     /** Uid of the owner of the installer session */
274     @GuardedBy("mLock")
275     private int mInstallerUid;
276 
277     /** Where this install request came from */
278     @GuardedBy("mLock")
279     private InstallSource mInstallSource;
280 
281     @GuardedBy("mLock")
282     private float mClientProgress = 0;
283     @GuardedBy("mLock")
284     private float mInternalProgress = 0;
285 
286     @GuardedBy("mLock")
287     private float mProgress = 0;
288     @GuardedBy("mLock")
289     private float mReportedProgress = -1;
290 
291     /** State of the session. */
292     @GuardedBy("mLock")
293     private boolean mPrepared = false;
294     @GuardedBy("mLock")
295     private boolean mSealed = false;
296     @GuardedBy("mLock")
297     private boolean mShouldBeSealed = false;
298     @GuardedBy("mLock")
299     private boolean mCommitted = false;
300     @GuardedBy("mLock")
301     private boolean mRelinquished = false;
302     @GuardedBy("mLock")
303     private boolean mDestroyed = false;
304 
305     /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
306     @GuardedBy("mLock")
307     private boolean mPermissionsManuallyAccepted = false;
308 
309     @GuardedBy("mLock")
310     private int mFinalStatus;
311     @GuardedBy("mLock")
312     private String mFinalMessage;
313 
314     @GuardedBy("mLock")
315     private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
316     @GuardedBy("mLock")
317     private final ArrayList<FileBridge> mBridges = new ArrayList<>();
318 
319     @GuardedBy("mLock")
320     private IntentSender mRemoteStatusReceiver;
321 
322     /** Fields derived from commit parsing */
323     @GuardedBy("mLock")
324     private String mPackageName;
325     @GuardedBy("mLock")
326     private long mVersionCode;
327     @GuardedBy("mLock")
328     private PackageParser.SigningDetails mSigningDetails;
329     @GuardedBy("mLock")
330     private SparseIntArray mChildSessionIds = new SparseIntArray();
331     @GuardedBy("mLock")
332     private int mParentSessionId;
333 
334     static class FileEntry {
335         private final int mIndex;
336         private final InstallationFile mFile;
337 
FileEntry(int index, InstallationFile file)338         FileEntry(int index, InstallationFile file) {
339             this.mIndex = index;
340             this.mFile = file;
341         }
342 
getIndex()343         int getIndex() {
344             return this.mIndex;
345         }
346 
getFile()347         InstallationFile getFile() {
348             return this.mFile;
349         }
350 
351         @Override
equals(Object obj)352         public boolean equals(Object obj) {
353             if (!(obj instanceof FileEntry)) {
354                 return false;
355             }
356             final FileEntry rhs = (FileEntry) obj;
357             return (mFile.getLocation() == rhs.mFile.getLocation()) && TextUtils.equals(
358                     mFile.getName(), rhs.mFile.getName());
359         }
360 
361         @Override
hashCode()362         public int hashCode() {
363             return Objects.hash(mFile.getLocation(), mFile.getName());
364         }
365     }
366 
367     @GuardedBy("mLock")
368     private ArraySet<FileEntry> mFiles = new ArraySet<>();
369 
370     @GuardedBy("mLock")
371     private boolean mStagedSessionApplied;
372     @GuardedBy("mLock")
373     private boolean mStagedSessionReady;
374     @GuardedBy("mLock")
375     private boolean mStagedSessionFailed;
376     @GuardedBy("mLock")
377     private int mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
378     @GuardedBy("mLock")
379     private String mStagedSessionErrorMessage;
380 
381     /**
382      * Path to the validated base APK for this session, which may point at an
383      * APK inside the session (when the session defines the base), or it may
384      * point at the existing base APK (when adding splits to an existing app).
385      * <p>
386      * This is used when confirming permissions, since we can't fully stage the
387      * session inside an ASEC before confirming with user.
388      */
389     @GuardedBy("mLock")
390     private File mResolvedBaseFile;
391 
392     @GuardedBy("mLock")
393     private final List<File> mResolvedStagedFiles = new ArrayList<>();
394     @GuardedBy("mLock")
395     private final List<File> mResolvedInheritedFiles = new ArrayList<>();
396     @GuardedBy("mLock")
397     private final List<String> mResolvedInstructionSets = new ArrayList<>();
398     @GuardedBy("mLock")
399     private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
400     @GuardedBy("mLock")
401     private File mInheritedFilesBase;
402     @GuardedBy("mLock")
403     private boolean mVerityFound;
404 
405     private boolean mDataLoaderFinished = false;
406 
407     // TODO(b/159663586): should be protected by mLock
408     private IncrementalFileStorages mIncrementalFileStorages;
409 
410     private static final FileFilter sAddedApkFilter = new FileFilter() {
411         @Override
412         public boolean accept(File file) {
413             // Installers can't stage directories, so it's fine to ignore
414             // entries like "lost+found".
415             if (file.isDirectory()) return false;
416             if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
417             if (DexMetadataHelper.isDexMetadataFile(file)) return false;
418             if (VerityUtils.isFsveritySignatureFile(file)) return false;
419             return true;
420         }
421     };
422     private static final FileFilter sAddedFilter = new FileFilter() {
423         @Override
424         public boolean accept(File file) {
425             // Installers can't stage directories, so it's fine to ignore
426             // entries like "lost+found".
427             if (file.isDirectory()) return false;
428             if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
429             return true;
430         }
431     };
432     private static final FileFilter sRemovedFilter = new FileFilter() {
433         @Override
434         public boolean accept(File file) {
435             if (file.isDirectory()) return false;
436             if (!file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
437             return true;
438         }
439     };
440 
441     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
442         @Override
443         public boolean handleMessage(Message msg) {
444             switch (msg.what) {
445                 case MSG_STREAM_VALIDATE_AND_COMMIT:
446                     handleStreamValidateAndCommit();
447                     break;
448                 case MSG_INSTALL:
449                     handleInstall();
450                     break;
451                 case MSG_ON_PACKAGE_INSTALLED:
452                     final SomeArgs args = (SomeArgs) msg.obj;
453                     final String packageName = (String) args.arg1;
454                     final String message = (String) args.arg2;
455                     final Bundle extras = (Bundle) args.arg3;
456                     final IntentSender statusReceiver = (IntentSender) args.arg4;
457                     final int returnCode = args.argi1;
458                     args.recycle();
459 
460                     sendOnPackageInstalled(mContext, statusReceiver, sessionId,
461                             isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId,
462                             packageName, returnCode, message, extras);
463 
464                     break;
465                 case MSG_SESSION_VERIFICATION_FAILURE:
466                     final int error = msg.arg1;
467                     final String detailMessage = (String) msg.obj;
468                     onSessionVerificationFailure(error, detailMessage);
469                     break;
470             }
471 
472             return true;
473         }
474     };
475 
isDataLoaderInstallation()476     private boolean isDataLoaderInstallation() {
477         return params.dataLoaderParams != null;
478     }
479 
isStreamingInstallation()480     private boolean isStreamingInstallation() {
481         return isDataLoaderInstallation() && params.dataLoaderParams.getType() == STREAMING;
482     }
483 
isIncrementalInstallation()484     private boolean isIncrementalInstallation() {
485         return isDataLoaderInstallation() && params.dataLoaderParams.getType() == INCREMENTAL;
486     }
487 
488     /**
489      * @return {@code true} iff the installing is app an device owner or affiliated profile owner.
490      */
491     @GuardedBy("mLock")
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()492     private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
493         if (userId != UserHandle.getUserId(mInstallerUid)) {
494             return false;
495         }
496         DevicePolicyManagerInternal dpmi =
497                 LocalServices.getService(DevicePolicyManagerInternal.class);
498         return dpmi != null && dpmi.canSilentlyInstallPackage(
499                 mInstallSource.installerPackageName, mInstallerUid);
500     }
501 
502     /**
503      * Checks if the permissions still need to be confirmed.
504      *
505      * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
506      * installer might still {@link #transfer(String) change}.
507      *
508      * @return {@code true} iff we need to ask to confirm the permissions?
509      */
510     @GuardedBy("mLock")
needToAskForPermissionsLocked()511     private boolean needToAskForPermissionsLocked() {
512         if (mPermissionsManuallyAccepted) {
513             return false;
514         }
515 
516         final boolean isInstallPermissionGranted =
517                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
518                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
519         final boolean isSelfUpdatePermissionGranted =
520                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
521                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
522         final boolean isUpdatePermissionGranted =
523                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
524                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
525         final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
526         final boolean isPermissionGranted = isInstallPermissionGranted
527                 || (isUpdatePermissionGranted && targetPackageUid != -1)
528                 || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
529         final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
530         final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
531         final boolean forcePermissionPrompt =
532                 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
533 
534         // Device owners and affiliated profile owners  are allowed to silently install packages, so
535         // the permission check is waived if the installer is the device owner.
536         return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
537                 || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
538     }
539 
PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager, int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, SessionParams params, long createdMillis, File stageDir, String stageCid, InstallationFile[] files, boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int stagedSessionErrorCode, String stagedSessionErrorMessage)540     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
541             Context context, PackageManagerService pm,
542             PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
543             int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
544             SessionParams params, long createdMillis,
545             File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
546             boolean committed, boolean destroyed, boolean sealed,
547             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
548             boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
549             String stagedSessionErrorMessage) {
550         mCallback = callback;
551         mContext = context;
552         mPm = pm;
553         mSessionProvider = sessionProvider;
554         mHandler = new Handler(looper, mHandlerCallback);
555         mStagingManager = stagingManager;
556 
557         this.sessionId = sessionId;
558         this.userId = userId;
559         mOriginalInstallerUid = installerUid;
560         mInstallerUid = installerUid;
561         mInstallSource = Objects.requireNonNull(installSource);
562         mOriginalInstallerPackageName = mInstallSource.installerPackageName;
563         this.params = params;
564         this.createdMillis = createdMillis;
565         this.updatedMillis = createdMillis;
566         this.stageDir = stageDir;
567         this.stageCid = stageCid;
568         this.mShouldBeSealed = sealed;
569         if (childSessionIds != null) {
570             for (int childSessionId : childSessionIds) {
571                 mChildSessionIds.put(childSessionId, 0);
572             }
573         }
574         this.mParentSessionId = parentSessionId;
575 
576         if (files != null) {
577             for (int i = 0, size = files.length; i < size; ++i) {
578                 InstallationFile file = files[i];
579                 if (!mFiles.add(new FileEntry(i, file))) {
580                     throw new IllegalArgumentException(
581                             "Trying to add a duplicate installation file");
582                 }
583             }
584         }
585 
586         if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
587             throw new IllegalArgumentException(
588                     "Exactly one of stageDir or stageCid stage must be set");
589         }
590 
591         mPrepared = prepared;
592         mCommitted = committed;
593         mDestroyed = destroyed;
594         mStagedSessionReady = isReady;
595         mStagedSessionFailed = isFailed;
596         mStagedSessionApplied = isApplied;
597         mStagedSessionErrorCode = stagedSessionErrorCode;
598         mStagedSessionErrorMessage =
599                 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
600 
601         if (isDataLoaderInstallation()) {
602             if (isApexInstallation()) {
603                 throw new IllegalArgumentException(
604                         "DataLoader installation of APEX modules is not allowed.");
605             }
606             if (this.params.dataLoaderParams.getComponentName().getPackageName()
607                     == SYSTEM_DATA_LOADER_PACKAGE) {
608                 assertShellOrSystemCalling("System data loaders");
609             }
610         }
611 
612         if (isIncrementalInstallation()) {
613             if (!IncrementalManager.isAllowed()) {
614                 throw new IllegalArgumentException("Incremental installation not allowed.");
615             }
616             if (!isIncrementalInstallationAllowed(mPackageName)) {
617                 throw new IllegalArgumentException(
618                         "Incremental installation of this package is not allowed.");
619             }
620         }
621     }
622 
623     /**
624      * Returns {@code true} if the {@link SessionInfo} object should be produced with potentially
625      * sensitive data scrubbed from its fields.
626      *
627      * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
628      *                   need to be scrubbed
629      */
shouldScrubData(int callingUid)630     private boolean shouldScrubData(int callingUid) {
631         return !(callingUid < Process.FIRST_APPLICATION_UID || getInstallerUid() == callingUid);
632     }
633 
634     /**
635      * Generates a {@link SessionInfo} object for the provided uid. This may result in some fields
636      * that may contain sensitive info being filtered.
637      *
638      * @param includeIcon true if the icon should be included in the object
639      * @param callingUid the uid of the caller; the recipient of the {@link SessionInfo} that may
640      *                   need to be scrubbed
641      * @see #shouldScrubData(int)
642      */
generateInfoForCaller(boolean includeIcon, int callingUid)643     public SessionInfo generateInfoForCaller(boolean includeIcon, int callingUid) {
644         return generateInfoInternal(includeIcon, shouldScrubData(callingUid));
645     }
646 
647     /**
648      * Generates a {@link SessionInfo} object to ensure proper hiding of sensitive fields.
649      *
650      * @param includeIcon true if the icon should be included in the object
651      * @see #generateInfoForCaller(boolean, int)
652      */
generateInfoScrubbed(boolean includeIcon)653     public SessionInfo generateInfoScrubbed(boolean includeIcon) {
654         return generateInfoInternal(includeIcon, true /*scrubData*/);
655     }
656 
generateInfoInternal(boolean includeIcon, boolean scrubData)657     private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) {
658         final SessionInfo info = new SessionInfo();
659         synchronized (mLock) {
660             info.sessionId = sessionId;
661             info.userId = userId;
662             info.installerPackageName = mInstallSource.installerPackageName;
663             info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
664                     mResolvedBaseFile.getAbsolutePath() : null;
665             info.progress = mProgress;
666             info.sealed = mSealed;
667             info.isCommitted = mCommitted;
668             info.active = mActiveCount.get() > 0;
669 
670             info.mode = params.mode;
671             info.installReason = params.installReason;
672             info.sizeBytes = params.sizeBytes;
673             info.appPackageName = params.appPackageName;
674             if (includeIcon) {
675                 info.appIcon = params.appIcon;
676             }
677             info.appLabel = params.appLabel;
678 
679             info.installLocation = params.installLocation;
680             if (!scrubData) {
681                 info.originatingUri = params.originatingUri;
682             }
683             info.originatingUid = params.originatingUid;
684             if (!scrubData) {
685                 info.referrerUri = params.referrerUri;
686             }
687             info.grantedRuntimePermissions = params.grantedRuntimePermissions;
688             info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions;
689             info.autoRevokePermissionsMode = params.autoRevokePermissionsMode;
690             info.installFlags = params.installFlags;
691             info.isMultiPackage = params.isMultiPackage;
692             info.isStaged = params.isStaged;
693             info.rollbackDataPolicy = params.rollbackDataPolicy;
694             info.parentSessionId = mParentSessionId;
695             info.childSessionIds = mChildSessionIds.copyKeys();
696             if (info.childSessionIds == null) {
697                 info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
698             }
699             info.isStagedSessionApplied = mStagedSessionApplied;
700             info.isStagedSessionReady = mStagedSessionReady;
701             info.isStagedSessionFailed = mStagedSessionFailed;
702             info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage);
703             info.createdMillis = createdMillis;
704             info.updatedMillis = updatedMillis;
705         }
706         return info;
707     }
708 
isPrepared()709     public boolean isPrepared() {
710         synchronized (mLock) {
711             return mPrepared;
712         }
713     }
714 
isSealed()715     public boolean isSealed() {
716         synchronized (mLock) {
717             return mSealed;
718         }
719     }
720 
721     /** {@hide} */
isCommitted()722     boolean isCommitted() {
723         synchronized (mLock) {
724             return mCommitted;
725         }
726     }
727 
728     /** {@hide} */
isDestroyed()729     boolean isDestroyed() {
730         synchronized (mLock) {
731             return mDestroyed;
732         }
733     }
734 
735     /** Returns true if a staged session has reached a final state and can be forgotten about  */
isStagedAndInTerminalState()736     public boolean isStagedAndInTerminalState() {
737         synchronized (mLock) {
738             return params.isStaged && (mStagedSessionApplied || mStagedSessionFailed);
739         }
740     }
741 
742     @GuardedBy("mLock")
assertPreparedAndNotSealedLocked(String cookie)743     private void assertPreparedAndNotSealedLocked(String cookie) {
744         assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
745         if (mSealed) {
746             throw new SecurityException(cookie + " not allowed after sealing");
747         }
748     }
749 
750     @GuardedBy("mLock")
assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)751     private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
752         assertPreparedAndNotDestroyedLocked(cookie);
753         if (mCommitted) {
754             throw new SecurityException(cookie + " not allowed after commit");
755         }
756     }
757 
758     @GuardedBy("mLock")
assertPreparedAndNotDestroyedLocked(String cookie)759     private void assertPreparedAndNotDestroyedLocked(String cookie) {
760         if (!mPrepared) {
761             throw new IllegalStateException(cookie + " before prepared");
762         }
763         if (mDestroyed) {
764             throw new SecurityException(cookie + " not allowed after destruction");
765         }
766     }
767 
768     @GuardedBy("mLock")
setClientProgressLocked(float progress)769     private void setClientProgressLocked(float progress) {
770         // Always publish first staging movement
771         final boolean forcePublish = (mClientProgress == 0);
772         mClientProgress = progress;
773         computeProgressLocked(forcePublish);
774     }
775 
776     @Override
setClientProgress(float progress)777     public void setClientProgress(float progress) {
778         synchronized (mLock) {
779             assertCallerIsOwnerOrRootLocked();
780             setClientProgressLocked(progress);
781         }
782     }
783 
784     @Override
addClientProgress(float progress)785     public void addClientProgress(float progress) {
786         synchronized (mLock) {
787             assertCallerIsOwnerOrRootLocked();
788             setClientProgressLocked(mClientProgress + progress);
789         }
790     }
791 
792     @GuardedBy("mLock")
computeProgressLocked(boolean forcePublish)793     private void computeProgressLocked(boolean forcePublish) {
794         mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
795                 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
796 
797         // Only publish when meaningful change
798         if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
799             mReportedProgress = mProgress;
800             mCallback.onSessionProgressChanged(this, mProgress);
801         }
802     }
803 
804     @Override
getNames()805     public String[] getNames() {
806         synchronized (mLock) {
807             assertCallerIsOwnerOrRootLocked();
808             assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
809 
810             return getNamesLocked();
811         }
812     }
813 
814     @GuardedBy("mLock")
getNamesLocked()815     private String[] getNamesLocked() {
816         if (!isDataLoaderInstallation()) {
817             String[] result = stageDir.list();
818             if (result == null) {
819                 result = EmptyArray.STRING;
820             }
821             return result;
822         }
823 
824         InstallationFile[] files = getInstallationFilesLocked();
825         String[] result = new String[files.length];
826         for (int i = 0, size = files.length; i < size; ++i) {
827             result[i] = files[i].getName();
828         }
829         return result;
830     }
831 
832     @GuardedBy("mLock")
getInstallationFilesLocked()833     private InstallationFile[] getInstallationFilesLocked() {
834         final InstallationFile[] result = new InstallationFile[mFiles.size()];
835         for (FileEntry fileEntry : mFiles) {
836             result[fileEntry.getIndex()] = fileEntry.getFile();
837         }
838         return result;
839     }
840 
filterFiles(File parent, String[] names, FileFilter filter)841     private static ArrayList<File> filterFiles(File parent, String[] names, FileFilter filter) {
842         ArrayList<File> result = new ArrayList<>(names.length);
843         for (String name : names) {
844             File file = new File(parent, name);
845             if (filter.accept(file)) {
846                 result.add(file);
847             }
848         }
849         return result;
850     }
851 
852     @GuardedBy("mLock")
getAddedApksLocked()853     private List<File> getAddedApksLocked() {
854         String[] names = getNamesLocked();
855         return filterFiles(stageDir, names, sAddedApkFilter);
856     }
857 
858     @GuardedBy("mLock")
getRemovedFilesLocked()859     private List<File> getRemovedFilesLocked() {
860         String[] names = getNamesLocked();
861         return filterFiles(stageDir, names, sRemovedFilter);
862     }
863 
864     @Override
removeSplit(String splitName)865     public void removeSplit(String splitName) {
866         if (isDataLoaderInstallation()) {
867             throw new IllegalStateException(
868                     "Cannot remove splits in a data loader installation session.");
869         }
870         if (TextUtils.isEmpty(params.appPackageName)) {
871             throw new IllegalStateException("Must specify package name to remove a split");
872         }
873 
874         synchronized (mLock) {
875             assertCallerIsOwnerOrRootLocked();
876             assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
877 
878             try {
879                 createRemoveSplitMarkerLocked(splitName);
880             } catch (IOException e) {
881                 throw ExceptionUtils.wrap(e);
882             }
883         }
884     }
885 
getRemoveMarkerName(String name)886     private static String getRemoveMarkerName(String name) {
887         final String markerName = name + REMOVE_MARKER_EXTENSION;
888         if (!FileUtils.isValidExtFilename(markerName)) {
889             throw new IllegalArgumentException("Invalid marker: " + markerName);
890         }
891         return markerName;
892     }
893 
createRemoveSplitMarkerLocked(String splitName)894     private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
895         try {
896             final File target = new File(stageDir, getRemoveMarkerName(splitName));
897             target.createNewFile();
898             Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
899         } catch (ErrnoException e) {
900             throw e.rethrowAsIOException();
901         }
902     }
903 
assertShellOrSystemCalling(String operation)904     private void assertShellOrSystemCalling(String operation) {
905         switch (Binder.getCallingUid()) {
906             case android.os.Process.SHELL_UID:
907             case android.os.Process.ROOT_UID:
908             case android.os.Process.SYSTEM_UID:
909                 break;
910             default:
911                 throw new SecurityException(operation + " only supported from shell or system");
912         }
913     }
914 
assertCanWrite(boolean reverseMode)915     private void assertCanWrite(boolean reverseMode) {
916         if (isDataLoaderInstallation()) {
917             throw new IllegalStateException(
918                     "Cannot write regular files in a data loader installation session.");
919         }
920         synchronized (mLock) {
921             assertCallerIsOwnerOrRootLocked();
922             assertPreparedAndNotSealedLocked("assertCanWrite");
923         }
924         if (reverseMode) {
925             assertShellOrSystemCalling("Reverse mode");
926         }
927     }
928 
929     @Override
openWrite(String name, long offsetBytes, long lengthBytes)930     public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
931         assertCanWrite(false);
932         try {
933             return doWriteInternal(name, offsetBytes, lengthBytes, null);
934         } catch (IOException e) {
935             throw ExceptionUtils.wrap(e);
936         }
937     }
938 
939     @Override
write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)940     public void write(String name, long offsetBytes, long lengthBytes,
941             ParcelFileDescriptor fd) {
942         assertCanWrite(fd != null);
943         try {
944             doWriteInternal(name, offsetBytes, lengthBytes, fd);
945         } catch (IOException e) {
946             throw ExceptionUtils.wrap(e);
947         }
948     }
949 
openTargetInternal(String path, int flags, int mode)950     private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode)
951             throws IOException, ErrnoException {
952         // TODO: this should delegate to DCS so the system process avoids
953         // holding open FDs into containers.
954         final FileDescriptor fd = Os.open(path, flags, mode);
955         return new ParcelFileDescriptor(fd);
956     }
957 
createRevocableFdInternal(RevocableFileDescriptor fd, ParcelFileDescriptor pfd)958     private ParcelFileDescriptor createRevocableFdInternal(RevocableFileDescriptor fd,
959             ParcelFileDescriptor pfd) throws IOException {
960         int releasedFdInt = pfd.detachFd();
961         FileDescriptor releasedFd = new FileDescriptor();
962         releasedFd.setInt$(releasedFdInt);
963         fd.init(mContext, releasedFd);
964         return fd.getRevocableFileDescriptor();
965     }
966 
doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)967     private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
968             ParcelFileDescriptor incomingFd) throws IOException {
969         // Quick sanity check of state, and allocate a pipe for ourselves. We
970         // then do heavy disk allocation outside the lock, but this open pipe
971         // will block any attempted install transitions.
972         final RevocableFileDescriptor fd;
973         final FileBridge bridge;
974         synchronized (mLock) {
975             if (PackageInstaller.ENABLE_REVOCABLE_FD) {
976                 fd = new RevocableFileDescriptor();
977                 bridge = null;
978                 mFds.add(fd);
979             } else {
980                 fd = null;
981                 bridge = new FileBridge();
982                 mBridges.add(bridge);
983             }
984         }
985 
986         try {
987             // Use installer provided name for now; we always rename later
988             if (!FileUtils.isValidExtFilename(name)) {
989                 throw new IllegalArgumentException("Invalid name: " + name);
990             }
991             final File target;
992             final long identity = Binder.clearCallingIdentity();
993             try {
994                 target = new File(stageDir, name);
995             } finally {
996                 Binder.restoreCallingIdentity(identity);
997             }
998 
999             ParcelFileDescriptor targetPfd = openTargetInternal(target.getAbsolutePath(),
1000                     O_CREAT | O_WRONLY, 0644);
1001             Os.chmod(target.getAbsolutePath(), 0644);
1002 
1003             // If caller specified a total length, allocate it for them. Free up
1004             // cache space to grow, if needed.
1005             if (stageDir != null && lengthBytes > 0) {
1006                 mContext.getSystemService(StorageManager.class).allocateBytes(
1007                         targetPfd.getFileDescriptor(), lengthBytes,
1008                         PackageHelper.translateAllocateFlags(params.installFlags));
1009             }
1010 
1011             if (offsetBytes > 0) {
1012                 Os.lseek(targetPfd.getFileDescriptor(), offsetBytes, OsConstants.SEEK_SET);
1013             }
1014 
1015             if (incomingFd != null) {
1016                 // In "reverse" mode, we're streaming data ourselves from the
1017                 // incoming FD, which means we never have to hand out our
1018                 // sensitive internal FD. We still rely on a "bridge" being
1019                 // inserted above to hold the session active.
1020                 try {
1021                     final Int64Ref last = new Int64Ref(0);
1022                     FileUtils.copy(incomingFd.getFileDescriptor(), targetPfd.getFileDescriptor(),
1023                             lengthBytes, null, Runnable::run,
1024                             (long progress) -> {
1025                                 if (params.sizeBytes > 0) {
1026                                     final long delta = progress - last.value;
1027                                     last.value = progress;
1028                                     synchronized (mLock) {
1029                                         setClientProgressLocked(mClientProgress
1030                                                 + (float) delta / (float) params.sizeBytes);
1031                                     }
1032                                 }
1033                             });
1034                 } finally {
1035                     IoUtils.closeQuietly(targetPfd);
1036                     IoUtils.closeQuietly(incomingFd);
1037 
1038                     // We're done here, so remove the "bridge" that was holding
1039                     // the session active.
1040                     synchronized (mLock) {
1041                         if (PackageInstaller.ENABLE_REVOCABLE_FD) {
1042                             mFds.remove(fd);
1043                         } else {
1044                             bridge.forceClose();
1045                             mBridges.remove(bridge);
1046                         }
1047                     }
1048                 }
1049                 return null;
1050             } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
1051                 return createRevocableFdInternal(fd, targetPfd);
1052             } else {
1053                 bridge.setTargetFile(targetPfd);
1054                 bridge.start();
1055                 return bridge.getClientSocket();
1056             }
1057 
1058         } catch (ErrnoException e) {
1059             throw e.rethrowAsIOException();
1060         }
1061     }
1062 
1063     @Override
openRead(String name)1064     public ParcelFileDescriptor openRead(String name) {
1065         if (isDataLoaderInstallation()) {
1066             throw new IllegalStateException(
1067                     "Cannot read regular files in a data loader installation session.");
1068         }
1069         synchronized (mLock) {
1070             assertCallerIsOwnerOrRootLocked();
1071             assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
1072             try {
1073                 return openReadInternalLocked(name);
1074             } catch (IOException e) {
1075                 throw ExceptionUtils.wrap(e);
1076             }
1077         }
1078     }
1079 
openReadInternalLocked(String name)1080     private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
1081         try {
1082             if (!FileUtils.isValidExtFilename(name)) {
1083                 throw new IllegalArgumentException("Invalid name: " + name);
1084             }
1085             final File target = new File(stageDir, name);
1086             final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);
1087             return new ParcelFileDescriptor(targetFd);
1088         } catch (ErrnoException e) {
1089             throw e.rethrowAsIOException();
1090         }
1091     }
1092 
1093     /**
1094      * Check if the caller is the owner of this session. Otherwise throw a
1095      * {@link SecurityException}.
1096      */
1097     @GuardedBy("mLock")
assertCallerIsOwnerOrRootLocked()1098     private void assertCallerIsOwnerOrRootLocked() {
1099         final int callingUid = Binder.getCallingUid();
1100         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
1101             throw new SecurityException("Session does not belong to uid " + callingUid);
1102         }
1103     }
1104 
1105     /**
1106      * Check if the caller is the owner of this session. Otherwise throw a
1107      * {@link SecurityException}.
1108      */
1109     @GuardedBy("mLock")
assertCallerIsOwnerOrRootOrSystemLocked()1110     private void assertCallerIsOwnerOrRootOrSystemLocked() {
1111         final int callingUid = Binder.getCallingUid();
1112         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid
1113                 && callingUid != Process.SYSTEM_UID) {
1114             throw new SecurityException("Session does not belong to uid " + callingUid);
1115         }
1116     }
1117 
1118     /**
1119      * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
1120      */
1121     @GuardedBy("mLock")
assertNoWriteFileTransfersOpenLocked()1122     private void assertNoWriteFileTransfersOpenLocked() {
1123         // Verify that all writers are hands-off
1124         for (RevocableFileDescriptor fd : mFds) {
1125             if (!fd.isRevoked()) {
1126                 throw new SecurityException("Files still open");
1127             }
1128         }
1129         for (FileBridge bridge : mBridges) {
1130             if (!bridge.isClosed()) {
1131                 throw new SecurityException("Files still open");
1132             }
1133         }
1134     }
1135 
1136     @Override
commit(@onNull IntentSender statusReceiver, boolean forTransfer)1137     public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
1138         if (hasParentSessionId()) {
1139             throw new IllegalStateException(
1140                     "Session " + sessionId + " is a child of multi-package session "
1141                             + mParentSessionId +  " and may not be committed directly.");
1142         }
1143 
1144         if (!markAsSealed(statusReceiver, forTransfer)) {
1145             return;
1146         }
1147         if (isMultiPackage()) {
1148             final SparseIntArray remainingSessions = mChildSessionIds.clone();
1149             final IntentSender childIntentSender =
1150                     new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
1151                             .getIntentSender();
1152             boolean sealFailed = false;
1153             for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
1154                 final int childSessionId = mChildSessionIds.keyAt(i);
1155                 // seal all children, regardless if any of them fail; we'll throw/return
1156                 // as appropriate once all children have been processed
1157                 if (!mSessionProvider.getSession(childSessionId)
1158                         .markAsSealed(childIntentSender, forTransfer)) {
1159                     sealFailed = true;
1160                 }
1161             }
1162             if (sealFailed) {
1163                 return;
1164             }
1165         }
1166 
1167         dispatchStreamValidateAndCommit();
1168     }
1169 
dispatchStreamValidateAndCommit()1170     private void dispatchStreamValidateAndCommit() {
1171         mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
1172     }
1173 
handleStreamValidateAndCommit()1174     private void handleStreamValidateAndCommit() {
1175         PackageManagerException unrecoverableFailure = null;
1176         // This will track whether the session and any children were validated and are ready to
1177         // progress to the next phase of install
1178         boolean allSessionsReady = false;
1179         try {
1180             allSessionsReady = streamValidateAndCommit();
1181         } catch (PackageManagerException e) {
1182             unrecoverableFailure = e;
1183         }
1184 
1185         if (isMultiPackage()) {
1186             int childCount = mChildSessionIds.size();
1187 
1188             // This will contain all child sessions that do not encounter an unrecoverable failure
1189             ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount);
1190 
1191             for (int i = childCount - 1; i >= 0; --i) {
1192                 final int childSessionId = mChildSessionIds.keyAt(i);
1193                 // commit all children, regardless if any of them fail; we'll throw/return
1194                 // as appropriate once all children have been processed
1195                 try {
1196                     PackageInstallerSession session = mSessionProvider.getSession(childSessionId);
1197                     allSessionsReady &= session.streamValidateAndCommit();
1198                     nonFailingSessions.add(session);
1199                 } catch (PackageManagerException e) {
1200                     allSessionsReady = false;
1201                     if (unrecoverableFailure == null) {
1202                         unrecoverableFailure = e;
1203                     }
1204                 }
1205             }
1206             // If we encountered any unrecoverable failures, destroy all other sessions including
1207             // the parent
1208             if (unrecoverableFailure != null) {
1209                 // {@link #streamValidateAndCommit()} calls
1210                 // {@link #onSessionVerificationFailure(PackageManagerException)}, but we don't
1211                 // expect it to ever do so for parent sessions. Call that on this parent to clean
1212                 // it up and notify listeners of the error.
1213                 onSessionVerificationFailure(unrecoverableFailure);
1214                 // fail other child sessions that did not already fail
1215                 for (int i = nonFailingSessions.size() - 1; i >= 0; --i) {
1216                     PackageInstallerSession session = nonFailingSessions.get(i);
1217                     session.onSessionVerificationFailure(unrecoverableFailure);
1218                 }
1219             }
1220         }
1221 
1222         if (!allSessionsReady) {
1223             return;
1224         }
1225 
1226         mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
1227     }
1228 
1229     private final class FileSystemConnector extends
1230             IPackageInstallerSessionFileSystemConnector.Stub {
1231         final Set<String> mAddedFiles = new ArraySet<>();
1232 
FileSystemConnector(List<InstallationFileParcel> addedFiles)1233         FileSystemConnector(List<InstallationFileParcel> addedFiles) {
1234             for (InstallationFileParcel file : addedFiles) {
1235                 mAddedFiles.add(file.name);
1236             }
1237         }
1238 
1239         @Override
writeData(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)1240         public void writeData(String name, long offsetBytes, long lengthBytes,
1241                 ParcelFileDescriptor incomingFd) {
1242             if (incomingFd == null) {
1243                 throw new IllegalArgumentException("incomingFd can't be null");
1244             }
1245             if (!mAddedFiles.contains(name)) {
1246                 throw new SecurityException("File name is not in the list of added files.");
1247             }
1248             try {
1249                 doWriteInternal(name, offsetBytes, lengthBytes, incomingFd);
1250             } catch (IOException e) {
1251                 throw ExceptionUtils.wrap(e);
1252             }
1253         }
1254     }
1255 
1256     private class ChildStatusIntentReceiver {
1257         private final SparseIntArray mChildSessionsRemaining;
1258         private final IntentSender mStatusReceiver;
1259         private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
1260             @Override
1261             public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
1262                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
1263                 statusUpdate(intent);
1264             }
1265         };
1266 
ChildStatusIntentReceiver(SparseIntArray remainingSessions, IntentSender statusReceiver)1267         private ChildStatusIntentReceiver(SparseIntArray remainingSessions,
1268                 IntentSender statusReceiver) {
1269             this.mChildSessionsRemaining = remainingSessions;
1270             this.mStatusReceiver = statusReceiver;
1271         }
1272 
getIntentSender()1273         public IntentSender getIntentSender() {
1274             return new IntentSender((IIntentSender) mLocalSender);
1275         }
1276 
statusUpdate(Intent intent)1277         public void statusUpdate(Intent intent) {
1278             mHandler.post(() -> {
1279                 if (mChildSessionsRemaining.size() == 0) {
1280                     // no children to deal with, ignore.
1281                     return;
1282                 }
1283                 final boolean destroyed;
1284                 synchronized (mLock) {
1285                     destroyed = mDestroyed;
1286                 }
1287                 if (destroyed) {
1288                     // the parent has already been terminated, ignore.
1289                     return;
1290                 }
1291                 final int sessionId = intent.getIntExtra(
1292                         PackageInstaller.EXTRA_SESSION_ID, 0);
1293                 final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
1294                         PackageInstaller.STATUS_FAILURE);
1295                 final int sessionIndex = mChildSessionsRemaining.indexOfKey(sessionId);
1296                 if (PackageInstaller.STATUS_SUCCESS == status) {
1297                     mChildSessionsRemaining.removeAt(sessionIndex);
1298                     if (mChildSessionsRemaining.size() == 0) {
1299                         try {
1300                             intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
1301                                     PackageInstallerSession.this.sessionId);
1302                             mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
1303                         } catch (IntentSender.SendIntentException ignore) {
1304                         }
1305                     }
1306                 } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) {
1307                     try {
1308                         mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
1309                     } catch (IntentSender.SendIntentException ignore) {
1310                     }
1311                 } else { // failure, let's forward and clean up this session.
1312                     intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
1313                             PackageInstallerSession.this.sessionId);
1314                     mChildSessionsRemaining.clear(); // we're done. Don't send any more.
1315                     try {
1316                         mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
1317                     } catch (IntentSender.SendIntentException ignore) {
1318                     }
1319                 }
1320             });
1321         }
1322     }
1323 
1324     /** {@hide} */
1325     private class StreamingException extends Exception {
StreamingException(Throwable cause)1326         StreamingException(Throwable cause) {
1327             super(cause);
1328         }
1329     }
1330 
1331     /**
1332      * Returns whether or not a package can be installed while Secure FRP is enabled.
1333      * <p>
1334      * Only callers with the INSTALL_PACKAGES permission are allowed to install. However,
1335      * prevent the package installer from installing anything because, while it has the
1336      * permission, it will allows packages to be installed from anywhere.
1337      */
isSecureFrpInstallAllowed(Context context, int callingUid)1338     private static boolean isSecureFrpInstallAllowed(Context context, int callingUid) {
1339         final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
1340         final String[] systemInstaller = pmi.getKnownPackageNames(
1341                 PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM);
1342         final AndroidPackage callingInstaller = pmi.getPackage(callingUid);
1343         if (callingInstaller != null
1344                 && ArrayUtils.contains(systemInstaller, callingInstaller.getPackageName())) {
1345             // don't allow the system package installer to install while under secure FRP
1346             return false;
1347         }
1348 
1349         // require caller to hold the INSTALL_PACKAGES permission
1350         return context.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
1351                 == PackageManager.PERMISSION_GRANTED;
1352     }
1353 
1354     /**
1355      * Checks if the package can be installed on IncFs.
1356      */
isIncrementalInstallationAllowed(String packageName)1357     private static boolean isIncrementalInstallationAllowed(String packageName) {
1358         final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
1359         final PackageSetting existingPkgSetting = pmi.getPackageSetting(packageName);
1360         if (existingPkgSetting == null || existingPkgSetting.pkg == null) {
1361             return true;
1362         }
1363 
1364         return !existingPkgSetting.pkg.isSystem()
1365                 && !existingPkgSetting.getPkgState().isUpdatedSystemApp();
1366     }
1367 
1368     /**
1369      * If this was not already called, the session will be sealed.
1370      *
1371      * This method may be called multiple times to update the status receiver validate caller
1372      * permissions.
1373      */
markAsSealed(@onNull IntentSender statusReceiver, boolean forTransfer)1374     private boolean markAsSealed(@NonNull IntentSender statusReceiver, boolean forTransfer) {
1375         Objects.requireNonNull(statusReceiver);
1376 
1377         List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
1378 
1379         synchronized (mLock) {
1380             assertCallerIsOwnerOrRootLocked();
1381             assertPreparedAndNotDestroyedLocked("commit");
1382             assertNoWriteFileTransfersOpenLocked();
1383 
1384             final boolean isSecureFrpEnabled =
1385                     (Secure.getInt(mContext.getContentResolver(), Secure.SECURE_FRP_MODE, 0) == 1);
1386             if (isSecureFrpEnabled
1387                     && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
1388                 throw new SecurityException("Can't install packages while in secure FRP");
1389             }
1390 
1391             if (forTransfer) {
1392                 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
1393                 if (mInstallerUid == mOriginalInstallerUid) {
1394                     throw new IllegalArgumentException("Session has not been transferred");
1395                 }
1396             } else {
1397                 if (mInstallerUid != mOriginalInstallerUid) {
1398                     throw new IllegalArgumentException("Session has been transferred");
1399                 }
1400             }
1401 
1402             mRemoteStatusReceiver = statusReceiver;
1403 
1404             // After updating the observer, we can skip re-sealing.
1405             if (mSealed) {
1406                 return true;
1407             }
1408 
1409             try {
1410                 sealLocked(childSessions);
1411             } catch (PackageManagerException e) {
1412                 return false;
1413             }
1414         }
1415 
1416         // Persist the fact that we've sealed ourselves to prevent
1417         // mutations of any hard links we create. We do this without holding
1418         // the session lock, since otherwise it's a lock inversion.
1419         mCallback.onSessionSealedBlocking(this);
1420 
1421         return true;
1422     }
1423 
1424     /**
1425      * Returns true if the session is successfully validated and committed. Returns false if the
1426      * dataloader could not be prepared. This can be called multiple times so long as no
1427      * exception is thrown.
1428      * @throws PackageManagerException on an unrecoverable error.
1429      */
streamValidateAndCommit()1430     private boolean streamValidateAndCommit() throws PackageManagerException {
1431         // TODO(patb): since the work done here for a parent session in a multi-package install is
1432         //             mostly superficial, consider splitting this method for the parent and
1433         //             single / child sessions.
1434         synchronized (mLock) {
1435             if (mCommitted) {
1436                 return true;
1437             }
1438 
1439             if (!streamAndValidateLocked()) {
1440                 return false;
1441             }
1442 
1443             // Client staging is fully done at this point
1444             mClientProgress = 1f;
1445             computeProgressLocked(true);
1446 
1447             // This ongoing commit should keep session active, even though client
1448             // will probably close their end.
1449             mActiveCount.incrementAndGet();
1450 
1451             mCommitted = true;
1452         }
1453         return true;
1454     }
1455 
1456     /** Return a list of child sessions or null if the session is not multipackage
1457      *
1458      * <p> This method is handy to prevent potential deadlocks (b/123391593)
1459      */
getChildSessionsNotLocked()1460     private @Nullable List<PackageInstallerSession> getChildSessionsNotLocked() {
1461         if (Thread.holdsLock(mLock)) {
1462             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
1463                     + " is holding mLock", new Throwable());
1464         }
1465         List<PackageInstallerSession> childSessions = null;
1466         if (isMultiPackage()) {
1467             final int[] childSessionIds = getChildSessionIds();
1468             childSessions = new ArrayList<>(childSessionIds.length);
1469             for (int childSessionId : childSessionIds) {
1470                 childSessions.add(mSessionProvider.getSession(childSessionId));
1471             }
1472         }
1473         return childSessions;
1474     }
1475 
1476     /**
1477      * Assert multipackage install has consistent sessions.
1478      *
1479      * @throws PackageManagerException if child sessions don't match parent session
1480      *                                  in respect to staged and enable rollback parameters.
1481      */
1482     @GuardedBy("mLock")
assertMultiPackageConsistencyLocked( @onNull List<PackageInstallerSession> childSessions)1483     private void assertMultiPackageConsistencyLocked(
1484             @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException {
1485         for (PackageInstallerSession childSession : childSessions) {
1486             // It might be that the parent session is loaded before all of it's child sessions are,
1487             // e.g. when reading sessions from XML. Those sessions will be null here, and their
1488             // conformance with the multipackage params will be checked when they're loaded.
1489             if (childSession == null) {
1490                 continue;
1491             }
1492             assertConsistencyWithLocked(childSession);
1493         }
1494     }
1495 
1496     /**
1497      * Assert consistency with the given session.
1498      *
1499      * @throws PackageManagerException if other sessions doesn't match this session
1500      *                                  in respect to staged and enable rollback parameters.
1501      */
1502     @GuardedBy("mLock")
assertConsistencyWithLocked(PackageInstallerSession other)1503     private void assertConsistencyWithLocked(PackageInstallerSession other)
1504             throws PackageManagerException {
1505         // Session groups must be consistent wrt to isStaged parameter. Non-staging session
1506         // cannot be grouped with staging sessions.
1507         if (this.params.isStaged != other.params.isStaged) {
1508             throw new PackageManagerException(
1509                 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
1510                 "Multipackage Inconsistency: session " + other.sessionId
1511                     + " and session " + sessionId
1512                     + " have inconsistent staged settings");
1513         }
1514         if (this.params.getEnableRollback() != other.params.getEnableRollback()) {
1515             throw new PackageManagerException(
1516                 PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
1517                 "Multipackage Inconsistency: session " + other.sessionId
1518                     + " and session " + sessionId
1519                     + " have inconsistent rollback settings");
1520         }
1521     }
1522 
1523     /**
1524      * Seal the session to prevent further modification.
1525      *
1526      * <p>The session will be sealed after calling this method even if it failed.
1527      *
1528      * @throws PackageManagerException if the session was sealed but something went wrong. If the
1529      *                                 session was sealed this is the only possible exception.
1530      */
1531     @GuardedBy("mLock")
sealLocked(List<PackageInstallerSession> childSessions)1532     private void sealLocked(List<PackageInstallerSession> childSessions)
1533             throws PackageManagerException {
1534         try {
1535             assertNoWriteFileTransfersOpenLocked();
1536             assertPreparedAndNotDestroyedLocked("sealing of session");
1537 
1538             mSealed = true;
1539 
1540             if (childSessions != null) {
1541                 assertMultiPackageConsistencyLocked(childSessions);
1542             }
1543         } catch (PackageManagerException e) {
1544             throw onSessionVerificationFailure(e);
1545         } catch (Throwable e) {
1546             // Convert all exceptions into package manager exceptions as only those are handled
1547             // in the code above.
1548             throw onSessionVerificationFailure(new PackageManagerException(e));
1549         }
1550     }
1551 
1552     /**
1553      * Prepare DataLoader and stream content for DataLoader sessions.
1554      * Validate the contents of all session.
1555      *
1556      * @return false if the data loader could not be prepared.
1557      * @throws PackageManagerException when an unrecoverable exception is encountered
1558      */
1559     @GuardedBy("mLock")
streamAndValidateLocked()1560     private boolean streamAndValidateLocked() throws PackageManagerException {
1561         try {
1562             // Read transfers from the original owner stay open, but as the session's data cannot
1563             // be modified anymore, there is no leak of information. For staged sessions, further
1564             // validation is performed by the staging manager.
1565             if (!params.isMultiPackage) {
1566                 if (!prepareDataLoaderLocked()) {
1567                     return false;
1568                 }
1569 
1570                 if (isApexInstallation()) {
1571                     validateApexInstallLocked();
1572                 } else {
1573                     validateApkInstallLocked();
1574                 }
1575             }
1576 
1577             if (params.isStaged) {
1578                 mStagingManager.checkNonOverlappingWithStagedSessions(this);
1579             }
1580             return true;
1581         } catch (PackageManagerException e) {
1582             throw onSessionVerificationFailure(e);
1583         } catch (Throwable e) {
1584             // Convert all exceptions into package manager exceptions as only those are handled
1585             // in the code above.
1586             throw onSessionVerificationFailure(new PackageManagerException(e));
1587         }
1588     }
1589 
onSessionVerificationFailure(PackageManagerException e)1590     private PackageManagerException onSessionVerificationFailure(PackageManagerException e) {
1591         onSessionVerificationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
1592         return e;
1593     }
1594 
onSessionVerificationFailure(int error, String detailMessage)1595     private void onSessionVerificationFailure(int error, String detailMessage) {
1596         // Session is sealed but could not be verified, we need to destroy it.
1597         destroyInternal();
1598         // Dispatch message to remove session from PackageInstallerService.
1599         dispatchSessionFinished(error, detailMessage, null);
1600         // TODO(b/173194203): clean up staged session in destroyInternal() call instead
1601         if (isStaged() && stageDir != null) {
1602             cleanStageDir();
1603         }
1604     }
1605 
onStorageUnhealthy()1606     private void onStorageUnhealthy() {
1607         if (TextUtils.isEmpty(mPackageName)) {
1608             // The package has not been installed.
1609             return;
1610         }
1611         final PackageManagerService packageManagerService = mPm;
1612         final String packageName = mPackageName;
1613         mHandler.post(() -> {
1614             if (packageManagerService.deletePackageX(packageName,
1615                     PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
1616                     PackageManager.DELETE_ALL_USERS) != PackageManager.DELETE_SUCCEEDED) {
1617                 Slog.e(TAG, "Failed to uninstall package with failed dataloader: " + packageName);
1618             }
1619         });
1620     }
1621 
1622     /**
1623      * If session should be sealed, then it's sealed to prevent further modification.
1624      * If the session can't be sealed then it's destroyed.
1625      *
1626      * Additionally for staged APEX sessions read+validate the package and populate req'd fields.
1627      *
1628      * <p> This is meant to be called after all of the sessions are loaded and added to
1629      * PackageInstallerService
1630      */
onAfterSessionRead()1631     void onAfterSessionRead() {
1632         synchronized (mLock) {
1633             if (!mShouldBeSealed || isStagedAndInTerminalState()) {
1634                 return;
1635             }
1636         }
1637         List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
1638         synchronized (mLock) {
1639             try {
1640                 sealLocked(childSessions);
1641 
1642                 if (isApexInstallation()) {
1643                     // APEX installations rely on certain fields to be populated after reboot.
1644                     // E.g. mPackageName.
1645                     validateApexInstallLocked();
1646                 }
1647             } catch (PackageManagerException e) {
1648                 Slog.e(TAG, "Package not valid", e);
1649             }
1650         }
1651     }
1652 
1653     /** Update the timestamp of when the staged session last changed state */
markUpdated()1654     public void markUpdated() {
1655         synchronized (mLock) {
1656             this.updatedMillis = System.currentTimeMillis();
1657         }
1658     }
1659 
1660     @Override
transfer(String packageName)1661     public void transfer(String packageName) {
1662         Objects.requireNonNull(packageName);
1663 
1664         ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
1665         if (newOwnerAppInfo == null) {
1666             throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
1667         }
1668 
1669         if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
1670                 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
1671             throw new SecurityException("Destination package " + packageName + " does not have "
1672                     + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
1673         }
1674 
1675         // Only install flags that can be verified by the app the session is transferred to are
1676         // allowed. The parameters can be read via PackageInstaller.SessionInfo.
1677         if (!params.areHiddenOptionsSet()) {
1678             throw new SecurityException("Can only transfer sessions that use public options");
1679         }
1680 
1681         List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
1682 
1683         synchronized (mLock) {
1684             assertCallerIsOwnerOrRootLocked();
1685             assertPreparedAndNotSealedLocked("transfer");
1686 
1687             try {
1688                 sealLocked(childSessions);
1689             } catch (PackageManagerException e) {
1690                 throw new IllegalArgumentException("Package is not valid", e);
1691             }
1692 
1693             mInstallerUid = newOwnerAppInfo.uid;
1694             mInstallSource = InstallSource.create(packageName, null, packageName);
1695         }
1696 
1697         // Persist the fact that we've sealed ourselves to prevent
1698         // mutations of any hard links we create. We do this without holding
1699         // the session lock, since otherwise it's a lock inversion.
1700         mCallback.onSessionSealedBlocking(this);
1701     }
1702 
handleInstall()1703     private void handleInstall() {
1704         if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
1705             DevicePolicyEventLogger
1706                     .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
1707                     .setAdmin(mInstallSource.installerPackageName)
1708                     .write();
1709         }
1710         if (params.isStaged) {
1711             mStagingManager.commitSession(this);
1712             destroyInternal();
1713             dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
1714             return;
1715         }
1716 
1717         if (isApexInstallation()) {
1718             destroyInternal();
1719             dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
1720                     "APEX packages can only be installed using staged sessions.", null);
1721             return;
1722         }
1723 
1724         // For a multiPackage session, read the child sessions
1725         // outside of the lock, because reading the child
1726         // sessions with the lock held could lead to deadlock
1727         // (b/123391593).
1728         List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
1729 
1730         try {
1731             synchronized (mLock) {
1732                 installNonStagedLocked(childSessions);
1733             }
1734         } catch (PackageManagerException e) {
1735             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
1736             Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
1737             destroyInternal();
1738             dispatchSessionFinished(e.error, completeMsg, null);
1739         }
1740     }
1741 
1742     @GuardedBy("mLock")
installNonStagedLocked(List<PackageInstallerSession> childSessions)1743     private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
1744             throws PackageManagerException {
1745         final PackageManagerService.ActiveInstallSession installingSession =
1746                 makeSessionActiveLocked();
1747         if (installingSession == null) {
1748             return;
1749         }
1750         if (isMultiPackage()) {
1751             List<PackageManagerService.ActiveInstallSession> installingChildSessions =
1752                     new ArrayList<>(childSessions.size());
1753             boolean success = true;
1754             PackageManagerException failure = null;
1755             for (int i = 0; i < childSessions.size(); ++i) {
1756                 final PackageInstallerSession session = childSessions.get(i);
1757                 try {
1758                     final PackageManagerService.ActiveInstallSession installingChildSession =
1759                             session.makeSessionActiveLocked();
1760                     if (installingChildSession != null) {
1761                         installingChildSessions.add(installingChildSession);
1762                     }
1763                 } catch (PackageManagerException e) {
1764                     failure = e;
1765                     success = false;
1766                 }
1767             }
1768             if (!success) {
1769                 sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
1770                         isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
1771                         failure.error, failure.getLocalizedMessage(), null);
1772                 return;
1773             }
1774             mPm.installStage(installingChildSessions);
1775         } else {
1776             mPm.installStage(installingSession);
1777         }
1778     }
1779 
1780     /**
1781      * Stages this session for install and returns a
1782      * {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
1783      * in case permissions need to be requested before install can proceed.
1784      */
1785     @GuardedBy("mLock")
makeSessionActiveLocked()1786     private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
1787             throws PackageManagerException {
1788         if (mRelinquished) {
1789             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1790                     "Session relinquished");
1791         }
1792         if (mDestroyed) {
1793             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
1794         }
1795         if (!mSealed) {
1796             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
1797         }
1798 
1799         final IPackageInstallObserver2 localObserver;
1800         if (isApexInstallation()) {
1801             localObserver = null;
1802         } else {
1803             if (!params.isMultiPackage) {
1804                 Objects.requireNonNull(mPackageName);
1805                 Objects.requireNonNull(mSigningDetails);
1806                 Objects.requireNonNull(mResolvedBaseFile);
1807 
1808                 if (needToAskForPermissionsLocked()) {
1809                     // User needs to confirm installation;
1810                     // give installer an intent they can use to involve
1811                     // user.
1812                     final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
1813                     intent.setPackage(mPm.getPackageInstallerPackageName());
1814                     intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
1815 
1816                     sendOnUserActionRequired(mContext, mRemoteStatusReceiver, sessionId, intent);
1817 
1818                     // Commit was keeping session marked as active until now; release
1819                     // that extra refcount so session appears idle.
1820                     closeInternal(false);
1821                     return null;
1822                 }
1823 
1824                 // Inherit any packages and native libraries from existing install that
1825                 // haven't been overridden.
1826                 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
1827                     try {
1828                         final List<File> fromFiles = mResolvedInheritedFiles;
1829                         final File toDir = stageDir;
1830 
1831                         if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
1832                         if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
1833                             throw new IllegalStateException("mInheritedFilesBase == null");
1834                         }
1835 
1836                         if (isLinkPossible(fromFiles, toDir)) {
1837                             if (!mResolvedInstructionSets.isEmpty()) {
1838                                 final File oatDir = new File(toDir, "oat");
1839                                 createOatDirs(mResolvedInstructionSets, oatDir);
1840                             }
1841                             // pre-create lib dirs for linking if necessary
1842                             if (!mResolvedNativeLibPaths.isEmpty()) {
1843                                 for (String libPath : mResolvedNativeLibPaths) {
1844                                     // "/lib/arm64" -> ["lib", "arm64"]
1845                                     final int splitIndex = libPath.lastIndexOf('/');
1846                                     if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
1847                                         Slog.e(TAG,
1848                                                 "Skipping native library creation for linking due"
1849                                                         + " to invalid path: " + libPath);
1850                                         continue;
1851                                     }
1852                                     final String libDirPath = libPath.substring(1, splitIndex);
1853                                     final File libDir = new File(toDir, libDirPath);
1854                                     if (!libDir.exists()) {
1855                                         NativeLibraryHelper.createNativeLibrarySubdir(libDir);
1856                                     }
1857                                     final String archDirPath = libPath.substring(splitIndex + 1);
1858                                     NativeLibraryHelper.createNativeLibrarySubdir(
1859                                             new File(libDir, archDirPath));
1860                                 }
1861                             }
1862                             linkFiles(fromFiles, toDir, mInheritedFilesBase);
1863                         } else {
1864                             // TODO: this should delegate to DCS so the system process
1865                             // avoids holding open FDs into containers.
1866                             copyFiles(fromFiles, toDir);
1867                         }
1868                     } catch (IOException e) {
1869                         throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
1870                                 "Failed to inherit existing install", e);
1871                     }
1872                 }
1873 
1874                 // TODO: surface more granular state from dexopt
1875                 mInternalProgress = 0.5f;
1876                 computeProgressLocked(true);
1877 
1878                 extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
1879             }
1880 
1881             // We've reached point of no return; call into PMS to install the stage.
1882             // Regardless of success or failure we always destroy session.
1883             localObserver = new IPackageInstallObserver2.Stub() {
1884                 @Override
1885                 public void onUserActionRequired(Intent intent) {
1886                     throw new IllegalStateException();
1887                 }
1888 
1889                 @Override
1890                 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
1891                         Bundle extras) {
1892                     destroyInternal();
1893                     dispatchSessionFinished(returnCode, msg, extras);
1894                 }
1895             };
1896         }
1897 
1898         final UserHandle user;
1899         if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
1900             user = UserHandle.ALL;
1901         } else {
1902             user = new UserHandle(userId);
1903         }
1904 
1905         mRelinquished = true;
1906         return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir, localObserver,
1907                 sessionId, params, mInstallerUid, mInstallSource, user, mSigningDetails);
1908     }
1909 
maybeRenameFile(File from, File to)1910     private static void maybeRenameFile(File from, File to) throws PackageManagerException {
1911         if (!from.equals(to)) {
1912             if (!from.renameTo(to)) {
1913                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1914                         "Could not rename file " + from + " to " + to);
1915             }
1916         }
1917     }
1918 
logDataLoaderInstallationSession(int returnCode)1919     private void logDataLoaderInstallationSession(int returnCode) {
1920         // Skip logging the side-loaded app installations, as those are private and aren't reported
1921         // anywhere; app stores already have a record of the installation and that's why reporting
1922         // it here is fine
1923         final String packageNameToLog =
1924                 (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? mPackageName : "";
1925         final long currentTimestamp = System.currentTimeMillis();
1926         FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
1927                 isIncrementalInstallation(),
1928                 packageNameToLog,
1929                 currentTimestamp - createdMillis,
1930                 returnCode,
1931                 getApksSize());
1932     }
1933 
getApksSize()1934     private long getApksSize() {
1935         final PackageSetting ps = mPm.getPackageSetting(mPackageName);
1936         if (ps == null) {
1937             return 0;
1938         }
1939         final File apkDirOrPath = ps.codePath;
1940         if (apkDirOrPath == null) {
1941             return 0;
1942         }
1943         if (apkDirOrPath.isFile() && apkDirOrPath.getName().toLowerCase().endsWith(".apk")) {
1944             return apkDirOrPath.length();
1945         }
1946         if (!apkDirOrPath.isDirectory()) {
1947             return 0;
1948         }
1949         final File[] files = apkDirOrPath.listFiles();
1950         long apksSize = 0;
1951         for (int i = 0; i < files.length; i++) {
1952             if (files[i].getName().toLowerCase().endsWith(".apk")) {
1953                 apksSize += files[i].length();
1954             }
1955         }
1956         return apksSize;
1957     }
1958 
1959     /**
1960      * Returns true if the session should attempt to inherit any existing native libraries already
1961      * extracted at the current install location. This is necessary to prevent double loading of
1962      * native libraries already loaded by the running app.
1963      */
mayInheritNativeLibs()1964     private boolean mayInheritNativeLibs() {
1965         return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) &&
1966                 params.mode == SessionParams.MODE_INHERIT_EXISTING &&
1967                 (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
1968     }
1969 
1970     /**
1971      * Returns true if the session is installing an APEX package.
1972      */
isApexInstallation()1973     private boolean isApexInstallation() {
1974         return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
1975     }
1976 
1977     /**
1978      * Validate apex install.
1979      * <p>
1980      * Sets {@link #mResolvedBaseFile} for RollbackManager to use. Sets {@link #mPackageName} for
1981      * StagingManager to use.
1982      */
1983     @GuardedBy("mLock")
validateApexInstallLocked()1984     private void validateApexInstallLocked()
1985             throws PackageManagerException {
1986         final List<File> addedFiles = getAddedApksLocked();
1987         if (addedFiles.isEmpty()) {
1988             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1989                     String.format("Session: %d. No packages staged in %s", sessionId,
1990                           stageDir.getAbsolutePath()));
1991         }
1992 
1993         if (ArrayUtils.size(addedFiles) > 1) {
1994             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1995                     "Too many files for apex install");
1996         }
1997 
1998         File addedFile = addedFiles.get(0); // there is only one file
1999 
2000         // Ensure file name has proper suffix
2001         final String sourceName = addedFile.getName();
2002         final String targetName = sourceName.endsWith(APEX_FILE_EXTENSION)
2003                 ? sourceName
2004                 : sourceName + APEX_FILE_EXTENSION;
2005         if (!FileUtils.isValidExtFilename(targetName)) {
2006             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2007                     "Invalid filename: " + targetName);
2008         }
2009 
2010         final File targetFile = new File(stageDir, targetName);
2011         resolveAndStageFile(addedFile, targetFile);
2012         mResolvedBaseFile = targetFile;
2013 
2014         // Populate package name of the apex session
2015         mPackageName = null;
2016         final ApkLite apk;
2017         try {
2018             apk = PackageParser.parseApkLite(
2019                     mResolvedBaseFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
2020         } catch (PackageParserException e) {
2021             throw PackageManagerException.from(e);
2022         }
2023 
2024         if (mPackageName == null) {
2025             mPackageName = apk.packageName;
2026             mVersionCode = apk.getLongVersionCode();
2027         }
2028     }
2029 
2030     /**
2031      * Validate install by confirming that all application packages are have
2032      * consistent package name, version code, and signing certificates.
2033      * <p>
2034      * Clears and populates {@link #mResolvedBaseFile},
2035      * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
2036      * <p>
2037      * Renames package files in stage to match split names defined inside.
2038      * <p>
2039      * Note that upgrade compatibility is still performed by
2040      * {@link PackageManagerService}.
2041      */
2042     @GuardedBy("mLock")
validateApkInstallLocked()2043     private void validateApkInstallLocked() throws PackageManagerException {
2044         ApkLite baseApk = null;
2045         mPackageName = null;
2046         mVersionCode = -1;
2047         mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
2048 
2049         mResolvedBaseFile = null;
2050         mResolvedStagedFiles.clear();
2051         mResolvedInheritedFiles.clear();
2052 
2053         final PackageInfo pkgInfo = mPm.getPackageInfo(
2054                 params.appPackageName, PackageManager.GET_SIGNATURES
2055                         | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
2056 
2057         // Partial installs must be consistent with existing install
2058         if (params.mode == SessionParams.MODE_INHERIT_EXISTING
2059                 && (pkgInfo == null || pkgInfo.applicationInfo == null)) {
2060             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2061                     "Missing existing base package");
2062         }
2063         // Default to require only if existing base has fs-verity.
2064         mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
2065                 && params.mode == SessionParams.MODE_INHERIT_EXISTING
2066                 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
2067 
2068         final List<File> removedFiles = getRemovedFilesLocked();
2069         final List<String> removeSplitList = new ArrayList<>();
2070         if (!removedFiles.isEmpty()) {
2071             for (File removedFile : removedFiles) {
2072                 final String fileName = removedFile.getName();
2073                 final String splitName = fileName.substring(
2074                         0, fileName.length() - REMOVE_MARKER_EXTENSION.length());
2075                 removeSplitList.add(splitName);
2076             }
2077         }
2078 
2079         final List<File> addedFiles = getAddedApksLocked();
2080         if (addedFiles.isEmpty() && removeSplitList.size() == 0) {
2081             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2082                     String.format("Session: %d. No packages staged in %s", sessionId,
2083                           stageDir.getAbsolutePath()));
2084         }
2085 
2086         // Verify that all staged packages are internally consistent
2087         final ArraySet<String> stagedSplits = new ArraySet<>();
2088         ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
2089         for (File addedFile : addedFiles) {
2090             ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
2091                     addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
2092             if (result.isError()) {
2093                 throw new PackageManagerException(result.getErrorCode(),
2094                         result.getErrorMessage(), result.getException());
2095             }
2096 
2097             final ApkLite apk = result.getResult();
2098             if (!stagedSplits.add(apk.splitName)) {
2099                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2100                         "Split " + apk.splitName + " was defined multiple times");
2101             }
2102 
2103             // Use first package to define unknown values
2104             if (mPackageName == null) {
2105                 mPackageName = apk.packageName;
2106                 mVersionCode = apk.getLongVersionCode();
2107             }
2108             if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
2109                 mSigningDetails = apk.signingDetails;
2110             }
2111 
2112             assertApkConsistentLocked(String.valueOf(addedFile), apk);
2113 
2114             // Take this opportunity to enforce uniform naming
2115             final String targetName;
2116             if (apk.splitName == null) {
2117                 targetName = "base" + APK_FILE_EXTENSION;
2118             } else {
2119                 targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
2120             }
2121             if (!FileUtils.isValidExtFilename(targetName)) {
2122                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2123                         "Invalid filename: " + targetName);
2124             }
2125 
2126             final File targetFile = new File(stageDir, targetName);
2127             resolveAndStageFile(addedFile, targetFile);
2128 
2129             // Base is coming from session
2130             if (apk.splitName == null) {
2131                 mResolvedBaseFile = targetFile;
2132                 baseApk = apk;
2133             }
2134 
2135             // Validate and add Dex Metadata (.dm).
2136             final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
2137             if (dexMetadataFile != null) {
2138                 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
2139                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2140                             "Invalid filename: " + dexMetadataFile);
2141                 }
2142                 final File targetDexMetadataFile = new File(stageDir,
2143                         DexMetadataHelper.buildDexMetadataPathForApk(targetName));
2144                 resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
2145             }
2146         }
2147 
2148         if (removeSplitList.size() > 0) {
2149             if (pkgInfo == null) {
2150                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2151                         "Missing existing base package for " + mPackageName);
2152             }
2153 
2154             // validate split names marked for removal
2155             for (String splitName : removeSplitList) {
2156                 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
2157                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2158                             "Split not found: " + splitName);
2159                 }
2160             }
2161 
2162             // ensure we've got appropriate package name, version code and signatures
2163             if (mPackageName == null) {
2164                 mPackageName = pkgInfo.packageName;
2165                 mVersionCode = pkgInfo.getLongVersionCode();
2166             }
2167             if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
2168                 try {
2169                     mSigningDetails = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
2170                             pkgInfo.applicationInfo.sourceDir,
2171                             PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
2172                 } catch (PackageParserException e) {
2173                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2174                             "Couldn't obtain signatures from base APK");
2175                 }
2176             }
2177         }
2178 
2179         if (mInstallerUid != mOriginalInstallerUid) {
2180             // Session has been transferred, check package name.
2181             if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals(
2182                     mOriginalInstallerPackageName)) {
2183                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
2184                         "Can only transfer sessions that update the original installer");
2185             }
2186         }
2187 
2188         if (params.mode == SessionParams.MODE_FULL_INSTALL) {
2189             // Full installs must include a base package
2190             if (!stagedSplits.contains(null)) {
2191                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2192                         "Full install must include a base package");
2193             }
2194 
2195         } else {
2196             ApplicationInfo appInfo = pkgInfo.applicationInfo;
2197             ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite(
2198                     input.reset(), new File(appInfo.getCodePath()), 0);
2199             if (pkgLiteResult.isError()) {
2200                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
2201                         pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
2202             }
2203             final PackageLite existing = pkgLiteResult.getResult();
2204             ParseResult<ApkLite> apkLiteResult = ApkLiteParseUtils.parseApkLite(input.reset(),
2205                     new File(appInfo.getBaseCodePath()),
2206                     PackageParser.PARSE_COLLECT_CERTIFICATES);
2207             if (apkLiteResult.isError()) {
2208                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
2209                         apkLiteResult.getErrorMessage(), apkLiteResult.getException());
2210             }
2211             final ApkLite existingBase = apkLiteResult.getResult();
2212 
2213             assertApkConsistentLocked("Existing base", existingBase);
2214 
2215             // Inherit base if not overridden
2216             if (mResolvedBaseFile == null) {
2217                 mResolvedBaseFile = new File(appInfo.getBaseCodePath());
2218                 resolveInheritedFile(mResolvedBaseFile);
2219                 // Inherit the dex metadata if present.
2220                 final File baseDexMetadataFile =
2221                         DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
2222                 if (baseDexMetadataFile != null) {
2223                     resolveInheritedFile(baseDexMetadataFile);
2224                 }
2225                 baseApk = existingBase;
2226             }
2227 
2228             // Inherit splits if not overridden
2229             if (!ArrayUtils.isEmpty(existing.splitNames)) {
2230                 for (int i = 0; i < existing.splitNames.length; i++) {
2231                     final String splitName = existing.splitNames[i];
2232                     final File splitFile = new File(existing.splitCodePaths[i]);
2233                     final boolean splitRemoved = removeSplitList.contains(splitName);
2234                     if (!stagedSplits.contains(splitName) && !splitRemoved) {
2235                         resolveInheritedFile(splitFile);
2236                         // Inherit the dex metadata if present.
2237                         final File splitDexMetadataFile =
2238                                 DexMetadataHelper.findDexMetadataForFile(splitFile);
2239                         if (splitDexMetadataFile != null) {
2240                             resolveInheritedFile(splitDexMetadataFile);
2241                         }
2242                     }
2243                 }
2244             }
2245 
2246             // Inherit compiled oat directory.
2247             final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
2248             mInheritedFilesBase = packageInstallDir;
2249             final File oatDir = new File(packageInstallDir, "oat");
2250             if (oatDir.exists()) {
2251                 final File[] archSubdirs = oatDir.listFiles();
2252 
2253                 // Keep track of all instruction sets we've seen compiled output for.
2254                 // If we're linking (and not copying) inherited files, we can recreate the
2255                 // instruction set hierarchy and link compiled output.
2256                 if (archSubdirs != null && archSubdirs.length > 0) {
2257                     final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
2258                     for (File archSubDir : archSubdirs) {
2259                         // Skip any directory that isn't an ISA subdir.
2260                         if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
2261                             continue;
2262                         }
2263 
2264                         File[] files = archSubDir.listFiles();
2265                         if (files == null || files.length == 0) {
2266                             continue;
2267                         }
2268 
2269                         mResolvedInstructionSets.add(archSubDir.getName());
2270                         mResolvedInheritedFiles.addAll(Arrays.asList(files));
2271                     }
2272                 }
2273             }
2274 
2275             // Inherit native libraries for DONT_KILL sessions.
2276             if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
2277                 File[] libDirs = new File[]{
2278                         new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME),
2279                         new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)};
2280                 for (File libDir : libDirs) {
2281                     if (!libDir.exists() || !libDir.isDirectory()) {
2282                         continue;
2283                     }
2284                     final List<String> libDirsToInherit = new ArrayList<>();
2285                     final List<File> libFilesToInherit = new ArrayList<>();
2286                     for (File archSubDir : libDir.listFiles()) {
2287                         if (!archSubDir.isDirectory()) {
2288                             continue;
2289                         }
2290                         String relLibPath;
2291                         try {
2292                             relLibPath = getRelativePath(archSubDir, packageInstallDir);
2293                         } catch (IOException e) {
2294                             Slog.e(TAG, "Skipping linking of native library directory!", e);
2295                             // shouldn't be possible, but let's avoid inheriting these to be safe
2296                             libDirsToInherit.clear();
2297                             libFilesToInherit.clear();
2298                             break;
2299                         }
2300 
2301                         File[] files = archSubDir.listFiles();
2302                         if (files == null || files.length == 0) {
2303                             continue;
2304                         }
2305 
2306                         libDirsToInherit.add(relLibPath);
2307                         libFilesToInherit.addAll(Arrays.asList(files));
2308                     }
2309                     for (String subDir : libDirsToInherit) {
2310                         if (!mResolvedNativeLibPaths.contains(subDir)) {
2311                             mResolvedNativeLibPaths.add(subDir);
2312                         }
2313                     }
2314                     mResolvedInheritedFiles.addAll(libFilesToInherit);
2315                 }
2316             }
2317         }
2318         if (baseApk.useEmbeddedDex) {
2319             for (File file : mResolvedStagedFiles) {
2320                 if (file.getName().endsWith(".apk")
2321                         && !DexManager.auditUncompressedDexInApk(file.getPath())) {
2322                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2323                             "Some dex are not uncompressed and aligned correctly for "
2324                             + mPackageName);
2325                 }
2326             }
2327         }
2328         if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
2329             throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
2330                     "Missing split for " + mPackageName);
2331         }
2332 
2333         final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
2334         if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
2335             if (!baseApk.debuggable && !baseApk.profilableByShell) {
2336                 mIncrementalFileStorages.disableReadLogs();
2337             }
2338         }
2339     }
2340 
resolveAndStageFile(File origFile, File targetFile)2341     private void resolveAndStageFile(File origFile, File targetFile)
2342             throws PackageManagerException {
2343         mResolvedStagedFiles.add(targetFile);
2344         maybeRenameFile(origFile, targetFile);
2345 
2346         final File originalSignature = new File(
2347                 VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
2348         // Make sure .fsv_sig exists when it should, then resolve and stage it.
2349         if (originalSignature.exists()) {
2350             // mVerityFound can only change from false to true here during the staging loop. Since
2351             // all or none of files should have .fsv_sig, this should only happen in the first time
2352             // (or never), otherwise bail out.
2353             if (!mVerityFound) {
2354                 mVerityFound = true;
2355                 if (mResolvedStagedFiles.size() > 1) {
2356                     throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
2357                             "Some file is missing fs-verity signature");
2358                 }
2359             }
2360         } else {
2361             if (!mVerityFound) {
2362                 return;
2363             }
2364             throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE,
2365                     "Missing corresponding fs-verity signature to " + origFile);
2366         }
2367 
2368         final File stagedSignature = new File(
2369                 VerityUtils.getFsveritySignatureFilePath(targetFile.getPath()));
2370         maybeRenameFile(originalSignature, stagedSignature);
2371         mResolvedStagedFiles.add(stagedSignature);
2372     }
2373 
resolveInheritedFile(File origFile)2374     private void resolveInheritedFile(File origFile) {
2375         mResolvedInheritedFiles.add(origFile);
2376 
2377         // Inherit the fsverity signature file if present.
2378         final File fsveritySignatureFile = new File(
2379                 VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
2380         if (fsveritySignatureFile.exists()) {
2381             mResolvedInheritedFiles.add(fsveritySignatureFile);
2382         }
2383     }
2384 
2385     @GuardedBy("mLock")
assertApkConsistentLocked(String tag, ApkLite apk)2386     private void assertApkConsistentLocked(String tag, ApkLite apk)
2387             throws PackageManagerException {
2388         if (!mPackageName.equals(apk.packageName)) {
2389             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
2390                     + apk.packageName + " inconsistent with " + mPackageName);
2391         }
2392         if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
2393             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
2394                     + " specified package " + params.appPackageName
2395                     + " inconsistent with " + apk.packageName);
2396         }
2397         if (mVersionCode != apk.getLongVersionCode()) {
2398             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
2399                     + " version code " + apk.versionCode + " inconsistent with "
2400                     + mVersionCode);
2401         }
2402         if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
2403             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
2404                     tag + " signatures are inconsistent");
2405         }
2406     }
2407 
2408     /**
2409      * Determine if creating hard links between source and destination is
2410      * possible. That is, do they all live on the same underlying device.
2411      */
isLinkPossible(List<File> fromFiles, File toDir)2412     private boolean isLinkPossible(List<File> fromFiles, File toDir) {
2413         try {
2414             final StructStat toStat = Os.stat(toDir.getAbsolutePath());
2415             for (File fromFile : fromFiles) {
2416                 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
2417                 if (fromStat.st_dev != toStat.st_dev) {
2418                     return false;
2419                 }
2420             }
2421         } catch (ErrnoException e) {
2422             Slog.w(TAG, "Failed to detect if linking possible: " + e);
2423             return false;
2424         }
2425         return true;
2426     }
2427 
2428     /**
2429      * @return the uid of the owner this session
2430      */
getInstallerUid()2431     public int getInstallerUid() {
2432         synchronized (mLock) {
2433             return mInstallerUid;
2434         }
2435     }
2436 
2437     /**
2438      * @return the package name of this session
2439      */
getPackageName()2440     String getPackageName() {
2441         synchronized (mLock) {
2442             return mPackageName;
2443         }
2444     }
2445 
2446     /**
2447      * @return the timestamp of when this session last changed state
2448      */
getUpdatedMillis()2449     public long getUpdatedMillis() {
2450         synchronized (mLock) {
2451             return updatedMillis;
2452         }
2453     }
2454 
getInstallerPackageName()2455     String getInstallerPackageName() {
2456         return getInstallSource().installerPackageName;
2457     }
2458 
getInstallSource()2459     InstallSource getInstallSource() {
2460         synchronized (mLock) {
2461             return mInstallSource;
2462         }
2463     }
2464 
getRelativePath(File file, File base)2465     private static String getRelativePath(File file, File base) throws IOException {
2466         final String pathStr = file.getAbsolutePath();
2467         final String baseStr = base.getAbsolutePath();
2468         // Don't allow relative paths.
2469         if (pathStr.contains("/.") ) {
2470             throw new IOException("Invalid path (was relative) : " + pathStr);
2471         }
2472 
2473         if (pathStr.startsWith(baseStr)) {
2474             return pathStr.substring(baseStr.length());
2475         }
2476 
2477         throw new IOException("File: " + pathStr + " outside base: " + baseStr);
2478     }
2479 
createOatDirs(List<String> instructionSets, File fromDir)2480     private void createOatDirs(List<String> instructionSets, File fromDir)
2481             throws PackageManagerException {
2482         for (String instructionSet : instructionSets) {
2483             try {
2484                 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
2485             } catch (InstallerException e) {
2486                 throw PackageManagerException.from(e);
2487             }
2488         }
2489     }
2490 
linkFiles(List<File> fromFiles, File toDir, File fromDir)2491     private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
2492             throws IOException {
2493         for (File fromFile : fromFiles) {
2494             final String relativePath = getRelativePath(fromFile, fromDir);
2495             try {
2496                 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
2497                         toDir.getAbsolutePath());
2498             } catch (InstallerException e) {
2499                 throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
2500                         + fromDir + ", " + toDir + ")", e);
2501             }
2502         }
2503 
2504         Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
2505     }
2506 
copyFiles(List<File> fromFiles, File toDir)2507     private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
2508         // Remove any partial files from previous attempt
2509         for (File file : toDir.listFiles()) {
2510             if (file.getName().endsWith(".tmp")) {
2511                 file.delete();
2512             }
2513         }
2514 
2515         for (File fromFile : fromFiles) {
2516             final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
2517             if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
2518             if (!FileUtils.copyFile(fromFile, tmpFile)) {
2519                 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
2520             }
2521             try {
2522                 Os.chmod(tmpFile.getAbsolutePath(), 0644);
2523             } catch (ErrnoException e) {
2524                 throw new IOException("Failed to chmod " + tmpFile);
2525             }
2526             final File toFile = new File(toDir, fromFile.getName());
2527             if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
2528             if (!tmpFile.renameTo(toFile)) {
2529                 throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
2530             }
2531         }
2532         Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
2533     }
2534 
extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)2535     private void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
2536             throws PackageManagerException {
2537         final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
2538         if (!inherit) {
2539             // Start from a clean slate
2540             NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
2541         }
2542 
2543         NativeLibraryHelper.Handle handle = null;
2544         try {
2545             handle = NativeLibraryHelper.Handle.create(packageDir);
2546             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
2547                     abiOverride, isIncrementalInstallation());
2548             if (res != PackageManager.INSTALL_SUCCEEDED) {
2549                 throw new PackageManagerException(res,
2550                         "Failed to extract native libraries, res=" + res);
2551             }
2552         } catch (IOException e) {
2553             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
2554                     "Failed to extract native libraries", e);
2555         } finally {
2556             IoUtils.closeQuietly(handle);
2557         }
2558     }
2559 
setPermissionsResult(boolean accepted)2560     void setPermissionsResult(boolean accepted) {
2561         if (!mSealed) {
2562             throw new SecurityException("Must be sealed to accept permissions");
2563         }
2564 
2565         if (accepted) {
2566             // Mark and kick off another install pass
2567             synchronized (mLock) {
2568                 mPermissionsManuallyAccepted = true;
2569                 mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
2570             }
2571         } else {
2572             destroyInternal();
2573             dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
2574         }
2575     }
2576 
2577     /**
2578      * Adds a child session ID without any safety / sanity checks. This should only be used to
2579      * build a session from XML or similar.
2580      */
addChildSessionIdInternal(int sessionId)2581     void addChildSessionIdInternal(int sessionId) {
2582         mChildSessionIds.put(sessionId, 0);
2583     }
2584 
open()2585     public void open() throws IOException {
2586         if (mActiveCount.getAndIncrement() == 0) {
2587             mCallback.onSessionActiveChanged(this, true);
2588         }
2589 
2590         boolean wasPrepared;
2591         synchronized (mLock) {
2592             wasPrepared = mPrepared;
2593             if (!mPrepared) {
2594                 if (stageDir != null) {
2595                     prepareStageDir(stageDir);
2596                 } else if (params.isMultiPackage) {
2597                     // it's all ok
2598                 } else {
2599                     throw new IllegalArgumentException("stageDir must be set");
2600                 }
2601 
2602                 mPrepared = true;
2603             }
2604         }
2605 
2606         if (!wasPrepared) {
2607             mCallback.onSessionPrepared(this);
2608         }
2609     }
2610 
2611     @Override
close()2612     public void close() {
2613         closeInternal(true);
2614     }
2615 
closeInternal(boolean checkCaller)2616     private void closeInternal(boolean checkCaller) {
2617         int activeCount;
2618         synchronized (mLock) {
2619             if (checkCaller) {
2620                 assertCallerIsOwnerOrRootLocked();
2621             }
2622 
2623             activeCount = mActiveCount.decrementAndGet();
2624         }
2625 
2626         if (activeCount == 0) {
2627             mCallback.onSessionActiveChanged(this, false);
2628         }
2629     }
2630 
2631     @Override
abandon()2632     public void abandon() {
2633         if (hasParentSessionId()) {
2634             throw new IllegalStateException(
2635                     "Session " + sessionId + " is a child of multi-package session "
2636                             + mParentSessionId +  " and may not be abandoned directly.");
2637         }
2638 
2639         List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
2640         synchronized (mLock) {
2641             if (params.isStaged && mDestroyed) {
2642                 // If a user abandons staged session in an unsafe state, then system will try to
2643                 // abandon the destroyed staged session when it is safe on behalf of the user.
2644                 assertCallerIsOwnerOrRootOrSystemLocked();
2645             } else {
2646                 assertCallerIsOwnerOrRootLocked();
2647             }
2648 
2649             if (isStagedAndInTerminalState()) {
2650                 // We keep the session in the database if it's in a finalized state. It will be
2651                 // removed by PackageInstallerService when the last update time is old enough.
2652                 // Also, in such cases cleanStageDir() has already been executed so no need to
2653                 // do it now.
2654                 return;
2655             }
2656             if (mCommitted && params.isStaged) {
2657                 mDestroyed = true;
2658                 if (!mStagingManager.abortCommittedSessionLocked(this)) {
2659                     // Do not clean up the staged session from system. It is not safe yet.
2660                     mCallback.onStagedSessionChanged(this);
2661                     return;
2662                 }
2663                 cleanStageDir(childSessions);
2664             }
2665 
2666             if (mRelinquished) {
2667                 Slog.d(TAG, "Ignoring abandon after commit relinquished control");
2668                 return;
2669             }
2670             destroyInternal();
2671         }
2672         dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
2673     }
2674 
2675     @Override
isMultiPackage()2676     public boolean isMultiPackage() {
2677         return params.isMultiPackage;
2678     }
2679 
2680     @Override
isStaged()2681     public boolean isStaged() {
2682         return params.isStaged;
2683     }
2684 
2685     @Override
getDataLoaderParams()2686     public DataLoaderParamsParcel getDataLoaderParams() {
2687         mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
2688         return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
2689     }
2690 
2691     @Override
addFile(int location, String name, long lengthBytes, byte[] metadata, byte[] signature)2692     public void addFile(int location, String name, long lengthBytes, byte[] metadata,
2693             byte[] signature) {
2694         mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
2695         if (!isDataLoaderInstallation()) {
2696             throw new IllegalStateException(
2697                     "Cannot add files to non-data loader installation session.");
2698         }
2699         if (isStreamingInstallation()) {
2700             if (location != LOCATION_DATA_APP) {
2701                 throw new IllegalArgumentException(
2702                         "Non-incremental installation only supports /data/app placement: " + name);
2703             }
2704         }
2705         if (metadata == null) {
2706             throw new IllegalArgumentException(
2707                     "DataLoader installation requires valid metadata: " + name);
2708         }
2709         // Use installer provided name for now; we always rename later
2710         if (!FileUtils.isValidExtFilename(name)) {
2711             throw new IllegalArgumentException("Invalid name: " + name);
2712         }
2713 
2714         synchronized (mLock) {
2715             assertCallerIsOwnerOrRootLocked();
2716             assertPreparedAndNotSealedLocked("addFile");
2717 
2718             if (!mFiles.add(new FileEntry(mFiles.size(),
2719                     new InstallationFile(location, name, lengthBytes, metadata, signature)))) {
2720                 throw new IllegalArgumentException("File already added: " + name);
2721             }
2722         }
2723     }
2724 
2725     @Override
removeFile(int location, String name)2726     public void removeFile(int location, String name) {
2727         mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
2728         if (!isDataLoaderInstallation()) {
2729             throw new IllegalStateException(
2730                     "Cannot add files to non-data loader installation session.");
2731         }
2732         if (TextUtils.isEmpty(params.appPackageName)) {
2733             throw new IllegalStateException("Must specify package name to remove a split");
2734         }
2735 
2736         synchronized (mLock) {
2737             assertCallerIsOwnerOrRootLocked();
2738             assertPreparedAndNotSealedLocked("removeFile");
2739 
2740             if (!mFiles.add(new FileEntry(mFiles.size(),
2741                     new InstallationFile(location, getRemoveMarkerName(name), -1, null, null)))) {
2742                 throw new IllegalArgumentException("File already removed: " + name);
2743             }
2744         }
2745     }
2746 
2747     /**
2748      * Makes sure files are present in staging location.
2749      * @return if the image is ready for installation
2750      */
2751     @GuardedBy("mLock")
prepareDataLoaderLocked()2752     private boolean prepareDataLoaderLocked()
2753             throws PackageManagerException {
2754         if (!isDataLoaderInstallation()) {
2755             return true;
2756         }
2757         if (mDataLoaderFinished) {
2758             return true;
2759         }
2760 
2761         // Retrying commit.
2762         if (mIncrementalFileStorages != null) {
2763             try {
2764                 mIncrementalFileStorages.startLoading();
2765             } catch (IOException e) {
2766                 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
2767                         e.getCause());
2768             }
2769             return false;
2770         }
2771 
2772         final List<InstallationFileParcel> addedFiles = new ArrayList<>();
2773         final List<String> removedFiles = new ArrayList<>();
2774 
2775         final InstallationFile[] files = getInstallationFilesLocked();
2776         for (InstallationFile file : files) {
2777             if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
2778                 addedFiles.add(file.getData());
2779                 continue;
2780             }
2781             if (sRemovedFilter.accept(new File(this.stageDir, file.getName()))) {
2782                 String name = file.getName().substring(
2783                         0, file.getName().length() - REMOVE_MARKER_EXTENSION.length());
2784                 removedFiles.add(name);
2785             }
2786         }
2787 
2788         final DataLoaderManager dataLoaderManager = mContext.getSystemService(
2789                 DataLoaderManager.class);
2790         if (dataLoaderManager == null) {
2791             throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
2792                     "Failed to find data loader manager service");
2793         }
2794 
2795         final DataLoaderParams params = this.params.dataLoaderParams;
2796         final boolean manualStartAndDestroy = !isIncrementalInstallation();
2797         final IDataLoaderStatusListener statusListener = new IDataLoaderStatusListener.Stub() {
2798             @Override
2799             public void onStatusChanged(int dataLoaderId, int status) {
2800                 switch (status) {
2801                     case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
2802                     case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
2803                         return;
2804                 }
2805 
2806                 if (mDestroyed || mDataLoaderFinished) {
2807                     switch (status) {
2808                         case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
2809                             onStorageUnhealthy();
2810                             return;
2811                     }
2812                     return;
2813                 }
2814 
2815                 try {
2816                     IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
2817                     if (dataLoader == null) {
2818                         mDataLoaderFinished = true;
2819                         dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
2820                                 "Failure to obtain data loader");
2821                         return;
2822                     }
2823 
2824                     switch (status) {
2825                         case IDataLoaderStatusListener.DATA_LOADER_BOUND: {
2826                             if (manualStartAndDestroy) {
2827                                 FileSystemControlParcel control = new FileSystemControlParcel();
2828                                 control.callback = new FileSystemConnector(addedFiles);
2829                                 dataLoader.create(dataLoaderId, params.getData(), control, this);
2830                             }
2831 
2832                             break;
2833                         }
2834                         case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
2835                             if (manualStartAndDestroy) {
2836                                 // IncrementalFileStorages will call start after all files are
2837                                 // created in IncFS.
2838                                 dataLoader.start(dataLoaderId);
2839                             }
2840                             break;
2841                         }
2842                         case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
2843                             dataLoader.prepareImage(
2844                                     dataLoaderId,
2845                                     addedFiles.toArray(
2846                                             new InstallationFileParcel[addedFiles.size()]),
2847                                     removedFiles.toArray(new String[removedFiles.size()]));
2848                             break;
2849                         }
2850                         case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
2851                             mDataLoaderFinished = true;
2852                             if (hasParentSessionId()) {
2853                                 mSessionProvider.getSession(
2854                                         mParentSessionId).dispatchStreamValidateAndCommit();
2855                             } else {
2856                                 dispatchStreamValidateAndCommit();
2857                             }
2858                             if (manualStartAndDestroy) {
2859                                 dataLoader.destroy(dataLoaderId);
2860                             }
2861                             break;
2862                         }
2863                         case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
2864                             mDataLoaderFinished = true;
2865                             dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
2866                                     "Failed to prepare image.");
2867                             if (manualStartAndDestroy) {
2868                                 dataLoader.destroy(dataLoaderId);
2869                             }
2870                             break;
2871                         }
2872                         case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
2873                             // Don't fail or commit the session. Allow caller to commit again.
2874                             sendPendingStreaming("DataLoader unavailable");
2875                             break;
2876                         }
2877                         case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
2878                             mDataLoaderFinished = true;
2879                             dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
2880                                     "DataLoader reported unrecoverable failure.");
2881                             break;
2882                     }
2883                 } catch (RemoteException e) {
2884                     // In case of streaming failure we don't want to fail or commit the session.
2885                     // Just return from this method and allow caller to commit again.
2886                     sendPendingStreaming(e.getMessage());
2887                 }
2888             }
2889         };
2890 
2891         if (!manualStartAndDestroy) {
2892             final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams();
2893             healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
2894             healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
2895             healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
2896 
2897             final boolean systemDataLoader =
2898                     params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
2899             final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
2900                 @Override
2901                 public void onHealthStatus(int storageId, int status) {
2902                     if (mDestroyed || mDataLoaderFinished) {
2903                         // App's installed.
2904                         switch (status) {
2905                             case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
2906                                 onStorageUnhealthy();
2907                                 return;
2908                         }
2909                         return;
2910                     }
2911 
2912                     switch (status) {
2913                         case IStorageHealthListener.HEALTH_STATUS_OK:
2914                             break;
2915                         case IStorageHealthListener.HEALTH_STATUS_READS_PENDING:
2916                         case IStorageHealthListener.HEALTH_STATUS_BLOCKED:
2917                             if (systemDataLoader) {
2918                                 // It's OK for ADB data loader to wait for pages.
2919                                 break;
2920                             }
2921                             // fallthrough
2922                         case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
2923                             // Even ADB installation can't wait for missing pages for too long.
2924                             mDataLoaderFinished = true;
2925                             dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
2926                                     "Image is missing pages required for installation.");
2927                             break;
2928                     }
2929                 }
2930             };
2931 
2932             try {
2933                 mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
2934                         params, statusListener, healthCheckParams, healthListener, addedFiles);
2935                 return false;
2936             } catch (IOException e) {
2937                 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
2938                         e.getCause());
2939             }
2940         }
2941 
2942         if (!dataLoaderManager.bindToDataLoader(sessionId, params.getData(), statusListener)) {
2943             throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
2944                     "Failed to initialize data loader");
2945         }
2946 
2947         return false;
2948     }
2949 
dispatchSessionVerificationFailure(int error, String detailMessage)2950     private void dispatchSessionVerificationFailure(int error, String detailMessage) {
2951         mHandler.obtainMessage(MSG_SESSION_VERIFICATION_FAILURE, error, -1,
2952                 detailMessage).sendToTarget();
2953     }
2954 
2955     @Override
getChildSessionIds()2956     public int[] getChildSessionIds() {
2957         final int[] childSessionIds = mChildSessionIds.copyKeys();
2958         if (childSessionIds != null) {
2959             return childSessionIds;
2960         }
2961         return EMPTY_CHILD_SESSION_ARRAY;
2962     }
2963 
2964     @Override
addChildSessionId(int childSessionId)2965     public void addChildSessionId(int childSessionId) {
2966         final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
2967         if (childSession == null
2968                 || (childSession.hasParentSessionId() && childSession.mParentSessionId != sessionId)
2969                 || childSession.mCommitted
2970                 || childSession.mDestroyed) {
2971             throw new IllegalStateException("Unable to add child session " + childSessionId
2972                             + " as it does not exist or is in an invalid state.");
2973         }
2974         synchronized (mLock) {
2975             assertCallerIsOwnerOrRootLocked();
2976             assertPreparedAndNotSealedLocked("addChildSessionId");
2977 
2978             final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId);
2979             if (indexOfSession >= 0) {
2980                 return;
2981             }
2982             childSession.setParentSessionId(this.sessionId);
2983             addChildSessionIdInternal(childSessionId);
2984         }
2985     }
2986 
2987     @Override
removeChildSessionId(int sessionId)2988     public void removeChildSessionId(int sessionId) {
2989         final PackageInstallerSession session = mSessionProvider.getSession(sessionId);
2990         synchronized (mLock) {
2991             final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
2992             if (session != null) {
2993                 session.setParentSessionId(SessionInfo.INVALID_ID);
2994             }
2995             if (indexOfSession < 0) {
2996                 // not added in the first place; no-op
2997                 return;
2998             }
2999             mChildSessionIds.removeAt(indexOfSession);
3000         }
3001     }
3002 
3003     /**
3004      * Sets the parent session ID if not already set.
3005      * If {@link SessionInfo#INVALID_ID} is passed, it will be unset.
3006      */
setParentSessionId(int parentSessionId)3007     void setParentSessionId(int parentSessionId) {
3008         synchronized (mLock) {
3009             if (parentSessionId != SessionInfo.INVALID_ID
3010                     && mParentSessionId != SessionInfo.INVALID_ID) {
3011                 throw new IllegalStateException("The parent of " + sessionId + " is" + " already"
3012                         + "set to " + mParentSessionId);
3013             }
3014             this.mParentSessionId = parentSessionId;
3015         }
3016     }
3017 
hasParentSessionId()3018     boolean hasParentSessionId() {
3019         return mParentSessionId != SessionInfo.INVALID_ID;
3020     }
3021 
3022     @Override
getParentSessionId()3023     public int getParentSessionId() {
3024         return mParentSessionId;
3025     }
3026 
dispatchSessionFinished(int returnCode, String msg, Bundle extras)3027     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
3028         final IntentSender statusReceiver;
3029         final String packageName;
3030         synchronized (mLock) {
3031             mFinalStatus = returnCode;
3032             mFinalMessage = msg;
3033 
3034             statusReceiver = mRemoteStatusReceiver;
3035             packageName = mPackageName;
3036         }
3037 
3038         if (statusReceiver != null) {
3039             // Execute observer.onPackageInstalled on different thread as we don't want callers
3040             // inside the system server have to worry about catching the callbacks while they are
3041             // calling into the session
3042             final SomeArgs args = SomeArgs.obtain();
3043             args.arg1 = packageName;
3044             args.arg2 = msg;
3045             args.arg3 = extras;
3046             args.arg4 = statusReceiver;
3047             args.argi1 = returnCode;
3048 
3049             mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
3050         }
3051 
3052         final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
3053 
3054         // Send broadcast to default launcher only if it's a new install
3055         // TODO(b/144270665): Secure the usage of this broadcast.
3056         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
3057         if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()
3058                 && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
3059             mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);
3060         }
3061 
3062         mCallback.onSessionFinished(this, success);
3063         if (isDataLoaderInstallation()) {
3064             logDataLoaderInstallationSession(returnCode);
3065         }
3066     }
3067 
3068     /** {@hide} */
setStagedSessionReady()3069     void setStagedSessionReady() {
3070         synchronized (mLock) {
3071             if (mDestroyed) return; // Do not allow destroyed staged session to change state
3072             mStagedSessionReady = true;
3073             mStagedSessionApplied = false;
3074             mStagedSessionFailed = false;
3075             mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
3076             mStagedSessionErrorMessage = "";
3077         }
3078         mCallback.onStagedSessionChanged(this);
3079     }
3080 
3081     /** {@hide} */
setStagedSessionFailed(@tagedSessionErrorCode int errorCode, String errorMessage)3082     void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
3083                                 String errorMessage) {
3084         synchronized (mLock) {
3085             if (mDestroyed) return; // Do not allow destroyed staged session to change state
3086             mStagedSessionReady = false;
3087             mStagedSessionApplied = false;
3088             mStagedSessionFailed = true;
3089             mStagedSessionErrorCode = errorCode;
3090             mStagedSessionErrorMessage = errorMessage;
3091             Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
3092         }
3093         cleanStageDirNotLocked();
3094         mCallback.onStagedSessionChanged(this);
3095     }
3096 
3097     /** {@hide} */
setStagedSessionApplied()3098     void setStagedSessionApplied() {
3099         synchronized (mLock) {
3100             if (mDestroyed) return; // Do not allow destroyed staged session to change state
3101             mStagedSessionReady = false;
3102             mStagedSessionApplied = true;
3103             mStagedSessionFailed = false;
3104             mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
3105             mStagedSessionErrorMessage = "";
3106             Slog.d(TAG, "Marking session " + sessionId + " as applied");
3107         }
3108         cleanStageDirNotLocked();
3109         mCallback.onStagedSessionChanged(this);
3110     }
3111 
3112     /** {@hide} */
isStagedSessionReady()3113     boolean isStagedSessionReady() {
3114         return mStagedSessionReady;
3115     }
3116 
3117     /** {@hide} */
isStagedSessionApplied()3118     boolean isStagedSessionApplied() {
3119         return mStagedSessionApplied;
3120     }
3121 
3122     /** {@hide} */
isStagedSessionFailed()3123     boolean isStagedSessionFailed() {
3124         return mStagedSessionFailed;
3125     }
3126 
3127     /** {@hide} */
getStagedSessionErrorCode()3128     @StagedSessionErrorCode int getStagedSessionErrorCode() {
3129         return mStagedSessionErrorCode;
3130     }
3131 
3132     /** {@hide} */
getStagedSessionErrorMessage()3133     String getStagedSessionErrorMessage() {
3134         return mStagedSessionErrorMessage;
3135     }
3136 
destroyInternal()3137     private void destroyInternal() {
3138         synchronized (mLock) {
3139             mSealed = true;
3140             if (!params.isStaged || isStagedAndInTerminalState()) {
3141                 mDestroyed = true;
3142             }
3143             // Force shut down all bridges
3144             for (RevocableFileDescriptor fd : mFds) {
3145                 fd.revoke();
3146             }
3147             for (FileBridge bridge : mBridges) {
3148                 bridge.forceClose();
3149             }
3150         }
3151         if (mIncrementalFileStorages != null) {
3152             mIncrementalFileStorages.cleanUp();
3153             mIncrementalFileStorages = null;
3154         }
3155         // For staged sessions, we don't delete the directory where the packages have been copied,
3156         // since these packages are supposed to be read on reboot.
3157         // Those dirs are deleted when the staged session has reached a final state.
3158         if (stageDir != null && !params.isStaged) {
3159             try {
3160                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
3161             } catch (InstallerException ignored) {
3162             }
3163         }
3164     }
3165 
3166     /**
3167      * <b>must not hold {@link #mLock}</b>
3168      */
cleanStageDirNotLocked()3169     private void cleanStageDirNotLocked() {
3170         if (Thread.holdsLock(mLock)) {
3171             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
3172                     + " is holding mLock", new Throwable());
3173         }
3174         cleanStageDir(getChildSessionsNotLocked());
3175     }
3176 
cleanStageDir(List<PackageInstallerSession> childSessions)3177     private void cleanStageDir(List<PackageInstallerSession> childSessions) {
3178         if (childSessions != null) {
3179             for (PackageInstallerSession childSession : childSessions) {
3180                 if (childSession != null) {
3181                     childSession.cleanStageDir();
3182                 }
3183             }
3184         } else {
3185             cleanStageDir();
3186         }
3187     }
3188 
cleanStageDir()3189     private void cleanStageDir() {
3190         if (mIncrementalFileStorages != null) {
3191             mIncrementalFileStorages.cleanUp();
3192             mIncrementalFileStorages = null;
3193         }
3194         try {
3195             mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
3196         } catch (InstallerException ignored) {
3197         }
3198     }
3199 
dump(IndentingPrintWriter pw)3200     void dump(IndentingPrintWriter pw) {
3201         synchronized (mLock) {
3202             dumpLocked(pw);
3203         }
3204     }
3205 
3206     @GuardedBy("mLock")
dumpLocked(IndentingPrintWriter pw)3207     private void dumpLocked(IndentingPrintWriter pw) {
3208         pw.println("Session " + sessionId + ":");
3209         pw.increaseIndent();
3210 
3211         pw.printPair("userId", userId);
3212         pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
3213         pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName);
3214         pw.printPair("installerPackageName", mInstallSource.installerPackageName);
3215         pw.printPair("installInitiatingPackageName", mInstallSource.initiatingPackageName);
3216         pw.printPair("installOriginatingPackageName", mInstallSource.originatingPackageName);
3217         pw.printPair("mInstallerUid", mInstallerUid);
3218         pw.printPair("createdMillis", createdMillis);
3219         pw.printPair("updatedMillis", updatedMillis);
3220         pw.printPair("stageDir", stageDir);
3221         pw.printPair("stageCid", stageCid);
3222         pw.println();
3223 
3224         params.dump(pw);
3225 
3226         pw.printPair("mClientProgress", mClientProgress);
3227         pw.printPair("mProgress", mProgress);
3228         pw.printPair("mCommitted", mCommitted);
3229         pw.printPair("mSealed", mSealed);
3230         pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
3231         pw.printPair("mRelinquished", mRelinquished);
3232         pw.printPair("mDestroyed", mDestroyed);
3233         pw.printPair("mFds", mFds.size());
3234         pw.printPair("mBridges", mBridges.size());
3235         pw.printPair("mFinalStatus", mFinalStatus);
3236         pw.printPair("mFinalMessage", mFinalMessage);
3237         pw.printPair("params.isMultiPackage", params.isMultiPackage);
3238         pw.printPair("params.isStaged", params.isStaged);
3239         pw.printPair("mParentSessionId", mParentSessionId);
3240         pw.printPair("mChildSessionIds", mChildSessionIds);
3241         pw.printPair("mStagedSessionApplied", mStagedSessionApplied);
3242         pw.printPair("mStagedSessionFailed", mStagedSessionFailed);
3243         pw.printPair("mStagedSessionReady", mStagedSessionReady);
3244         pw.printPair("mStagedSessionErrorCode", mStagedSessionErrorCode);
3245         pw.printPair("mStagedSessionErrorMessage", mStagedSessionErrorMessage);
3246         pw.println();
3247 
3248         pw.decreaseIndent();
3249     }
3250 
sendOnUserActionRequired(Context context, IntentSender target, int sessionId, Intent intent)3251     private static void sendOnUserActionRequired(Context context, IntentSender target,
3252             int sessionId, Intent intent) {
3253         final Intent fillIn = new Intent();
3254         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
3255         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
3256         fillIn.putExtra(Intent.EXTRA_INTENT, intent);
3257         try {
3258             target.sendIntent(context, 0, fillIn, null, null);
3259         } catch (IntentSender.SendIntentException ignored) {
3260         }
3261     }
3262 
sendOnPackageInstalled(Context context, IntentSender target, int sessionId, boolean showNotification, int userId, String basePackageName, int returnCode, String msg, Bundle extras)3263     private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
3264             boolean showNotification, int userId, String basePackageName, int returnCode,
3265             String msg, Bundle extras) {
3266         if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
3267             boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
3268             Notification notification = PackageInstallerService.buildSuccessNotification(context,
3269                     context.getResources()
3270                             .getString(update ? R.string.package_updated_device_owner :
3271                                     R.string.package_installed_device_owner),
3272                     basePackageName,
3273                     userId);
3274             if (notification != null) {
3275                 NotificationManager notificationManager = (NotificationManager)
3276                         context.getSystemService(Context.NOTIFICATION_SERVICE);
3277                 notificationManager.notify(basePackageName,
3278                         SystemMessageProto.SystemMessage.NOTE_PACKAGE_STATE,
3279                         notification);
3280             }
3281         }
3282         final Intent fillIn = new Intent();
3283         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
3284         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
3285         fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
3286                 PackageManager.installStatusToPublicStatus(returnCode));
3287         fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
3288                 PackageManager.installStatusToString(returnCode, msg));
3289         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
3290         if (extras != null) {
3291             final String existing = extras.getString(
3292                     PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
3293             if (!TextUtils.isEmpty(existing)) {
3294                 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
3295             }
3296         }
3297         try {
3298             target.sendIntent(context, 0, fillIn, null, null);
3299         } catch (IntentSender.SendIntentException ignored) {
3300         }
3301     }
3302 
sendPendingStreaming(@ullable String cause)3303     private void sendPendingStreaming(@Nullable String cause) {
3304         final IntentSender statusReceiver;
3305         synchronized (mLock) {
3306             statusReceiver = mRemoteStatusReceiver;
3307         }
3308 
3309         if (statusReceiver == null) {
3310             Slog.e(TAG, "Missing receiver for pending streaming status.");
3311             return;
3312         }
3313 
3314         final Intent intent = new Intent();
3315         intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
3316         intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING);
3317         if (!TextUtils.isEmpty(cause)) {
3318             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
3319                     "Staging Image Not Ready [" + cause + "]");
3320         } else {
3321             intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
3322         }
3323         try {
3324             statusReceiver.sendIntent(mContext, 0, intent, null, null);
3325         } catch (IntentSender.SendIntentException ignored) {
3326         }
3327     }
3328 
writeGrantedRuntimePermissionsLocked(XmlSerializer out, String[] grantedRuntimePermissions)3329     private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
3330             String[] grantedRuntimePermissions) throws IOException {
3331         if (grantedRuntimePermissions != null) {
3332             for (String permission : grantedRuntimePermissions) {
3333                 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
3334                 writeStringAttribute(out, ATTR_NAME, permission);
3335                 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
3336             }
3337         }
3338     }
3339 
writeWhitelistedRestrictedPermissionsLocked(@onNull XmlSerializer out, @Nullable List<String> whitelistedRestrictedPermissions)3340     private static void writeWhitelistedRestrictedPermissionsLocked(@NonNull XmlSerializer out,
3341             @Nullable List<String> whitelistedRestrictedPermissions) throws IOException {
3342         if (whitelistedRestrictedPermissions != null) {
3343             final int permissionCount = whitelistedRestrictedPermissions.size();
3344             for (int i = 0; i < permissionCount; i++) {
3345                 out.startTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION);
3346                 writeStringAttribute(out, ATTR_NAME, whitelistedRestrictedPermissions.get(i));
3347                 out.endTag(null, TAG_WHITELISTED_RESTRICTED_PERMISSION);
3348             }
3349         }
3350     }
3351 
writeAutoRevokePermissionsMode(@onNull XmlSerializer out, int mode)3352     private static void writeAutoRevokePermissionsMode(@NonNull XmlSerializer out, int mode)
3353             throws IOException {
3354         out.startTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
3355         writeIntAttribute(out, ATTR_MODE, mode);
3356         out.endTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
3357     }
3358 
3359 
buildAppIconFile(int sessionId, @NonNull File sessionsDir)3360     private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
3361         return new File(sessionsDir, "app_icon." + sessionId + ".png");
3362     }
3363 
3364     /**
3365      * Write this session to a {@link XmlSerializer}.
3366      *
3367      * @param out Where to write the session to
3368      * @param sessionsDir The directory containing the sessions
3369      */
write(@onNull XmlSerializer out, @NonNull File sessionsDir)3370     void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
3371         synchronized (mLock) {
3372             if (mDestroyed && !params.isStaged) {
3373                 return;
3374             }
3375 
3376             out.startTag(null, TAG_SESSION);
3377 
3378             writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
3379             writeIntAttribute(out, ATTR_USER_ID, userId);
3380             writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
3381                     mInstallSource.installerPackageName);
3382             writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
3383             writeStringAttribute(out, ATTR_INITIATING_PACKAGE_NAME,
3384                     mInstallSource.initiatingPackageName);
3385             writeStringAttribute(out, ATTR_ORIGINATING_PACKAGE_NAME,
3386                     mInstallSource.originatingPackageName);
3387             writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
3388             writeLongAttribute(out, ATTR_UPDATED_MILLIS, updatedMillis);
3389             if (stageDir != null) {
3390                 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
3391                         stageDir.getAbsolutePath());
3392             }
3393             if (stageCid != null) {
3394                 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
3395             }
3396             writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
3397             writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
3398             writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
3399             writeBooleanAttribute(out, ATTR_SEALED, isSealed());
3400 
3401             writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
3402             writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
3403             writeBooleanAttribute(out, ATTR_IS_READY, mStagedSessionReady);
3404             writeBooleanAttribute(out, ATTR_IS_FAILED, mStagedSessionFailed);
3405             writeBooleanAttribute(out, ATTR_IS_APPLIED, mStagedSessionApplied);
3406             writeIntAttribute(out, ATTR_STAGED_SESSION_ERROR_CODE, mStagedSessionErrorCode);
3407             writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE,
3408                     mStagedSessionErrorMessage);
3409             // TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
3410             //                       we've read all sessions.
3411             writeIntAttribute(out, ATTR_PARENT_SESSION_ID, mParentSessionId);
3412             writeIntAttribute(out, ATTR_MODE, params.mode);
3413             writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
3414             writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
3415             writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
3416             writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
3417             writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
3418             writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
3419             writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
3420             writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
3421             writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
3422             writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
3423             writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
3424 
3425             final boolean isDataLoader = params.dataLoaderParams != null;
3426             writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader);
3427             if (isDataLoader) {
3428                 writeIntAttribute(out, ATTR_DATALOADER_TYPE, params.dataLoaderParams.getType());
3429                 writeStringAttribute(out, ATTR_DATALOADER_PACKAGE_NAME,
3430                         params.dataLoaderParams.getComponentName().getPackageName());
3431                 writeStringAttribute(out, ATTR_DATALOADER_CLASS_NAME,
3432                         params.dataLoaderParams.getComponentName().getClassName());
3433                 writeStringAttribute(out, ATTR_DATALOADER_ARGUMENTS,
3434                         params.dataLoaderParams.getArguments());
3435             }
3436 
3437             writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
3438             writeWhitelistedRestrictedPermissionsLocked(out,
3439                     params.whitelistedRestrictedPermissions);
3440             writeAutoRevokePermissionsMode(out, params.autoRevokePermissionsMode);
3441 
3442             // Persist app icon if changed since last written
3443             File appIconFile = buildAppIconFile(sessionId, sessionsDir);
3444             if (params.appIcon == null && appIconFile.exists()) {
3445                 appIconFile.delete();
3446             } else if (params.appIcon != null
3447                     && appIconFile.lastModified() != params.appIconLastModified) {
3448                 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
3449                 FileOutputStream os = null;
3450                 try {
3451                     os = new FileOutputStream(appIconFile);
3452                     params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
3453                 } catch (IOException e) {
3454                     Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
3455                 } finally {
3456                     IoUtils.closeQuietly(os);
3457                 }
3458 
3459                 params.appIconLastModified = appIconFile.lastModified();
3460             }
3461             final int[] childSessionIds = getChildSessionIds();
3462             for (int childSessionId : childSessionIds) {
3463                 out.startTag(null, TAG_CHILD_SESSION);
3464                 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
3465                 out.endTag(null, TAG_CHILD_SESSION);
3466             }
3467 
3468             final InstallationFile[] files = getInstallationFilesLocked();
3469             for (InstallationFile file : getInstallationFilesLocked()) {
3470                 out.startTag(null, TAG_SESSION_FILE);
3471                 writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
3472                 writeStringAttribute(out, ATTR_NAME, file.getName());
3473                 writeLongAttribute(out, ATTR_LENGTH_BYTES, file.getLengthBytes());
3474                 writeByteArrayAttribute(out, ATTR_METADATA, file.getMetadata());
3475                 writeByteArrayAttribute(out, ATTR_SIGNATURE, file.getSignature());
3476                 out.endTag(null, TAG_SESSION_FILE);
3477             }
3478         }
3479 
3480         out.endTag(null, TAG_SESSION);
3481     }
3482 
3483     // Sanity check to be performed when the session is restored from an external file. Only one
3484     // of the session states should be true, or none of them.
isStagedSessionStateValid(boolean isReady, boolean isApplied, boolean isFailed)3485     private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
3486                                                      boolean isFailed) {
3487         return (!isReady && !isApplied && !isFailed)
3488                 || (isReady && !isApplied && !isFailed)
3489                 || (!isReady && isApplied && !isFailed)
3490                 || (!isReady && !isApplied && isFailed);
3491     }
3492 
3493     /**
3494      * Read new session from a {@link XmlPullParser xml description} and create it.
3495      *
3496      * @param in The source of the description
3497      * @param callback Callback the session uses to notify about changes of it's state
3498      * @param context Context to be used by the session
3499      * @param pm PackageManager to use by the session
3500      * @param installerThread Thread to be used for callbacks of this session
3501      * @param sessionsDir The directory the sessions are stored in
3502      *
3503      * @param sessionProvider
3504      * @return The newly created session
3505      */
readFromXml(@onNull XmlPullParser in, @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, @NonNull PackageManagerService pm, Looper installerThread, @NonNull StagingManager stagingManager, @NonNull File sessionsDir, @NonNull PackageSessionProvider sessionProvider)3506     public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
3507             @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
3508             @NonNull PackageManagerService pm, Looper installerThread,
3509             @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
3510             @NonNull PackageSessionProvider sessionProvider)
3511             throws IOException, XmlPullParserException {
3512         final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
3513         final int userId = readIntAttribute(in, ATTR_USER_ID);
3514         final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
3515         final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
3516                 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
3517         final String installInitiatingPackageName =
3518                 readStringAttribute(in, ATTR_INITIATING_PACKAGE_NAME);
3519         final String installOriginatingPackageName =
3520                 readStringAttribute(in, ATTR_ORIGINATING_PACKAGE_NAME);
3521         final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
3522         long updatedMillis = readLongAttribute(in, ATTR_UPDATED_MILLIS);
3523         final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
3524         final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
3525         final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
3526         final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
3527         final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED);
3528         final boolean destroyed = readBooleanAttribute(in, ATTR_DESTROYED);
3529         final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
3530         final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID,
3531                 SessionInfo.INVALID_ID);
3532 
3533         final SessionParams params = new SessionParams(
3534                 SessionParams.MODE_INVALID);
3535         params.isMultiPackage = readBooleanAttribute(in, ATTR_MULTI_PACKAGE, false);
3536         params.isStaged = readBooleanAttribute(in, ATTR_STAGED_SESSION, false);
3537         params.mode = readIntAttribute(in, ATTR_MODE);
3538         params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
3539         params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
3540         params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
3541         params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
3542         params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
3543         params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
3544         params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
3545         params.originatingUid =
3546                 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
3547         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
3548         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
3549         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
3550         params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
3551 
3552         if (readBooleanAttribute(in, ATTR_IS_DATALOADER)) {
3553             params.dataLoaderParams = new DataLoaderParams(
3554                     readIntAttribute(in, ATTR_DATALOADER_TYPE),
3555                     new ComponentName(
3556                             readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME),
3557                             readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)),
3558                     readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS));
3559         }
3560 
3561         final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
3562         if (appIconFile.exists()) {
3563             params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
3564             params.appIconLastModified = appIconFile.lastModified();
3565         }
3566         final boolean isReady = readBooleanAttribute(in, ATTR_IS_READY);
3567         final boolean isFailed = readBooleanAttribute(in, ATTR_IS_FAILED);
3568         final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
3569         final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE,
3570                 SessionInfo.STAGED_SESSION_NO_ERROR);
3571         final String stagedSessionErrorMessage = readStringAttribute(in,
3572                 ATTR_STAGED_SESSION_ERROR_MESSAGE);
3573 
3574         if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
3575             throw new IllegalArgumentException("Can't restore staged session with invalid state.");
3576         }
3577 
3578         // Parse sub tags of this session, typically used for repeated values / arrays.
3579         // Sub tags can come in any order, therefore we need to keep track of what we find while
3580         // parsing and only set the right values at the end.
3581 
3582         // Store the current depth. We should stop parsing when we reach an end tag at the same
3583         // depth.
3584         List<String> grantedRuntimePermissions = new ArrayList<>();
3585         List<String> whitelistedRestrictedPermissions = new ArrayList<>();
3586         int autoRevokePermissionsMode = MODE_DEFAULT;
3587         List<Integer> childSessionIds = new ArrayList<>();
3588         List<InstallationFile> files = new ArrayList<>();
3589         int outerDepth = in.getDepth();
3590         int type;
3591         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
3592                 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
3593             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
3594                 continue;
3595             }
3596             if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
3597                 grantedRuntimePermissions.add(readStringAttribute(in, ATTR_NAME));
3598             }
3599             if (TAG_WHITELISTED_RESTRICTED_PERMISSION.equals(in.getName())) {
3600                 whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME));
3601 
3602             }
3603             if (TAG_AUTO_REVOKE_PERMISSIONS_MODE.equals(in.getName())) {
3604                 autoRevokePermissionsMode = readIntAttribute(in, ATTR_MODE);
3605             }
3606             if (TAG_CHILD_SESSION.equals(in.getName())) {
3607                 childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
3608             }
3609             if (TAG_SESSION_FILE.equals(in.getName())) {
3610                 files.add(new InstallationFile(
3611                         readIntAttribute(in, ATTR_LOCATION, 0),
3612                         readStringAttribute(in, ATTR_NAME),
3613                         readLongAttribute(in, ATTR_LENGTH_BYTES, -1),
3614                         readByteArrayAttribute(in, ATTR_METADATA),
3615                         readByteArrayAttribute(in, ATTR_SIGNATURE)));
3616             }
3617         }
3618 
3619         if (grantedRuntimePermissions.size() > 0) {
3620             params.grantedRuntimePermissions =
3621                     grantedRuntimePermissions.toArray(EmptyArray.STRING);
3622         }
3623 
3624         if (whitelistedRestrictedPermissions.size() > 0) {
3625             params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
3626         }
3627 
3628         params.autoRevokePermissionsMode = autoRevokePermissionsMode;
3629 
3630         int[] childSessionIdsArray;
3631         if (childSessionIds.size() > 0) {
3632             childSessionIdsArray = new int[childSessionIds.size()];
3633             for (int i = 0, size = childSessionIds.size(); i < size; ++i) {
3634                 childSessionIdsArray[i] = childSessionIds.get(i);
3635             }
3636         } else {
3637             childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
3638         }
3639 
3640         InstallationFile[] fileArray = null;
3641         if (!files.isEmpty()) {
3642             fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY);
3643         }
3644 
3645         InstallSource installSource = InstallSource.create(installInitiatingPackageName,
3646                 installOriginatingPackageName, installerPackageName);
3647         return new PackageInstallerSession(callback, context, pm, sessionProvider,
3648                 installerThread, stagingManager, sessionId, userId, installerUid,
3649                 installSource, params, createdMillis, stageDir, stageCid, fileArray,
3650                 prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
3651                 isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
3652     }
3653 }
3654