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.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
20 import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
21 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
22 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
23 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
24 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
25 import static android.system.OsConstants.O_CREAT;
26 import static android.system.OsConstants.O_RDONLY;
27 import static android.system.OsConstants.O_WRONLY;
28 
29 import static com.android.internal.util.XmlUtils.readBitmapAttribute;
30 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
31 import static com.android.internal.util.XmlUtils.readIntAttribute;
32 import static com.android.internal.util.XmlUtils.readLongAttribute;
33 import static com.android.internal.util.XmlUtils.readStringAttribute;
34 import static com.android.internal.util.XmlUtils.readUriAttribute;
35 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
36 import static com.android.internal.util.XmlUtils.writeIntAttribute;
37 import static com.android.internal.util.XmlUtils.writeLongAttribute;
38 import static com.android.internal.util.XmlUtils.writeStringAttribute;
39 import static com.android.internal.util.XmlUtils.writeUriAttribute;
40 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
41 
42 import android.Manifest;
43 import android.annotation.NonNull;
44 import android.annotation.Nullable;
45 import android.app.admin.DeviceAdminInfo;
46 import android.app.admin.DevicePolicyManagerInternal;
47 import android.content.Context;
48 import android.content.Intent;
49 import android.content.IntentSender;
50 import android.content.pm.ApplicationInfo;
51 import android.content.pm.IPackageInstallObserver2;
52 import android.content.pm.IPackageInstallerSession;
53 import android.content.pm.PackageInfo;
54 import android.content.pm.PackageInstaller;
55 import android.content.pm.PackageInstaller.SessionInfo;
56 import android.content.pm.PackageInstaller.SessionParams;
57 import android.content.pm.PackageManager;
58 import android.content.pm.PackageParser;
59 import android.content.pm.PackageParser.ApkLite;
60 import android.content.pm.PackageParser.PackageLite;
61 import android.content.pm.PackageParser.PackageParserException;
62 import android.graphics.Bitmap;
63 import android.graphics.BitmapFactory;
64 import android.os.Binder;
65 import android.os.Bundle;
66 import android.os.FileBridge;
67 import android.os.FileUtils;
68 import android.os.Handler;
69 import android.os.Looper;
70 import android.os.Message;
71 import android.os.ParcelFileDescriptor;
72 import android.os.ParcelableException;
73 import android.os.Process;
74 import android.os.RemoteException;
75 import android.os.RevocableFileDescriptor;
76 import android.os.SystemProperties;
77 import android.os.UserHandle;
78 import android.os.storage.StorageManager;
79 import android.system.ErrnoException;
80 import android.system.Int64Ref;
81 import android.system.Os;
82 import android.system.OsConstants;
83 import android.system.StructStat;
84 import android.text.TextUtils;
85 import android.util.ArraySet;
86 import android.util.ExceptionUtils;
87 import android.util.MathUtils;
88 import android.util.Slog;
89 import android.util.apk.ApkSignatureVerifier;
90 
91 import com.android.internal.annotations.GuardedBy;
92 import com.android.internal.content.NativeLibraryHelper;
93 import com.android.internal.content.PackageHelper;
94 import com.android.internal.os.SomeArgs;
95 import com.android.internal.util.ArrayUtils;
96 import com.android.internal.util.IndentingPrintWriter;
97 import com.android.internal.util.Preconditions;
98 import com.android.server.LocalServices;
99 import com.android.server.pm.Installer.InstallerException;
100 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
101 
102 import android.content.pm.dex.DexMetadataHelper;
103 import libcore.io.IoUtils;
104 
105 import org.xmlpull.v1.XmlPullParser;
106 import org.xmlpull.v1.XmlPullParserException;
107 import org.xmlpull.v1.XmlSerializer;
108 
109 import java.io.File;
110 import java.io.FileDescriptor;
111 import java.io.FileFilter;
112 import java.io.FileOutputStream;
113 import java.io.IOException;
114 import java.util.ArrayList;
115 import java.util.Arrays;
116 import java.util.LinkedList;
117 import java.util.List;
118 import java.util.concurrent.atomic.AtomicInteger;
119 
120 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
121     private static final String TAG = "PackageInstaller";
122     private static final boolean LOGD = true;
123     private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
124 
125     private static final int MSG_EARLY_BIND = 0;
126     private static final int MSG_COMMIT = 1;
127     private static final int MSG_ON_PACKAGE_INSTALLED = 2;
128 
129     /** XML constants used for persisting a session */
130     static final String TAG_SESSION = "session";
131     private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
132     private static final String ATTR_SESSION_ID = "sessionId";
133     private static final String ATTR_USER_ID = "userId";
134     private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
135     private static final String ATTR_INSTALLER_UID = "installerUid";
136     private static final String ATTR_CREATED_MILLIS = "createdMillis";
137     private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
138     private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
139     private static final String ATTR_PREPARED = "prepared";
140     private static final String ATTR_SEALED = "sealed";
141     private static final String ATTR_MODE = "mode";
142     private static final String ATTR_INSTALL_FLAGS = "installFlags";
143     private static final String ATTR_INSTALL_LOCATION = "installLocation";
144     private static final String ATTR_SIZE_BYTES = "sizeBytes";
145     private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
146     @Deprecated
147     private static final String ATTR_APP_ICON = "appIcon";
148     private static final String ATTR_APP_LABEL = "appLabel";
149     private static final String ATTR_ORIGINATING_URI = "originatingUri";
150     private static final String ATTR_ORIGINATING_UID = "originatingUid";
151     private static final String ATTR_REFERRER_URI = "referrerUri";
152     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
153     private static final String ATTR_VOLUME_UUID = "volumeUuid";
154     private static final String ATTR_NAME = "name";
155     private static final String ATTR_INSTALL_REASON = "installRason";
156 
157     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
158 
159     // TODO: enforce INSTALL_ALLOW_TEST
160     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
161 
162     private final PackageInstallerService.InternalCallback mCallback;
163     private final Context mContext;
164     private final PackageManagerService mPm;
165     private final Handler mHandler;
166 
167     final int sessionId;
168     final int userId;
169     final SessionParams params;
170     final long createdMillis;
171     final int defaultContainerGid;
172 
173     /** Staging location where client data is written. */
174     final File stageDir;
175     final String stageCid;
176 
177     private final AtomicInteger mActiveCount = new AtomicInteger();
178 
179     private final Object mLock = new Object();
180 
181     /** Uid of the creator of this session. */
182     private final int mOriginalInstallerUid;
183 
184     /** Package of the owner of the installer session */
185     @GuardedBy("mLock")
186     private String mInstallerPackageName;
187 
188     /** Uid of the owner of the installer session */
189     @GuardedBy("mLock")
190     private int mInstallerUid;
191 
192     @GuardedBy("mLock")
193     private float mClientProgress = 0;
194     @GuardedBy("mLock")
195     private float mInternalProgress = 0;
196 
197     @GuardedBy("mLock")
198     private float mProgress = 0;
199     @GuardedBy("mLock")
200     private float mReportedProgress = -1;
201 
202     /** State of the session. */
203     @GuardedBy("mLock")
204     private boolean mPrepared = false;
205     @GuardedBy("mLock")
206     private boolean mSealed = false;
207     @GuardedBy("mLock")
208     private boolean mCommitted = false;
209     @GuardedBy("mLock")
210     private boolean mRelinquished = false;
211     @GuardedBy("mLock")
212     private boolean mDestroyed = false;
213 
214     /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
215     @GuardedBy("mLock")
216     private boolean mPermissionsManuallyAccepted = false;
217 
218     @GuardedBy("mLock")
219     private int mFinalStatus;
220     @GuardedBy("mLock")
221     private String mFinalMessage;
222 
223     @GuardedBy("mLock")
224     private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
225     @GuardedBy("mLock")
226     private final ArrayList<FileBridge> mBridges = new ArrayList<>();
227 
228     @GuardedBy("mLock")
229     private IPackageInstallObserver2 mRemoteObserver;
230 
231     /** Fields derived from commit parsing */
232     @GuardedBy("mLock")
233     private String mPackageName;
234     @GuardedBy("mLock")
235     private long mVersionCode;
236     @GuardedBy("mLock")
237     private PackageParser.SigningDetails mSigningDetails;
238 
239     /**
240      * Path to the validated base APK for this session, which may point at an
241      * APK inside the session (when the session defines the base), or it may
242      * point at the existing base APK (when adding splits to an existing app).
243      * <p>
244      * This is used when confirming permissions, since we can't fully stage the
245      * session inside an ASEC before confirming with user.
246      */
247     @GuardedBy("mLock")
248     private File mResolvedBaseFile;
249 
250     @GuardedBy("mLock")
251     private File mResolvedStageDir;
252 
253     @GuardedBy("mLock")
254     private final List<File> mResolvedStagedFiles = new ArrayList<>();
255     @GuardedBy("mLock")
256     private final List<File> mResolvedInheritedFiles = new ArrayList<>();
257     @GuardedBy("mLock")
258     private final List<String> mResolvedInstructionSets = new ArrayList<>();
259     @GuardedBy("mLock")
260     private final List<String> mResolvedNativeLibPaths = new ArrayList<>();
261     @GuardedBy("mLock")
262     private File mInheritedFilesBase;
263 
264     private static final FileFilter sAddedFilter = new FileFilter() {
265         @Override
266         public boolean accept(File file) {
267             // Installers can't stage directories, so it's fine to ignore
268             // entries like "lost+found".
269             if (file.isDirectory()) return false;
270             if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
271             if (DexMetadataHelper.isDexMetadataFile(file)) return false;
272             return true;
273         }
274     };
275     private static final FileFilter sRemovedFilter = new FileFilter() {
276         @Override
277         public boolean accept(File file) {
278             if (file.isDirectory()) return false;
279             if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
280             return true;
281         }
282     };
283 
284     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
285         @Override
286         public boolean handleMessage(Message msg) {
287             switch (msg.what) {
288                 case MSG_EARLY_BIND:
289                     earlyBindToDefContainer();
290                     break;
291                 case MSG_COMMIT:
292                     synchronized (mLock) {
293                         try {
294                             commitLocked();
295                         } catch (PackageManagerException e) {
296                             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
297                             Slog.e(TAG,
298                                     "Commit of session " + sessionId + " failed: " + completeMsg);
299                             destroyInternal();
300                             dispatchSessionFinished(e.error, completeMsg, null);
301                         }
302                     }
303 
304                     break;
305                 case MSG_ON_PACKAGE_INSTALLED:
306                     final SomeArgs args = (SomeArgs) msg.obj;
307                     final String packageName = (String) args.arg1;
308                     final String message = (String) args.arg2;
309                     final Bundle extras = (Bundle) args.arg3;
310                     final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;
311                     final int returnCode = args.argi1;
312                     args.recycle();
313 
314                     try {
315                         observer.onPackageInstalled(packageName, returnCode, message, extras);
316                     } catch (RemoteException ignored) {
317                     }
318 
319                     break;
320             }
321 
322             return true;
323         }
324     };
325 
earlyBindToDefContainer()326     private void earlyBindToDefContainer() {
327         mPm.earlyBindToDefContainer();
328     }
329 
330     /**
331      * @return {@code true} iff the installing is app an device owner or affiliated profile owner.
332      */
333     @GuardedBy("mLock")
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()334     private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
335         DevicePolicyManagerInternal dpmi =
336                 LocalServices.getService(DevicePolicyManagerInternal.class);
337         return dpmi != null && dpmi.isActiveAdminWithPolicy(mInstallerUid,
338                 DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) && dpmi.isUserAffiliatedWithDevice(
339                 userId);
340     }
341 
342     /**
343      * Checks if the permissions still need to be confirmed.
344      *
345      * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
346      * installer might still {@link #transfer(String) change}.
347      *
348      * @return {@code true} iff we need to ask to confirm the permissions?
349      */
350     @GuardedBy("mLock")
needToAskForPermissionsLocked()351     private boolean needToAskForPermissionsLocked() {
352         if (mPermissionsManuallyAccepted) {
353             return false;
354         }
355 
356         final boolean isInstallPermissionGranted =
357                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
358                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
359         final boolean isSelfUpdatePermissionGranted =
360                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
361                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
362         final boolean isUpdatePermissionGranted =
363                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
364                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
365         final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
366         final boolean isPermissionGranted = isInstallPermissionGranted
367                 || (isUpdatePermissionGranted && targetPackageUid != -1)
368                 || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
369         final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
370         final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
371         final boolean forcePermissionPrompt =
372                 (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
373 
374         // Device owners and affiliated profile owners  are allowed to silently install packages, so
375         // the permission check is waived if the installer is the device owner.
376         return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
377                 || isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
378     }
379 
PackageInstallerSession(PackageInstallerService.InternalCallback callback, Context context, PackageManagerService pm, Looper looper, int sessionId, int userId, String installerPackageName, int installerUid, SessionParams params, long createdMillis, File stageDir, String stageCid, boolean prepared, boolean sealed)380     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
381             Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
382             String installerPackageName, int installerUid, SessionParams params, long createdMillis,
383             File stageDir, String stageCid, boolean prepared, boolean sealed) {
384         mCallback = callback;
385         mContext = context;
386         mPm = pm;
387         mHandler = new Handler(looper, mHandlerCallback);
388 
389         this.sessionId = sessionId;
390         this.userId = userId;
391         mOriginalInstallerUid = installerUid;
392         mInstallerPackageName = installerPackageName;
393         mInstallerUid = installerUid;
394         this.params = params;
395         this.createdMillis = createdMillis;
396         this.stageDir = stageDir;
397         this.stageCid = stageCid;
398 
399         if ((stageDir == null) == (stageCid == null)) {
400             throw new IllegalArgumentException(
401                     "Exactly one of stageDir or stageCid stage must be set");
402         }
403 
404         mPrepared = prepared;
405 
406         if (sealed) {
407             synchronized (mLock) {
408                 try {
409                     sealAndValidateLocked();
410                 } catch (PackageManagerException | IOException e) {
411                     destroyInternal();
412                     throw new IllegalArgumentException(e);
413                 }
414             }
415         }
416 
417         final long identity = Binder.clearCallingIdentity();
418         try {
419             final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
420                     PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
421             defaultContainerGid = UserHandle.getSharedAppGid(uid);
422         } finally {
423             Binder.restoreCallingIdentity(identity);
424         }
425         // attempt to bind to the DefContainer as early as possible
426         if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
427             mHandler.sendMessage(mHandler.obtainMessage(MSG_EARLY_BIND));
428         }
429     }
430 
generateInfo()431     public SessionInfo generateInfo() {
432         return generateInfo(true);
433     }
434 
generateInfo(boolean includeIcon)435     public SessionInfo generateInfo(boolean includeIcon) {
436         final SessionInfo info = new SessionInfo();
437         synchronized (mLock) {
438             info.sessionId = sessionId;
439             info.installerPackageName = mInstallerPackageName;
440             info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
441                     mResolvedBaseFile.getAbsolutePath() : null;
442             info.progress = mProgress;
443             info.sealed = mSealed;
444             info.active = mActiveCount.get() > 0;
445 
446             info.mode = params.mode;
447             info.installReason = params.installReason;
448             info.sizeBytes = params.sizeBytes;
449             info.appPackageName = params.appPackageName;
450             if (includeIcon) {
451                 info.appIcon = params.appIcon;
452             }
453             info.appLabel = params.appLabel;
454 
455             info.installLocation = params.installLocation;
456             info.originatingUri = params.originatingUri;
457             info.originatingUid = params.originatingUid;
458             info.referrerUri = params.referrerUri;
459             info.grantedRuntimePermissions = params.grantedRuntimePermissions;
460             info.installFlags = params.installFlags;
461         }
462         return info;
463     }
464 
isPrepared()465     public boolean isPrepared() {
466         synchronized (mLock) {
467             return mPrepared;
468         }
469     }
470 
isSealed()471     public boolean isSealed() {
472         synchronized (mLock) {
473             return mSealed;
474         }
475     }
476 
477     @GuardedBy("mLock")
assertPreparedAndNotSealedLocked(String cookie)478     private void assertPreparedAndNotSealedLocked(String cookie) {
479         assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
480         if (mSealed) {
481             throw new SecurityException(cookie + " not allowed after sealing");
482         }
483     }
484 
485     @GuardedBy("mLock")
assertPreparedAndNotCommittedOrDestroyedLocked(String cookie)486     private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
487         assertPreparedAndNotDestroyedLocked(cookie);
488         if (mCommitted) {
489             throw new SecurityException(cookie + " not allowed after commit");
490         }
491     }
492 
493     @GuardedBy("mLock")
assertPreparedAndNotDestroyedLocked(String cookie)494     private void assertPreparedAndNotDestroyedLocked(String cookie) {
495         if (!mPrepared) {
496             throw new IllegalStateException(cookie + " before prepared");
497         }
498         if (mDestroyed) {
499             throw new SecurityException(cookie + " not allowed after destruction");
500         }
501     }
502 
503     /**
504      * Resolve the actual location where staged data should be written. This
505      * might point at an ASEC mount point, which is why we delay path resolution
506      * until someone actively works with the session.
507      */
508     @GuardedBy("mLock")
resolveStageDirLocked()509     private File resolveStageDirLocked() throws IOException {
510         if (mResolvedStageDir == null) {
511             if (stageDir != null) {
512                 mResolvedStageDir = stageDir;
513             } else {
514                 throw new IOException("Missing stageDir");
515             }
516         }
517         return mResolvedStageDir;
518     }
519 
520     @Override
setClientProgress(float progress)521     public void setClientProgress(float progress) {
522         synchronized (mLock) {
523             assertCallerIsOwnerOrRootLocked();
524 
525             // Always publish first staging movement
526             final boolean forcePublish = (mClientProgress == 0);
527             mClientProgress = progress;
528             computeProgressLocked(forcePublish);
529         }
530     }
531 
532     @Override
addClientProgress(float progress)533     public void addClientProgress(float progress) {
534         synchronized (mLock) {
535             assertCallerIsOwnerOrRootLocked();
536 
537             setClientProgress(mClientProgress + progress);
538         }
539     }
540 
541     @GuardedBy("mLock")
computeProgressLocked(boolean forcePublish)542     private void computeProgressLocked(boolean forcePublish) {
543         mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
544                 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
545 
546         // Only publish when meaningful change
547         if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
548             mReportedProgress = mProgress;
549             mCallback.onSessionProgressChanged(this, mProgress);
550         }
551     }
552 
553     @Override
getNames()554     public String[] getNames() {
555         synchronized (mLock) {
556             assertCallerIsOwnerOrRootLocked();
557             assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
558 
559             try {
560                 return resolveStageDirLocked().list();
561             } catch (IOException e) {
562                 throw ExceptionUtils.wrap(e);
563             }
564         }
565     }
566 
567     @Override
removeSplit(String splitName)568     public void removeSplit(String splitName) {
569         if (TextUtils.isEmpty(params.appPackageName)) {
570             throw new IllegalStateException("Must specify package name to remove a split");
571         }
572 
573         synchronized (mLock) {
574             assertCallerIsOwnerOrRootLocked();
575             assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
576 
577             try {
578                 createRemoveSplitMarkerLocked(splitName);
579             } catch (IOException e) {
580                 throw ExceptionUtils.wrap(e);
581             }
582         }
583     }
584 
createRemoveSplitMarkerLocked(String splitName)585     private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
586         try {
587             final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
588             if (!FileUtils.isValidExtFilename(markerName)) {
589                 throw new IllegalArgumentException("Invalid marker: " + markerName);
590             }
591             final File target = new File(resolveStageDirLocked(), markerName);
592             target.createNewFile();
593             Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
594         } catch (ErrnoException e) {
595             throw e.rethrowAsIOException();
596         }
597     }
598 
599     @Override
openWrite(String name, long offsetBytes, long lengthBytes)600     public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
601         try {
602             return doWriteInternal(name, offsetBytes, lengthBytes, null);
603         } catch (IOException e) {
604             throw ExceptionUtils.wrap(e);
605         }
606     }
607 
608     @Override
write(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor fd)609     public void write(String name, long offsetBytes, long lengthBytes,
610             ParcelFileDescriptor fd) {
611         try {
612             doWriteInternal(name, offsetBytes, lengthBytes, fd);
613         } catch (IOException e) {
614             throw ExceptionUtils.wrap(e);
615         }
616     }
617 
doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)618     private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
619             ParcelFileDescriptor incomingFd) throws IOException {
620         // Quick sanity check of state, and allocate a pipe for ourselves. We
621         // then do heavy disk allocation outside the lock, but this open pipe
622         // will block any attempted install transitions.
623         final RevocableFileDescriptor fd;
624         final FileBridge bridge;
625         final File stageDir;
626         synchronized (mLock) {
627             assertCallerIsOwnerOrRootLocked();
628             assertPreparedAndNotSealedLocked("openWrite");
629 
630             if (PackageInstaller.ENABLE_REVOCABLE_FD) {
631                 fd = new RevocableFileDescriptor();
632                 bridge = null;
633                 mFds.add(fd);
634             } else {
635                 fd = null;
636                 bridge = new FileBridge();
637                 mBridges.add(bridge);
638             }
639 
640             stageDir = resolveStageDirLocked();
641         }
642 
643         try {
644             // Use installer provided name for now; we always rename later
645             if (!FileUtils.isValidExtFilename(name)) {
646                 throw new IllegalArgumentException("Invalid name: " + name);
647             }
648             final File target;
649             final long identity = Binder.clearCallingIdentity();
650             try {
651                 target = new File(stageDir, name);
652             } finally {
653                 Binder.restoreCallingIdentity(identity);
654             }
655 
656             // TODO: this should delegate to DCS so the system process avoids
657             // holding open FDs into containers.
658             final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),
659                     O_CREAT | O_WRONLY, 0644);
660             Os.chmod(target.getAbsolutePath(), 0644);
661 
662             // If caller specified a total length, allocate it for them. Free up
663             // cache space to grow, if needed.
664             if (stageDir != null && lengthBytes > 0) {
665                 mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
666                         PackageHelper.translateAllocateFlags(params.installFlags));
667             }
668 
669             if (offsetBytes > 0) {
670                 Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
671             }
672 
673             if (incomingFd != null) {
674                 switch (Binder.getCallingUid()) {
675                     case android.os.Process.SHELL_UID:
676                     case android.os.Process.ROOT_UID:
677                         break;
678                     default:
679                         throw new SecurityException("Reverse mode only supported from shell");
680                 }
681 
682                 // In "reverse" mode, we're streaming data ourselves from the
683                 // incoming FD, which means we never have to hand out our
684                 // sensitive internal FD. We still rely on a "bridge" being
685                 // inserted above to hold the session active.
686                 try {
687                     final Int64Ref last = new Int64Ref(0);
688                     FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> {
689                         if (params.sizeBytes > 0) {
690                             final long delta = progress - last.value;
691                             last.value = progress;
692                             addClientProgress((float) delta / (float) params.sizeBytes);
693                         }
694                     }, null, lengthBytes);
695                 } finally {
696                     IoUtils.closeQuietly(targetFd);
697                     IoUtils.closeQuietly(incomingFd);
698 
699                     // We're done here, so remove the "bridge" that was holding
700                     // the session active.
701                     synchronized (mLock) {
702                         if (PackageInstaller.ENABLE_REVOCABLE_FD) {
703                             mFds.remove(fd);
704                         } else {
705                             mBridges.remove(bridge);
706                         }
707                     }
708                 }
709                 return null;
710             } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
711                 fd.init(mContext, targetFd);
712                 return fd.getRevocableFileDescriptor();
713             } else {
714                 bridge.setTargetFile(targetFd);
715                 bridge.start();
716                 return new ParcelFileDescriptor(bridge.getClientSocket());
717             }
718 
719         } catch (ErrnoException e) {
720             throw e.rethrowAsIOException();
721         }
722     }
723 
724     @Override
openRead(String name)725     public ParcelFileDescriptor openRead(String name) {
726         synchronized (mLock) {
727             assertCallerIsOwnerOrRootLocked();
728             assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
729             try {
730                 return openReadInternalLocked(name);
731             } catch (IOException e) {
732                 throw ExceptionUtils.wrap(e);
733             }
734         }
735     }
736 
openReadInternalLocked(String name)737     private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
738         try {
739             if (!FileUtils.isValidExtFilename(name)) {
740                 throw new IllegalArgumentException("Invalid name: " + name);
741             }
742             final File target = new File(resolveStageDirLocked(), name);
743             final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);
744             return new ParcelFileDescriptor(targetFd);
745         } catch (ErrnoException e) {
746             throw e.rethrowAsIOException();
747         }
748     }
749 
750     /**
751      * Check if the caller is the owner of this session. Otherwise throw a
752      * {@link SecurityException}.
753      */
754     @GuardedBy("mLock")
assertCallerIsOwnerOrRootLocked()755     private void assertCallerIsOwnerOrRootLocked() {
756         final int callingUid = Binder.getCallingUid();
757         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
758             throw new SecurityException("Session does not belong to uid " + callingUid);
759         }
760     }
761 
762     /**
763      * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
764      */
765     @GuardedBy("mLock")
assertNoWriteFileTransfersOpenLocked()766     private void assertNoWriteFileTransfersOpenLocked() {
767         // Verify that all writers are hands-off
768         for (RevocableFileDescriptor fd : mFds) {
769             if (!fd.isRevoked()) {
770                 throw new SecurityException("Files still open");
771             }
772         }
773         for (FileBridge bridge : mBridges) {
774             if (!bridge.isClosed()) {
775                 throw new SecurityException("Files still open");
776             }
777         }
778     }
779 
780     @Override
commit(@onNull IntentSender statusReceiver, boolean forTransfer)781     public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
782         Preconditions.checkNotNull(statusReceiver);
783 
784         final boolean wasSealed;
785         synchronized (mLock) {
786             assertCallerIsOwnerOrRootLocked();
787             assertPreparedAndNotDestroyedLocked("commit");
788 
789             final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
790                     mContext, statusReceiver, sessionId,
791                     isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);
792             mRemoteObserver = adapter.getBinder();
793 
794             if (forTransfer) {
795                 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
796 
797                 if (mInstallerUid == mOriginalInstallerUid) {
798                     throw new IllegalArgumentException("Session has not been transferred");
799                 }
800             } else {
801                 if (mInstallerUid != mOriginalInstallerUid) {
802                     throw new IllegalArgumentException("Session has been transferred");
803                 }
804             }
805 
806             wasSealed = mSealed;
807             if (!mSealed) {
808                 try {
809                     sealAndValidateLocked();
810                 } catch (IOException e) {
811                     throw new IllegalArgumentException(e);
812                 } catch (PackageManagerException e) {
813                     // Do now throw an exception here to stay compatible with O and older
814                     destroyInternal();
815                     dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
816                     return;
817                 }
818             }
819 
820             // Client staging is fully done at this point
821             mClientProgress = 1f;
822             computeProgressLocked(true);
823 
824             // This ongoing commit should keep session active, even though client
825             // will probably close their end.
826             mActiveCount.incrementAndGet();
827 
828             mCommitted = true;
829             mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
830         }
831 
832         if (!wasSealed) {
833             // Persist the fact that we've sealed ourselves to prevent
834             // mutations of any hard links we create. We do this without holding
835             // the session lock, since otherwise it's a lock inversion.
836             mCallback.onSessionSealedBlocking(this);
837         }
838     }
839 
840     /**
841      * Seal the session to prevent further modification and validate the contents of it.
842      *
843      * <p>The session will be sealed after calling this method even if it failed.
844      *
845      * @throws PackageManagerException if the session was sealed but something went wrong. If the
846      *                                 session was sealed this is the only possible exception.
847      */
848     @GuardedBy("mLock")
sealAndValidateLocked()849     private void sealAndValidateLocked() throws PackageManagerException, IOException {
850         assertNoWriteFileTransfersOpenLocked();
851         assertPreparedAndNotDestroyedLocked("sealing of session");
852 
853         final PackageInfo pkgInfo = mPm.getPackageInfo(
854                 params.appPackageName, PackageManager.GET_SIGNATURES
855                         | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
856 
857         resolveStageDirLocked();
858 
859         mSealed = true;
860 
861         // Verify that stage looks sane with respect to existing application.
862         // This currently only ensures packageName, versionCode, and certificate
863         // consistency.
864         try {
865             validateInstallLocked(pkgInfo);
866         } catch (PackageManagerException e) {
867             throw e;
868         } catch (Throwable e) {
869             // Convert all exceptions into package manager exceptions as only those are handled
870             // in the code above
871             throw new PackageManagerException(e);
872         }
873 
874         // Read transfers from the original owner stay open, but as the session's data
875         // cannot be modified anymore, there is no leak of information.
876     }
877 
878     @Override
transfer(String packageName)879     public void transfer(String packageName) {
880         Preconditions.checkNotNull(packageName);
881 
882         ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
883         if (newOwnerAppInfo == null) {
884             throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
885         }
886 
887         if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
888                 Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
889             throw new SecurityException("Destination package " + packageName + " does not have "
890                     + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
891         }
892 
893         // Only install flags that can be verified by the app the session is transferred to are
894         // allowed. The parameters can be read via PackageInstaller.SessionInfo.
895         if (!params.areHiddenOptionsSet()) {
896             throw new SecurityException("Can only transfer sessions that use public options");
897         }
898 
899         synchronized (mLock) {
900             assertCallerIsOwnerOrRootLocked();
901             assertPreparedAndNotSealedLocked("transfer");
902 
903             try {
904                 sealAndValidateLocked();
905             } catch (IOException e) {
906                 throw new IllegalStateException(e);
907             } catch (PackageManagerException e) {
908                 // Session is sealed but could not be verified, we need to destroy it
909                 destroyInternal();
910                 dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
911 
912                 throw new IllegalArgumentException("Package is not valid", e);
913             }
914 
915             if (!mPackageName.equals(mInstallerPackageName)) {
916                 throw new SecurityException("Can only transfer sessions that update the original "
917                         + "installer");
918             }
919 
920             mInstallerPackageName = packageName;
921             mInstallerUid = newOwnerAppInfo.uid;
922         }
923 
924         // Persist the fact that we've sealed ourselves to prevent
925         // mutations of any hard links we create. We do this without holding
926         // the session lock, since otherwise it's a lock inversion.
927         mCallback.onSessionSealedBlocking(this);
928     }
929 
930     @GuardedBy("mLock")
commitLocked()931     private void commitLocked()
932             throws PackageManagerException {
933         if (mDestroyed) {
934             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
935         }
936         if (!mSealed) {
937             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
938         }
939 
940         Preconditions.checkNotNull(mPackageName);
941         Preconditions.checkNotNull(mSigningDetails);
942         Preconditions.checkNotNull(mResolvedBaseFile);
943 
944         if (needToAskForPermissionsLocked()) {
945             // User needs to accept permissions; give installer an intent they
946             // can use to involve user.
947             final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
948             intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
949             intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
950             try {
951                 mRemoteObserver.onUserActionRequired(intent);
952             } catch (RemoteException ignored) {
953             }
954 
955             // Commit was keeping session marked as active until now; release
956             // that extra refcount so session appears idle.
957             closeInternal(false);
958             return;
959         }
960 
961         // Inherit any packages and native libraries from existing install that
962         // haven't been overridden.
963         if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
964             try {
965                 final List<File> fromFiles = mResolvedInheritedFiles;
966                 final File toDir = resolveStageDirLocked();
967 
968                 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
969                 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
970                     throw new IllegalStateException("mInheritedFilesBase == null");
971                 }
972 
973                 if (isLinkPossible(fromFiles, toDir)) {
974                     if (!mResolvedInstructionSets.isEmpty()) {
975                         final File oatDir = new File(toDir, "oat");
976                         createOatDirs(mResolvedInstructionSets, oatDir);
977                     }
978                     // pre-create lib dirs for linking if necessary
979                     if (!mResolvedNativeLibPaths.isEmpty()) {
980                         for (String libPath : mResolvedNativeLibPaths) {
981                             // "/lib/arm64" -> ["lib", "arm64"]
982                             final int splitIndex = libPath.lastIndexOf('/');
983                             if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
984                                 Slog.e(TAG, "Skipping native library creation for linking due to "
985                                         + "invalid path: " + libPath);
986                                 continue;
987                             }
988                             final String libDirPath = libPath.substring(1, splitIndex);
989                             final File libDir = new File(toDir, libDirPath);
990                             if (!libDir.exists()) {
991                                 NativeLibraryHelper.createNativeLibrarySubdir(libDir);
992                             }
993                             final String archDirPath = libPath.substring(splitIndex + 1);
994                             NativeLibraryHelper.createNativeLibrarySubdir(
995                                     new File(libDir, archDirPath));
996                         }
997                     }
998                     linkFiles(fromFiles, toDir, mInheritedFilesBase);
999                 } else {
1000                     // TODO: this should delegate to DCS so the system process
1001                     // avoids holding open FDs into containers.
1002                     copyFiles(fromFiles, toDir);
1003                 }
1004             } catch (IOException e) {
1005                 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
1006                         "Failed to inherit existing install", e);
1007             }
1008         }
1009 
1010         // TODO: surface more granular state from dexopt
1011         mInternalProgress = 0.5f;
1012         computeProgressLocked(true);
1013 
1014         // Unpack native libraries
1015         extractNativeLibraries(mResolvedStageDir, params.abiOverride, mayInheritNativeLibs());
1016 
1017         // We've reached point of no return; call into PMS to install the stage.
1018         // Regardless of success or failure we always destroy session.
1019         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
1020             @Override
1021             public void onUserActionRequired(Intent intent) {
1022                 throw new IllegalStateException();
1023             }
1024 
1025             @Override
1026             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
1027                     Bundle extras) {
1028                 destroyInternal();
1029                 dispatchSessionFinished(returnCode, msg, extras);
1030             }
1031         };
1032 
1033         final UserHandle user;
1034         if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
1035             user = UserHandle.ALL;
1036         } else {
1037             user = new UserHandle(userId);
1038         }
1039 
1040         mRelinquished = true;
1041         mPm.installStage(mPackageName, stageDir, localObserver, params,
1042                 mInstallerPackageName, mInstallerUid, user, mSigningDetails);
1043     }
1044 
maybeRenameFile(File from, File to)1045     private static void maybeRenameFile(File from, File to) throws PackageManagerException {
1046         if (!from.equals(to)) {
1047             if (!from.renameTo(to)) {
1048                 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1049                         "Could not rename file " + from + " to " + to);
1050             }
1051         }
1052     }
1053 
1054     /**
1055      * Returns true if the session should attempt to inherit any existing native libraries already
1056      * extracted at the current install location. This is necessary to prevent double loading of
1057      * native libraries already loaded by the running app.
1058      */
mayInheritNativeLibs()1059     private boolean mayInheritNativeLibs() {
1060         return SystemProperties.getBoolean(PROPERTY_NAME_INHERIT_NATIVE, true) &&
1061                 params.mode == SessionParams.MODE_INHERIT_EXISTING &&
1062                 (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
1063     }
1064 
1065     /**
1066      * Validate install by confirming that all application packages are have
1067      * consistent package name, version code, and signing certificates.
1068      * <p>
1069      * Clears and populates {@link #mResolvedBaseFile},
1070      * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
1071      * <p>
1072      * Renames package files in stage to match split names defined inside.
1073      * <p>
1074      * Note that upgrade compatibility is still performed by
1075      * {@link PackageManagerService}.
1076      */
1077     @GuardedBy("mLock")
validateInstallLocked(@ullable PackageInfo pkgInfo)1078     private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
1079             throws PackageManagerException {
1080         mPackageName = null;
1081         mVersionCode = -1;
1082         mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
1083 
1084         mResolvedBaseFile = null;
1085         mResolvedStagedFiles.clear();
1086         mResolvedInheritedFiles.clear();
1087 
1088         try {
1089             resolveStageDirLocked();
1090         } catch (IOException e) {
1091             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1092                     "Failed to resolve stage location", e);
1093         }
1094 
1095         final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
1096         final List<String> removeSplitList = new ArrayList<>();
1097         if (!ArrayUtils.isEmpty(removedFiles)) {
1098             for (File removedFile : removedFiles) {
1099                 final String fileName = removedFile.getName();
1100                 final String splitName = fileName.substring(
1101                         0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
1102                 removeSplitList.add(splitName);
1103             }
1104         }
1105 
1106         final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
1107         if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
1108             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
1109         }
1110 
1111         // Verify that all staged packages are internally consistent
1112         final ArraySet<String> stagedSplits = new ArraySet<>();
1113         for (File addedFile : addedFiles) {
1114             final ApkLite apk;
1115             try {
1116                 apk = PackageParser.parseApkLite(
1117                         addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
1118             } catch (PackageParserException e) {
1119                 throw PackageManagerException.from(e);
1120             }
1121 
1122             if (!stagedSplits.add(apk.splitName)) {
1123                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1124                         "Split " + apk.splitName + " was defined multiple times");
1125             }
1126 
1127             // Use first package to define unknown values
1128             if (mPackageName == null) {
1129                 mPackageName = apk.packageName;
1130                 mVersionCode = apk.getLongVersionCode();
1131             }
1132             if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
1133                 mSigningDetails = apk.signingDetails;
1134             }
1135 
1136             assertApkConsistentLocked(String.valueOf(addedFile), apk);
1137 
1138             // Take this opportunity to enforce uniform naming
1139             final String targetName;
1140             if (apk.splitName == null) {
1141                 targetName = "base" + APK_FILE_EXTENSION;
1142             } else {
1143                 targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
1144             }
1145             if (!FileUtils.isValidExtFilename(targetName)) {
1146                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1147                         "Invalid filename: " + targetName);
1148             }
1149 
1150             final File targetFile = new File(mResolvedStageDir, targetName);
1151             maybeRenameFile(addedFile, targetFile);
1152 
1153             // Base is coming from session
1154             if (apk.splitName == null) {
1155                 mResolvedBaseFile = targetFile;
1156             }
1157 
1158             mResolvedStagedFiles.add(targetFile);
1159 
1160             final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
1161             if (dexMetadataFile != null) {
1162                 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
1163                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1164                             "Invalid filename: " + dexMetadataFile);
1165                 }
1166                 final File targetDexMetadataFile = new File(mResolvedStageDir,
1167                         DexMetadataHelper.buildDexMetadataPathForApk(targetName));
1168                 mResolvedStagedFiles.add(targetDexMetadataFile);
1169                 maybeRenameFile(dexMetadataFile, targetDexMetadataFile);
1170             }
1171         }
1172 
1173         if (removeSplitList.size() > 0) {
1174             if (pkgInfo == null) {
1175                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1176                         "Missing existing base package for " + mPackageName);
1177             }
1178 
1179             // validate split names marked for removal
1180             for (String splitName : removeSplitList) {
1181                 if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
1182                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1183                             "Split not found: " + splitName);
1184                 }
1185             }
1186 
1187             // ensure we've got appropriate package name, version code and signatures
1188             if (mPackageName == null) {
1189                 mPackageName = pkgInfo.packageName;
1190                 mVersionCode = pkgInfo.getLongVersionCode();
1191             }
1192             if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
1193                 try {
1194                     mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
1195                             pkgInfo.applicationInfo.sourceDir,
1196                             PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
1197                 } catch (PackageParserException e) {
1198                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1199                             "Couldn't obtain signatures from base APK");
1200                 }
1201             }
1202         }
1203 
1204         if (params.mode == SessionParams.MODE_FULL_INSTALL) {
1205             // Full installs must include a base package
1206             if (!stagedSplits.contains(null)) {
1207                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1208                         "Full install must include a base package");
1209             }
1210 
1211         } else {
1212             // Partial installs must be consistent with existing install
1213             if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1214                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1215                         "Missing existing base package for " + mPackageName);
1216             }
1217 
1218             final PackageLite existing;
1219             final ApkLite existingBase;
1220             ApplicationInfo appInfo = pkgInfo.applicationInfo;
1221             try {
1222                 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
1223                 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
1224                         PackageParser.PARSE_COLLECT_CERTIFICATES);
1225             } catch (PackageParserException e) {
1226                 throw PackageManagerException.from(e);
1227             }
1228 
1229             assertApkConsistentLocked("Existing base", existingBase);
1230 
1231             // Inherit base if not overridden
1232             if (mResolvedBaseFile == null) {
1233                 mResolvedBaseFile = new File(appInfo.getBaseCodePath());
1234                 mResolvedInheritedFiles.add(mResolvedBaseFile);
1235                 // Inherit the dex metadata if present.
1236                 final File baseDexMetadataFile =
1237                         DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
1238                 if (baseDexMetadataFile != null) {
1239                     mResolvedInheritedFiles.add(baseDexMetadataFile);
1240                 }
1241             }
1242 
1243             // Inherit splits if not overridden
1244             if (!ArrayUtils.isEmpty(existing.splitNames)) {
1245                 for (int i = 0; i < existing.splitNames.length; i++) {
1246                     final String splitName = existing.splitNames[i];
1247                     final File splitFile = new File(existing.splitCodePaths[i]);
1248                     final boolean splitRemoved = removeSplitList.contains(splitName);
1249                     if (!stagedSplits.contains(splitName) && !splitRemoved) {
1250                         mResolvedInheritedFiles.add(splitFile);
1251                         // Inherit the dex metadata if present.
1252                         final File splitDexMetadataFile =
1253                                 DexMetadataHelper.findDexMetadataForFile(splitFile);
1254                         if (splitDexMetadataFile != null) {
1255                             mResolvedInheritedFiles.add(splitDexMetadataFile);
1256                         }
1257                     }
1258                 }
1259             }
1260 
1261             // Inherit compiled oat directory.
1262             final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
1263             mInheritedFilesBase = packageInstallDir;
1264             final File oatDir = new File(packageInstallDir, "oat");
1265             if (oatDir.exists()) {
1266                 final File[] archSubdirs = oatDir.listFiles();
1267 
1268                 // Keep track of all instruction sets we've seen compiled output for.
1269                 // If we're linking (and not copying) inherited files, we can recreate the
1270                 // instruction set hierarchy and link compiled output.
1271                 if (archSubdirs != null && archSubdirs.length > 0) {
1272                     final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
1273                     for (File archSubDir : archSubdirs) {
1274                         // Skip any directory that isn't an ISA subdir.
1275                         if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
1276                             continue;
1277                         }
1278 
1279                         mResolvedInstructionSets.add(archSubDir.getName());
1280                         List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
1281                         if (!oatFiles.isEmpty()) {
1282                             mResolvedInheritedFiles.addAll(oatFiles);
1283                         }
1284                     }
1285                 }
1286             }
1287 
1288             // Inherit native libraries for DONT_KILL sessions.
1289             if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
1290                 File[] libDirs = new File[]{
1291                         new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME),
1292                         new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)};
1293                 for (File libDir : libDirs) {
1294                     if (!libDir.exists() || !libDir.isDirectory()) {
1295                         continue;
1296                     }
1297                     final List<File> libDirsToInherit = new LinkedList<>();
1298                     for (File archSubDir : libDir.listFiles()) {
1299                         if (!archSubDir.isDirectory()) {
1300                             continue;
1301                         }
1302                         String relLibPath;
1303                         try {
1304                             relLibPath = getRelativePath(archSubDir, packageInstallDir);
1305                         } catch (IOException e) {
1306                             Slog.e(TAG, "Skipping linking of native library directory!", e);
1307                             // shouldn't be possible, but let's avoid inheriting these to be safe
1308                             libDirsToInherit.clear();
1309                             break;
1310                         }
1311                         if (!mResolvedNativeLibPaths.contains(relLibPath)) {
1312                             mResolvedNativeLibPaths.add(relLibPath);
1313                         }
1314                         libDirsToInherit.addAll(Arrays.asList(archSubDir.listFiles()));
1315                     }
1316                     mResolvedInheritedFiles.addAll(libDirsToInherit);
1317                 }
1318             }
1319         }
1320     }
1321 
1322     @GuardedBy("mLock")
assertApkConsistentLocked(String tag, ApkLite apk)1323     private void assertApkConsistentLocked(String tag, ApkLite apk)
1324             throws PackageManagerException {
1325         if (!mPackageName.equals(apk.packageName)) {
1326             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
1327                     + apk.packageName + " inconsistent with " + mPackageName);
1328         }
1329         if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
1330             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1331                     + " specified package " + params.appPackageName
1332                     + " inconsistent with " + apk.packageName);
1333         }
1334         if (mVersionCode != apk.getLongVersionCode()) {
1335             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1336                     + " version code " + apk.versionCode + " inconsistent with "
1337                     + mVersionCode);
1338         }
1339         if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
1340             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1341                     tag + " signatures are inconsistent");
1342         }
1343     }
1344 
1345     /**
1346      * Determine if creating hard links between source and destination is
1347      * possible. That is, do they all live on the same underlying device.
1348      */
isLinkPossible(List<File> fromFiles, File toDir)1349     private boolean isLinkPossible(List<File> fromFiles, File toDir) {
1350         try {
1351             final StructStat toStat = Os.stat(toDir.getAbsolutePath());
1352             for (File fromFile : fromFiles) {
1353                 final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
1354                 if (fromStat.st_dev != toStat.st_dev) {
1355                     return false;
1356                 }
1357             }
1358         } catch (ErrnoException e) {
1359             Slog.w(TAG, "Failed to detect if linking possible: " + e);
1360             return false;
1361         }
1362         return true;
1363     }
1364 
1365     /**
1366      * @return the uid of the owner this session
1367      */
getInstallerUid()1368     public int getInstallerUid() {
1369         synchronized (mLock) {
1370             return mInstallerUid;
1371         }
1372     }
1373 
getRelativePath(File file, File base)1374     private static String getRelativePath(File file, File base) throws IOException {
1375         final String pathStr = file.getAbsolutePath();
1376         final String baseStr = base.getAbsolutePath();
1377         // Don't allow relative paths.
1378         if (pathStr.contains("/.") ) {
1379             throw new IOException("Invalid path (was relative) : " + pathStr);
1380         }
1381 
1382         if (pathStr.startsWith(baseStr)) {
1383             return pathStr.substring(baseStr.length());
1384         }
1385 
1386         throw new IOException("File: " + pathStr + " outside base: " + baseStr);
1387     }
1388 
createOatDirs(List<String> instructionSets, File fromDir)1389     private void createOatDirs(List<String> instructionSets, File fromDir)
1390             throws PackageManagerException {
1391         for (String instructionSet : instructionSets) {
1392             try {
1393                 mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
1394             } catch (InstallerException e) {
1395                 throw PackageManagerException.from(e);
1396             }
1397         }
1398     }
1399 
linkFiles(List<File> fromFiles, File toDir, File fromDir)1400     private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
1401             throws IOException {
1402         for (File fromFile : fromFiles) {
1403             final String relativePath = getRelativePath(fromFile, fromDir);
1404             try {
1405                 mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
1406                         toDir.getAbsolutePath());
1407             } catch (InstallerException e) {
1408                 throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
1409                         + fromDir + ", " + toDir + ")", e);
1410             }
1411         }
1412 
1413         Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
1414     }
1415 
copyFiles(List<File> fromFiles, File toDir)1416     private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
1417         // Remove any partial files from previous attempt
1418         for (File file : toDir.listFiles()) {
1419             if (file.getName().endsWith(".tmp")) {
1420                 file.delete();
1421             }
1422         }
1423 
1424         for (File fromFile : fromFiles) {
1425             final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
1426             if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
1427             if (!FileUtils.copyFile(fromFile, tmpFile)) {
1428                 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
1429             }
1430             try {
1431                 Os.chmod(tmpFile.getAbsolutePath(), 0644);
1432             } catch (ErrnoException e) {
1433                 throw new IOException("Failed to chmod " + tmpFile);
1434             }
1435             final File toFile = new File(toDir, fromFile.getName());
1436             if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
1437             if (!tmpFile.renameTo(toFile)) {
1438                 throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
1439             }
1440         }
1441         Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
1442     }
1443 
extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)1444     private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
1445             throws PackageManagerException {
1446         final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
1447         if (!inherit) {
1448             // Start from a clean slate
1449             NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
1450         }
1451 
1452         NativeLibraryHelper.Handle handle = null;
1453         try {
1454             handle = NativeLibraryHelper.Handle.create(packageDir);
1455             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
1456                     abiOverride);
1457             if (res != PackageManager.INSTALL_SUCCEEDED) {
1458                 throw new PackageManagerException(res,
1459                         "Failed to extract native libraries, res=" + res);
1460             }
1461         } catch (IOException e) {
1462             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1463                     "Failed to extract native libraries", e);
1464         } finally {
1465             IoUtils.closeQuietly(handle);
1466         }
1467     }
1468 
setPermissionsResult(boolean accepted)1469     void setPermissionsResult(boolean accepted) {
1470         if (!mSealed) {
1471             throw new SecurityException("Must be sealed to accept permissions");
1472         }
1473 
1474         if (accepted) {
1475             // Mark and kick off another install pass
1476             synchronized (mLock) {
1477                 mPermissionsManuallyAccepted = true;
1478                 mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
1479             }
1480         } else {
1481             destroyInternal();
1482             dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
1483         }
1484     }
1485 
open()1486     public void open() throws IOException {
1487         if (mActiveCount.getAndIncrement() == 0) {
1488             mCallback.onSessionActiveChanged(this, true);
1489         }
1490 
1491         boolean wasPrepared;
1492         synchronized (mLock) {
1493             wasPrepared = mPrepared;
1494             if (!mPrepared) {
1495                 if (stageDir != null) {
1496                     prepareStageDir(stageDir);
1497                 } else {
1498                     throw new IllegalArgumentException("stageDir must be set");
1499                 }
1500 
1501                 mPrepared = true;
1502             }
1503         }
1504 
1505         if (!wasPrepared) {
1506             mCallback.onSessionPrepared(this);
1507         }
1508     }
1509 
1510     @Override
close()1511     public void close() {
1512         closeInternal(true);
1513     }
1514 
closeInternal(boolean checkCaller)1515     private void closeInternal(boolean checkCaller) {
1516         int activeCount;
1517         synchronized (mLock) {
1518             if (checkCaller) {
1519                 assertCallerIsOwnerOrRootLocked();
1520             }
1521 
1522             activeCount = mActiveCount.decrementAndGet();
1523         }
1524 
1525         if (activeCount == 0) {
1526             mCallback.onSessionActiveChanged(this, false);
1527         }
1528     }
1529 
1530     @Override
abandon()1531     public void abandon() {
1532         synchronized (mLock) {
1533             assertCallerIsOwnerOrRootLocked();
1534 
1535             if (mRelinquished) {
1536                 Slog.d(TAG, "Ignoring abandon after commit relinquished control");
1537                 return;
1538             }
1539             destroyInternal();
1540         }
1541 
1542         dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
1543     }
1544 
dispatchSessionFinished(int returnCode, String msg, Bundle extras)1545     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
1546         final IPackageInstallObserver2 observer;
1547         final String packageName;
1548         synchronized (mLock) {
1549             mFinalStatus = returnCode;
1550             mFinalMessage = msg;
1551 
1552             observer = mRemoteObserver;
1553             packageName = mPackageName;
1554         }
1555 
1556         if (observer != null) {
1557             // Execute observer.onPackageInstalled on different tread as we don't want callers
1558             // inside the system server have to worry about catching the callbacks while they are
1559             // calling into the session
1560             final SomeArgs args = SomeArgs.obtain();
1561             args.arg1 = packageName;
1562             args.arg2 = msg;
1563             args.arg3 = extras;
1564             args.arg4 = observer;
1565             args.argi1 = returnCode;
1566 
1567             mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
1568         }
1569 
1570         final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
1571 
1572         // Send broadcast to default launcher only if it's a new install
1573         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
1574         if (success && isNewInstall) {
1575             mPm.sendSessionCommitBroadcast(generateInfo(), userId);
1576         }
1577 
1578         mCallback.onSessionFinished(this, success);
1579     }
1580 
destroyInternal()1581     private void destroyInternal() {
1582         synchronized (mLock) {
1583             mSealed = true;
1584             mDestroyed = true;
1585 
1586             // Force shut down all bridges
1587             for (RevocableFileDescriptor fd : mFds) {
1588                 fd.revoke();
1589             }
1590             for (FileBridge bridge : mBridges) {
1591                 bridge.forceClose();
1592             }
1593         }
1594         if (stageDir != null) {
1595             try {
1596                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
1597             } catch (InstallerException ignored) {
1598             }
1599         }
1600     }
1601 
dump(IndentingPrintWriter pw)1602     void dump(IndentingPrintWriter pw) {
1603         synchronized (mLock) {
1604             dumpLocked(pw);
1605         }
1606     }
1607 
1608     @GuardedBy("mLock")
dumpLocked(IndentingPrintWriter pw)1609     private void dumpLocked(IndentingPrintWriter pw) {
1610         pw.println("Session " + sessionId + ":");
1611         pw.increaseIndent();
1612 
1613         pw.printPair("userId", userId);
1614         pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
1615         pw.printPair("mInstallerPackageName", mInstallerPackageName);
1616         pw.printPair("mInstallerUid", mInstallerUid);
1617         pw.printPair("createdMillis", createdMillis);
1618         pw.printPair("stageDir", stageDir);
1619         pw.printPair("stageCid", stageCid);
1620         pw.println();
1621 
1622         params.dump(pw);
1623 
1624         pw.printPair("mClientProgress", mClientProgress);
1625         pw.printPair("mProgress", mProgress);
1626         pw.printPair("mSealed", mSealed);
1627         pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
1628         pw.printPair("mRelinquished", mRelinquished);
1629         pw.printPair("mDestroyed", mDestroyed);
1630         pw.printPair("mFds", mFds.size());
1631         pw.printPair("mBridges", mBridges.size());
1632         pw.printPair("mFinalStatus", mFinalStatus);
1633         pw.printPair("mFinalMessage", mFinalMessage);
1634         pw.println();
1635 
1636         pw.decreaseIndent();
1637     }
1638 
writeGrantedRuntimePermissionsLocked(XmlSerializer out, String[] grantedRuntimePermissions)1639     private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
1640             String[] grantedRuntimePermissions) throws IOException {
1641         if (grantedRuntimePermissions != null) {
1642             for (String permission : grantedRuntimePermissions) {
1643                 out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1644                 writeStringAttribute(out, ATTR_NAME, permission);
1645                 out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1646             }
1647         }
1648     }
1649 
buildAppIconFile(int sessionId, @NonNull File sessionsDir)1650     private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
1651         return new File(sessionsDir, "app_icon." + sessionId + ".png");
1652     }
1653 
1654     /**
1655      * Write this session to a {@link XmlSerializer}.
1656      *
1657      * @param out Where to write the session to
1658      * @param sessionsDir The directory containing the sessions
1659      */
write(@onNull XmlSerializer out, @NonNull File sessionsDir)1660     void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
1661         synchronized (mLock) {
1662             if (mDestroyed) {
1663                 return;
1664             }
1665 
1666             out.startTag(null, TAG_SESSION);
1667 
1668             writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
1669             writeIntAttribute(out, ATTR_USER_ID, userId);
1670             writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
1671                     mInstallerPackageName);
1672             writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
1673             writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
1674             if (stageDir != null) {
1675                 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
1676                         stageDir.getAbsolutePath());
1677             }
1678             if (stageCid != null) {
1679                 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
1680             }
1681             writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
1682             writeBooleanAttribute(out, ATTR_SEALED, isSealed());
1683 
1684             writeIntAttribute(out, ATTR_MODE, params.mode);
1685             writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
1686             writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
1687             writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
1688             writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
1689             writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
1690             writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
1691             writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
1692             writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
1693             writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
1694             writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
1695             writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
1696 
1697             writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
1698 
1699             // Persist app icon if changed since last written
1700             File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1701             if (params.appIcon == null && appIconFile.exists()) {
1702                 appIconFile.delete();
1703             } else if (params.appIcon != null
1704                     && appIconFile.lastModified() != params.appIconLastModified) {
1705                 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
1706                 FileOutputStream os = null;
1707                 try {
1708                     os = new FileOutputStream(appIconFile);
1709                     params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
1710                 } catch (IOException e) {
1711                     Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
1712                 } finally {
1713                     IoUtils.closeQuietly(os);
1714                 }
1715 
1716                 params.appIconLastModified = appIconFile.lastModified();
1717             }
1718         }
1719 
1720         out.endTag(null, TAG_SESSION);
1721     }
1722 
readGrantedRuntimePermissions(XmlPullParser in)1723     private static String[] readGrantedRuntimePermissions(XmlPullParser in)
1724             throws IOException, XmlPullParserException {
1725         List<String> permissions = null;
1726 
1727         final int outerDepth = in.getDepth();
1728         int type;
1729         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
1730                 && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
1731             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1732                 continue;
1733             }
1734             if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
1735                 String permission = readStringAttribute(in, ATTR_NAME);
1736                 if (permissions == null) {
1737                     permissions = new ArrayList<>();
1738                 }
1739                 permissions.add(permission);
1740             }
1741         }
1742 
1743         if (permissions == null) {
1744             return null;
1745         }
1746 
1747         String[] permissionsArray = new String[permissions.size()];
1748         permissions.toArray(permissionsArray);
1749         return permissionsArray;
1750     }
1751 
1752     /**
1753      * Read new session from a {@link XmlPullParser xml description} and create it.
1754      *
1755      * @param in The source of the description
1756      * @param callback Callback the session uses to notify about changes of it's state
1757      * @param context Context to be used by the session
1758      * @param pm PackageManager to use by the session
1759      * @param installerThread Thread to be used for callbacks of this session
1760      * @param sessionsDir The directory the sessions are stored in
1761      *
1762      * @return The newly created session
1763      */
readFromXml(@onNull XmlPullParser in, @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context, @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)1764     public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
1765             @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
1766             @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)
1767             throws IOException, XmlPullParserException {
1768         final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
1769         final int userId = readIntAttribute(in, ATTR_USER_ID);
1770         final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
1771         final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
1772                 installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
1773         final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1774         final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
1775         final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
1776         final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
1777         final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
1778         final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
1779 
1780         final SessionParams params = new SessionParams(
1781                 SessionParams.MODE_INVALID);
1782         params.mode = readIntAttribute(in, ATTR_MODE);
1783         params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
1784         params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
1785         params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
1786         params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
1787         params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
1788         params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
1789         params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
1790         params.originatingUid =
1791                 readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
1792         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
1793         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
1794         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
1795         params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
1796 
1797         params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
1798 
1799         final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1800         if (appIconFile.exists()) {
1801             params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
1802             params.appIconLastModified = appIconFile.lastModified();
1803         }
1804 
1805         return new PackageInstallerSession(callback, context, pm,
1806                 installerThread, sessionId, userId, installerPackageName, installerUid,
1807                 params, createdMillis, stageDir, stageCid, prepared, sealed);
1808     }
1809 }
1810