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.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
20 import static android.os.Process.INVALID_UID;
21 
22 import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
23 
24 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
25 import static org.xmlpull.v1.XmlPullParser.START_TAG;
26 
27 import android.Manifest;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.app.ActivityManager;
31 import android.app.AppGlobals;
32 import android.app.AppOpsManager;
33 import android.app.BroadcastOptions;
34 import android.app.Notification;
35 import android.app.NotificationManager;
36 import android.app.PackageDeleteObserver;
37 import android.app.admin.DevicePolicyEventLogger;
38 import android.app.admin.DevicePolicyManager;
39 import android.app.admin.DevicePolicyManagerInternal;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentSender;
43 import android.content.IntentSender.SendIntentException;
44 import android.content.pm.ApplicationInfo;
45 import android.content.pm.IPackageInstaller;
46 import android.content.pm.IPackageInstallerCallback;
47 import android.content.pm.IPackageInstallerSession;
48 import android.content.pm.PackageInfo;
49 import android.content.pm.PackageInstaller;
50 import android.content.pm.PackageInstaller.InstallConstraints;
51 import android.content.pm.PackageInstaller.InstallConstraintsResult;
52 import android.content.pm.PackageInstaller.SessionInfo;
53 import android.content.pm.PackageInstaller.SessionParams;
54 import android.content.pm.PackageItemInfo;
55 import android.content.pm.PackageManager;
56 import android.content.pm.ParceledListSlice;
57 import android.content.pm.VersionedPackage;
58 import android.content.pm.parsing.FrameworkParsingPackageUtils;
59 import android.graphics.Bitmap;
60 import android.net.Uri;
61 import android.os.Binder;
62 import android.os.Build;
63 import android.os.Bundle;
64 import android.os.Environment;
65 import android.os.Handler;
66 import android.os.HandlerThread;
67 import android.os.Looper;
68 import android.os.Message;
69 import android.os.Process;
70 import android.os.RemoteCallback;
71 import android.os.RemoteCallbackList;
72 import android.os.RemoteException;
73 import android.os.SELinux;
74 import android.os.UserHandle;
75 import android.os.UserManager;
76 import android.os.storage.StorageManager;
77 import android.stats.devicepolicy.DevicePolicyEnums;
78 import android.system.ErrnoException;
79 import android.system.Os;
80 import android.text.TextUtils;
81 import android.text.format.DateUtils;
82 import android.util.ArraySet;
83 import android.util.AtomicFile;
84 import android.util.ExceptionUtils;
85 import android.util.Log;
86 import android.util.Slog;
87 import android.util.SparseArray;
88 import android.util.SparseBooleanArray;
89 import android.util.SparseIntArray;
90 import android.util.Xml;
91 
92 import com.android.internal.R;
93 import com.android.internal.annotations.GuardedBy;
94 import com.android.internal.annotations.VisibleForTesting;
95 import com.android.internal.content.InstallLocationUtils;
96 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
97 import com.android.internal.notification.SystemNotificationChannels;
98 import com.android.internal.util.ImageUtils;
99 import com.android.internal.util.IndentingPrintWriter;
100 import com.android.modules.utils.TypedXmlPullParser;
101 import com.android.modules.utils.TypedXmlSerializer;
102 import com.android.server.IoThread;
103 import com.android.server.LocalServices;
104 import com.android.server.SystemConfig;
105 import com.android.server.SystemService;
106 import com.android.server.SystemServiceManager;
107 import com.android.server.pm.parsing.PackageParser2;
108 import com.android.server.pm.pkg.PackageStateInternal;
109 import com.android.server.pm.utils.RequestThrottle;
110 
111 import libcore.io.IoUtils;
112 
113 import org.xmlpull.v1.XmlPullParserException;
114 
115 import java.io.CharArrayWriter;
116 import java.io.File;
117 import java.io.FileInputStream;
118 import java.io.FileNotFoundException;
119 import java.io.FileOutputStream;
120 import java.io.FilenameFilter;
121 import java.io.IOException;
122 import java.security.SecureRandom;
123 import java.util.ArrayList;
124 import java.util.Collections;
125 import java.util.Comparator;
126 import java.util.List;
127 import java.util.Map;
128 import java.util.Objects;
129 import java.util.Random;
130 import java.util.Set;
131 import java.util.TreeMap;
132 import java.util.TreeSet;
133 import java.util.concurrent.CompletableFuture;
134 import java.util.function.IntPredicate;
135 import java.util.function.Supplier;
136 
137 /** The service responsible for installing packages. */
138 public class PackageInstallerService extends IPackageInstaller.Stub implements
139         PackageSessionProvider {
140     private static final String TAG = "PackageInstaller";
141     private static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
142 
143     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
144 
145     // TODO: remove outstanding sessions when installer package goes away
146     // TODO: notify listeners in other users when package has been installed there
147     // TODO: purge expired sessions periodically in addition to at reboot
148 
149     /** XML constants used in {@link #mSessionsFile} */
150     private static final String TAG_SESSIONS = "sessions";
151 
152     /** Automatically destroy sessions older than this */
153     private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
154     /** Automatically destroy staged sessions that have not changed state in this time */
155     private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 21 * DateUtils.DAY_IN_MILLIS;
156     /** Upper bound on number of active sessions for a UID that has INSTALL_PACKAGES */
157     private static final long MAX_ACTIVE_SESSIONS_WITH_PERMISSION = 1024;
158     /** Upper bound on number of active sessions for a UID without INSTALL_PACKAGES */
159     private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50;
160     /** Upper bound on number of historical sessions for a UID */
161     private static final long MAX_HISTORICAL_SESSIONS = 1048576;
162     /** Destroy sessions older than this on storage free request */
163     private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS;
164     /** Maximum time to wait for install constraints to be satisfied */
165     private static final long MAX_INSTALL_CONSTRAINTS_TIMEOUT_MILLIS = DateUtils.WEEK_IN_MILLIS;
166 
167     /** Threshold of historical sessions size */
168     private static final int HISTORICAL_SESSIONS_THRESHOLD = 500;
169     /** Size of historical sessions to be cleared when reaching threshold */
170     private static final int HISTORICAL_CLEAR_SIZE = 400;
171 
172     /**
173      * Allow verification-skipping if it's a development app installed through ADB with
174      * disable verification flag specified.
175      */
176     private static final int ADB_DEV_MODE = PackageManager.INSTALL_FROM_ADB
177             | PackageManager.INSTALL_ALLOW_TEST;
178 
179     /**
180      * Set of app op permissions that the installer of a session is allowed to change through
181      * {@link PackageInstaller.SessionParams#setPermissionState(String, int)}.
182      */
183     public static final Set<String> INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS = Set.of(
184             Manifest.permission.USE_FULL_SCREEN_INTENT
185     );
186 
187     private final Context mContext;
188     private final PackageManagerService mPm;
189     private final ApexManager mApexManager;
190     private final StagingManager mStagingManager;
191 
192     private AppOpsManager mAppOps;
193 
194     private final HandlerThread mInstallThread;
195     private final Handler mInstallHandler;
196 
197     private final Callbacks mCallbacks;
198 
199     private volatile boolean mOkToSendBroadcasts = false;
200     private volatile boolean mBypassNextStagedInstallerCheck = false;
201     private volatile boolean mBypassNextAllowedApexUpdateCheck = false;
202     private volatile int mDisableVerificationForUid = INVALID_UID;
203 
204     /**
205      * File storing persisted {@link #mSessions} metadata.
206      */
207     private final AtomicFile mSessionsFile;
208 
209     /**
210      * Directory storing persisted {@link #mSessions} metadata which is too
211      * heavy to store directly in {@link #mSessionsFile}.
212      */
213     private final File mSessionsDir;
214 
215     private final InternalCallback mInternalCallback = new InternalCallback();
216     private final PackageSessionVerifier mSessionVerifier;
217     private final GentleUpdateHelper mGentleUpdateHelper;
218 
219     /**
220      * Used for generating session IDs. Since this is created at boot time,
221      * normal random might be predictable.
222      */
223     private final Random mRandom = new SecureRandom();
224 
225     /** All sessions allocated */
226     @GuardedBy("mSessions")
227     private final SparseBooleanArray mAllocatedSessions = new SparseBooleanArray();
228 
229     @GuardedBy("mSessions")
230     private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
231 
232     /** Historical sessions kept around for debugging purposes */
233     @GuardedBy("mSessions")
234     private final List<String> mHistoricalSessions = new ArrayList<>();
235 
236     @GuardedBy("mSessions")
237     private final SparseIntArray mHistoricalSessionsByInstaller = new SparseIntArray();
238 
239     /** Sessions allocated to legacy users */
240     @GuardedBy("mSessions")
241     private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
242 
243     /** Policy for allowing a silent update. */
244     private final SilentUpdatePolicy mSilentUpdatePolicy = new SilentUpdatePolicy();
245 
246     private static final FilenameFilter sStageFilter = new FilenameFilter() {
247         @Override
248         public boolean accept(File dir, String name) {
249             return isStageName(name);
250         }
251     };
252 
253     private static final class Lifecycle extends SystemService {
254         private final PackageInstallerService mPackageInstallerService;
255 
Lifecycle(Context context, PackageInstallerService service)256         Lifecycle(Context context, PackageInstallerService service) {
257             super(context);
258             mPackageInstallerService = service;
259         }
260 
261         @Override
onStart()262         public void onStart() {
263             // no-op
264         }
265 
266         @Override
onBootPhase(int phase)267         public void onBootPhase(int phase) {
268             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
269                 mPackageInstallerService.onBroadcastReady();
270             }
271         }
272     }
273 
274     @NonNull
275     private final RequestThrottle mSettingsWriteRequest = new RequestThrottle(IoThread.getHandler(),
276             () -> {
277                 synchronized (mSessions) {
278                     return writeSessionsLocked();
279                 }
280             });
281 
PackageInstallerService(Context context, PackageManagerService pm, Supplier<PackageParser2> apexParserSupplier)282     public PackageInstallerService(Context context, PackageManagerService pm,
283             Supplier<PackageParser2> apexParserSupplier) {
284         mContext = context;
285         mPm = pm;
286 
287         mInstallThread = new HandlerThread(TAG);
288         mInstallThread.start();
289 
290         mInstallHandler = new Handler(mInstallThread.getLooper());
291 
292         mCallbacks = new Callbacks(mInstallThread.getLooper());
293 
294         mSessionsFile = new AtomicFile(
295                 new File(Environment.getDataSystemDirectory(), "install_sessions.xml"),
296                 "package-session");
297         mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
298         mSessionsDir.mkdirs();
299 
300         mApexManager = ApexManager.getInstance();
301         mStagingManager = new StagingManager(context);
302         mSessionVerifier = new PackageSessionVerifier(context, mPm, mApexManager,
303                 apexParserSupplier, mInstallThread.getLooper());
304         mGentleUpdateHelper = new GentleUpdateHelper(
305                 context, mInstallThread.getLooper(), new AppStateHelper(context));
306 
307         LocalServices.getService(SystemServiceManager.class).startService(
308                 new Lifecycle(context, this));
309     }
310 
getStagingManager()311     StagingManager getStagingManager() {
312         return mStagingManager;
313     }
314 
okToSendBroadcasts()315     boolean okToSendBroadcasts()  {
316         return mOkToSendBroadcasts;
317     }
318 
systemReady()319     public void systemReady() {
320         mAppOps = mContext.getSystemService(AppOpsManager.class);
321         mStagingManager.systemReady();
322         mGentleUpdateHelper.systemReady();
323 
324         synchronized (mSessions) {
325             readSessionsLocked();
326             expireSessionsLocked();
327 
328             reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL);
329 
330             final ArraySet<File> unclaimedIcons = newArraySet(
331                     mSessionsDir.listFiles());
332 
333             // Ignore stages and icons claimed by active sessions
334             for (int i = 0; i < mSessions.size(); i++) {
335                 final PackageInstallerSession session = mSessions.valueAt(i);
336                 unclaimedIcons.remove(buildAppIconFile(session.sessionId));
337             }
338 
339             // Clean up orphaned icons
340             for (File icon : unclaimedIcons) {
341                 Slog.w(TAG, "Deleting orphan icon " + icon);
342                 icon.delete();
343             }
344 
345             // Invalid sessions might have been marked while parsing. Re-write the database with
346             // the updated information.
347             mSettingsWriteRequest.runNow();
348 
349         }
350     }
351 
onBroadcastReady()352     private void onBroadcastReady() {
353         // Broadcasts are not sent while we restore sessions on boot, since no processes would be
354         // ready to listen to them. From now on, it is safe to send broadcasts which otherwise will
355         // be rejected by ActivityManagerService if its systemReady() is not completed.
356         mOkToSendBroadcasts = true;
357     }
358 
restoreAndApplyStagedSessionIfNeeded()359     void restoreAndApplyStagedSessionIfNeeded() {
360         List<StagingManager.StagedSession> stagedSessionsToRestore = new ArrayList<>();
361         synchronized (mSessions) {
362             for (int i = 0; i < mSessions.size(); i++) {
363                 final PackageInstallerSession session = mSessions.valueAt(i);
364                 if (!session.isStaged()) {
365                     continue;
366                 }
367                 StagingManager.StagedSession stagedSession = session.mStagedSession;
368                 if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId()
369                         && getSession(stagedSession.getParentSessionId()) == null) {
370                     stagedSession.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED,
371                             "An orphan staged session " + stagedSession.sessionId() + " is found, "
372                                 + "parent " + stagedSession.getParentSessionId() + " is missing");
373                     continue;
374                 }
375                 if (!stagedSession.hasParentSessionId() && stagedSession.isCommitted()
376                         && !stagedSession.isInTerminalState()) {
377                     // StagingManager.restoreSessions expects a list of committed, non-finalized
378                     // parent staged sessions.
379                     stagedSessionsToRestore.add(stagedSession);
380                 }
381             }
382         }
383         // Don't hold mSessions lock when calling restoreSessions, since it might trigger an APK
384         // atomic install which needs to query sessions, which requires lock on mSessions.
385         // Note: restoreSessions mutates content of stagedSessionsToRestore.
386         mStagingManager.restoreSessions(stagedSessionsToRestore, mPm.isDeviceUpgrading());
387     }
388 
389     @GuardedBy("mSessions")
reconcileStagesLocked(String volumeUuid)390     private void reconcileStagesLocked(String volumeUuid) {
391         final ArraySet<File> unclaimedStages = getStagingDirsOnVolume(volumeUuid);
392         // Ignore stages claimed by active sessions
393         for (int i = 0; i < mSessions.size(); i++) {
394             final PackageInstallerSession session = mSessions.valueAt(i);
395             unclaimedStages.remove(session.stageDir);
396         }
397         removeStagingDirs(unclaimedStages);
398     }
399 
getStagingDirsOnVolume(String volumeUuid)400     private ArraySet<File> getStagingDirsOnVolume(String volumeUuid) {
401         final File stagingDir = getTmpSessionDir(volumeUuid);
402         final ArraySet<File> stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter));
403 
404         // We also need to clean up orphaned staging directory for staged sessions
405         final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
406         stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
407         return stagingDirs;
408     }
409 
removeStagingDirs(ArraySet<File> stagingDirsToRemove)410     private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) {
411         final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
412         // Clean up orphaned staging directories
413         for (File stage : stagingDirsToRemove) {
414             Slog.w(TAG, "Deleting orphan stage " + stage);
415             removePackageHelper.removeCodePath(stage);
416         }
417     }
418 
onPrivateVolumeMounted(String volumeUuid)419     public void onPrivateVolumeMounted(String volumeUuid) {
420         synchronized (mSessions) {
421             reconcileStagesLocked(volumeUuid);
422         }
423     }
424 
425     /**
426      * Called to free up some storage space from obsolete installation files
427      */
freeStageDirs(String volumeUuid)428     public void freeStageDirs(String volumeUuid) {
429         final ArraySet<File> unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid);
430         final long currentTimeMillis = System.currentTimeMillis();
431         synchronized (mSessions) {
432             for (int i = 0; i < mSessions.size(); i++) {
433                 final PackageInstallerSession session = mSessions.valueAt(i);
434                 if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) {
435                     // Only handles sessions stored on the target volume
436                     continue;
437                 }
438                 final long age = currentTimeMillis - session.createdMillis;
439                 if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) {
440                     // Aggressively close old sessions because we are running low on storage
441                     // Their staging dirs will be removed too
442                     PackageInstallerSession root = !session.hasParentSessionId()
443                             ? session : mSessions.get(session.getParentSessionId());
444                     if (root == null) {
445                         Slog.e(TAG, "freeStageDirs: found an orphaned session: "
446                                 + session.sessionId + " parent=" + session.getParentSessionId());
447                     } else if (!root.isDestroyed()) {
448                         root.abandon();
449                     }
450                 } else {
451                     // Session is new enough, so it deserves to be kept even on low storage
452                     unclaimedStagingDirsOnVolume.remove(session.stageDir);
453                 }
454             }
455         }
456         removeStagingDirs(unclaimedStagingDirsOnVolume);
457     }
458 
459     @Deprecated
allocateStageDirLegacy(String volumeUuid, boolean isEphemeral)460     public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
461         synchronized (mSessions) {
462             try {
463                 final int sessionId = allocateSessionIdLocked();
464                 mLegacySessions.put(sessionId, true);
465                 final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
466                 prepareStageDir(sessionStageDir);
467                 return sessionStageDir;
468             } catch (IllegalStateException e) {
469                 throw new IOException(e);
470             }
471         }
472     }
473 
474     @Deprecated
allocateExternalStageCidLegacy()475     public String allocateExternalStageCidLegacy() {
476         synchronized (mSessions) {
477             final int sessionId = allocateSessionIdLocked();
478             mLegacySessions.put(sessionId, true);
479             return "smdl" + sessionId + ".tmp";
480         }
481     }
482 
483     @GuardedBy("mSessions")
readSessionsLocked()484     private void readSessionsLocked() {
485         if (LOGD) Slog.v(TAG, "readSessionsLocked()");
486 
487         mSessions.clear();
488 
489         FileInputStream fis = null;
490         try {
491             fis = mSessionsFile.openRead();
492             final TypedXmlPullParser in = Xml.resolvePullParser(fis);
493 
494             int type;
495             while ((type = in.next()) != END_DOCUMENT) {
496                 if (type == START_TAG) {
497                     final String tag = in.getName();
498                     if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
499                         final PackageInstallerSession session;
500                         try {
501                             session = PackageInstallerSession.readFromXml(in, mInternalCallback,
502                                     mContext, mPm, mInstallThread.getLooper(), mStagingManager,
503                                     mSessionsDir, this, mSilentUpdatePolicy);
504                         } catch (Exception e) {
505                             Slog.e(TAG, "Could not read session", e);
506                             continue;
507                         }
508                         mSessions.put(session.sessionId, session);
509                         mAllocatedSessions.put(session.sessionId, true);
510                     }
511                 }
512             }
513         } catch (FileNotFoundException e) {
514             // Missing sessions are okay, probably first boot
515         } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) {
516             Slog.wtf(TAG, "Failed reading install sessions", e);
517         } finally {
518             IoUtils.closeQuietly(fis);
519         }
520         // After reboot housekeeping.
521         for (int i = 0; i < mSessions.size(); ++i) {
522             PackageInstallerSession session = mSessions.valueAt(i);
523             session.onAfterSessionRead(mSessions);
524         }
525     }
526 
527     @GuardedBy("mSessions")
expireSessionsLocked()528     private void expireSessionsLocked() {
529         SparseArray<PackageInstallerSession> tmp = mSessions.clone();
530         final int n = tmp.size();
531         for (int i = 0; i < n; ++i) {
532             PackageInstallerSession session = tmp.valueAt(i);
533             if (session.hasParentSessionId()) {
534                 // Child sessions will be expired when handling parent sessions
535                 continue;
536             }
537             final long age = System.currentTimeMillis() - session.createdMillis;
538             final long timeSinceUpdate = System.currentTimeMillis() - session.getUpdatedMillis();
539             final boolean valid;
540             if (session.isStaged()) {
541                 valid = !session.isStagedAndInTerminalState()
542                         || timeSinceUpdate < MAX_TIME_SINCE_UPDATE_MILLIS;
543             } else if (age >= MAX_AGE_MILLIS) {
544                 Slog.w(TAG, "Abandoning old session created at "
545                         + session.createdMillis);
546                 valid = false;
547             } else {
548                 valid = true;
549             }
550             if (!valid) {
551                 Slog.w(TAG, "Remove old session: " + session.sessionId);
552                 // Remove expired sessions as well as child sessions if any
553                 removeActiveSession(session);
554             }
555         }
556     }
557 
558     /**
559      * Moves a session (including the child sessions) from mSessions to mHistoricalSessions.
560      * This should only be called on a root session.
561      */
562     @GuardedBy("mSessions")
563     private void removeActiveSession(PackageInstallerSession session) {
564         mSessions.remove(session.sessionId);
565         addHistoricalSessionLocked(session);
566         for (PackageInstallerSession child : session.getChildSessions()) {
567             mSessions.remove(child.sessionId);
568             addHistoricalSessionLocked(child);
569         }
570     }
571 
572     @GuardedBy("mSessions")
573     private void addHistoricalSessionLocked(PackageInstallerSession session) {
574         CharArrayWriter writer = new CharArrayWriter();
575         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "    ");
576         session.dump(pw);
577         if (mHistoricalSessions.size() > HISTORICAL_SESSIONS_THRESHOLD) {
578             Slog.d(TAG, "Historical sessions size reaches threshold, clear the oldest");
579             mHistoricalSessions.subList(0, HISTORICAL_CLEAR_SIZE).clear();
580         }
581         mHistoricalSessions.add(writer.toString());
582 
583         int installerUid = session.getInstallerUid();
584         // Increment the number of sessions by this installerUid.
585         mHistoricalSessionsByInstaller.put(installerUid,
586                 mHistoricalSessionsByInstaller.get(installerUid) + 1);
587     }
588 
589     @GuardedBy("mSessions")
590     private boolean writeSessionsLocked() {
591         if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
592 
593         FileOutputStream fos = null;
594         try {
595             fos = mSessionsFile.startWrite();
596 
597             final TypedXmlSerializer out = Xml.resolveSerializer(fos);
598             out.startDocument(null, true);
599             out.startTag(null, TAG_SESSIONS);
600             final int size = mSessions.size();
601             for (int i = 0; i < size; i++) {
602                 final PackageInstallerSession session = mSessions.valueAt(i);
603                 session.write(out, mSessionsDir);
604             }
605             out.endTag(null, TAG_SESSIONS);
606             out.endDocument();
607 
608             mSessionsFile.finishWrite(fos);
609             return true;
610         } catch (IOException e) {
611             if (fos != null) {
612                 mSessionsFile.failWrite(fos);
613             }
614         }
615 
616         return false;
617     }
618 
619     private File buildAppIconFile(int sessionId) {
620         return new File(mSessionsDir, "app_icon." + sessionId + ".png");
621     }
622 
623     @Override
624     public int createSession(SessionParams params, String installerPackageName,
625             String callingAttributionTag, int userId) {
626         try {
627             return createSessionInternal(params, installerPackageName, callingAttributionTag,
628                     userId);
629         } catch (IOException e) {
630             throw ExceptionUtils.wrap(e);
631         }
632     }
633 
634     private int createSessionInternal(SessionParams params, String installerPackageName,
635             String installerAttributionTag, int userId)
636             throws IOException {
637         final int callingUid = Binder.getCallingUid();
638         final Computer snapshot = mPm.snapshotComputer();
639         snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
640 
641         if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
642             throw new SecurityException("User restriction prevents installing");
643         }
644 
645         if (params.dataLoaderParams != null
646                 && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
647                         != PackageManager.PERMISSION_GRANTED) {
648             throw new SecurityException("You need the "
649                     + "com.android.permission.USE_INSTALLER_V2 permission "
650                     + "to use a data loader");
651         }
652 
653         // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK
654         // capability; ensure if this is set as the install reason the app has one of the necessary
655         // signature permissions to perform the rollback.
656         if (params.installReason == PackageManager.INSTALL_REASON_ROLLBACK) {
657             if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS)
658                     != PackageManager.PERMISSION_GRANTED &&
659                     mContext.checkCallingOrSelfPermission(Manifest.permission.TEST_MANAGE_ROLLBACKS)
660                     != PackageManager.PERMISSION_GRANTED) {
661                 throw new SecurityException(
662                         "INSTALL_REASON_ROLLBACK requires the MANAGE_ROLLBACKS permission or the "
663                                 + "TEST_MANAGE_ROLLBACKS permission");
664             }
665         }
666 
667         // App package name and label length is restricted so that really long strings aren't
668         // written to disk.
669         if (params.appPackageName != null && !isValidPackageName(params.appPackageName)) {
670             params.appPackageName = null;
671         }
672 
673         params.appLabel = TextUtils.trimToSize(params.appLabel,
674                 PackageItemInfo.MAX_SAFE_LABEL_LENGTH);
675 
676         // Validate installer package name.
677         if (params.installerPackageName != null && !isValidPackageName(
678                 params.installerPackageName)) {
679             params.installerPackageName = null;
680         }
681 
682         var requestedInstallerPackageName =
683                 params.installerPackageName != null ? params.installerPackageName
684                         : installerPackageName;
685 
686         if (PackageManagerServiceUtils.isRootOrShell(callingUid)
687                 || PackageInstallerSession.isSystemDataLoaderInstallation(params)
688                 || PackageManagerServiceUtils.isAdoptedShell(callingUid, mContext)) {
689             params.installFlags |= PackageManager.INSTALL_FROM_ADB;
690             // adb installs can override the installingPackageName, but not the
691             // initiatingPackageName
692             installerPackageName = SHELL_PACKAGE_NAME;
693         } else {
694             if (callingUid != Process.SYSTEM_UID) {
695                 // The supplied installerPackageName must always belong to the calling app.
696                 mAppOps.checkPackage(callingUid, installerPackageName);
697             }
698             // Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
699             // caller.
700             if (!TextUtils.equals(requestedInstallerPackageName, installerPackageName)) {
701                 if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
702                         != PackageManager.PERMISSION_GRANTED) {
703                     mAppOps.checkPackage(callingUid, requestedInstallerPackageName);
704                 }
705             }
706 
707             params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
708             params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
709             params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
710             if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
711                     && !mPm.isCallerVerifier(snapshot, callingUid)) {
712                 params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
713             }
714             if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_TEST_ONLY_PACKAGE)
715                     != PackageManager.PERMISSION_GRANTED) {
716                 params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
717             }
718         }
719 
720         String originatingPackageName = null;
721         if (params.originatingUid != SessionParams.UID_UNKNOWN
722                 && params.originatingUid != callingUid) {
723             String[] packages = snapshot.getPackagesForUid(params.originatingUid);
724             if (packages != null && packages.length > 0) {
725                 // Choose an arbitrary representative package in the case of a shared UID.
726                 originatingPackageName = packages[0];
727             }
728         }
729 
730         if (Build.IS_DEBUGGABLE || PackageManagerServiceUtils.isSystemOrRoot(callingUid)) {
731             params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
732         } else {
733             params.installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
734         }
735 
736         if (mDisableVerificationForUid != INVALID_UID) {
737             if (callingUid == mDisableVerificationForUid) {
738                 params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
739             } else {
740                 // Clear the flag if current calling uid doesn't match the requested uid.
741                 params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
742             }
743             // Reset the field as this is a one-off request.
744             mDisableVerificationForUid = INVALID_UID;
745         } else if ((params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) {
746             // Only tools under specific conditions (test app installed through ADB, and
747             // verification disabled flag specified) can disable verification.
748             params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
749         }
750 
751         boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
752         if (isApex) {
753             if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGE_UPDATES)
754                     == PackageManager.PERMISSION_DENIED
755                     && mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
756                     == PackageManager.PERMISSION_DENIED) {
757                 throw new SecurityException("Not allowed to perform APEX updates");
758             }
759         } else if (params.isStaged) {
760             mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG);
761         }
762 
763         if (isApex) {
764             if (!mApexManager.isApexSupported()) {
765                 throw new IllegalArgumentException(
766                     "This device doesn't support the installation of APEX files");
767             }
768             if (params.isMultiPackage) {
769                 throw new IllegalArgumentException("A multi-session can't be set as APEX.");
770             }
771             if (PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)
772                     || mBypassNextAllowedApexUpdateCheck) {
773                 params.installFlags |= PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
774             } else {
775                 // Only specific APEX updates (installed through ADB, or for CTS tests) can disable
776                 // allowed APEX update check.
777                 params.installFlags &= ~PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
778             }
779         }
780 
781         if ((params.installFlags & PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK) != 0
782                 && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)
783                 && !Build.IS_DEBUGGABLE) {
784             // If the bypass flag is set, but not running as system root or shell then remove
785             // the flag
786             params.installFlags &= ~PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
787         }
788 
789         if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
790                 && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)
791                 && (snapshot.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM)
792                 == 0) {
793             throw new SecurityException(
794                     "Only system apps could use the PackageManager.INSTALL_INSTANT_APP flag.");
795         }
796 
797         if (params.isStaged && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) {
798             if (!mBypassNextStagedInstallerCheck
799                     && !isStagedInstallerAllowed(requestedInstallerPackageName)) {
800                 throw new SecurityException("Installer not allowed to commit staged install");
801             }
802         }
803         if (isApex && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) {
804             if (!mBypassNextStagedInstallerCheck
805                     && !isStagedInstallerAllowed(requestedInstallerPackageName)) {
806                 throw new SecurityException(
807                         "Installer not allowed to commit non-staged APEX install");
808             }
809         }
810 
811         mBypassNextStagedInstallerCheck = false;
812         mBypassNextAllowedApexUpdateCheck = false;
813 
814         if (!params.isMultiPackage) {
815             var hasInstallGrantRuntimePermissions = mContext.checkCallingOrSelfPermission(
816                     Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS)
817                     == PackageManager.PERMISSION_GRANTED;
818 
819             // Only system components can circumvent runtime permissions when installing.
820             if ((params.installFlags & PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS) != 0
821                     && !hasInstallGrantRuntimePermissions) {
822                 throw new SecurityException("You need the "
823                         + Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
824                         + " permission to use the"
825                         + " PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS flag");
826             }
827 
828             var permissionStates = params.getPermissionStates();
829             if (!permissionStates.isEmpty()) {
830                 if (!hasInstallGrantRuntimePermissions) {
831                     for (int index = 0; index < permissionStates.size(); index++) {
832                         var permissionName = permissionStates.keyAt(index);
833                         if (!INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS.contains(permissionName)) {
834                             throw new SecurityException("You need the "
835                                     + Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
836                                     + " permission to grant runtime permissions for a session");
837                         }
838                     }
839                 }
840             }
841 
842             // Defensively resize giant app icons
843             if (params.appIcon != null) {
844                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
845                         Context.ACTIVITY_SERVICE);
846                 final int iconSize = am.getLauncherLargeIconSize();
847                 if ((params.appIcon.getWidth() > iconSize * 2)
848                         || (params.appIcon.getHeight() > iconSize * 2)) {
849                     params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
850                             true);
851                 }
852             }
853 
854             switch (params.mode) {
855                 case SessionParams.MODE_FULL_INSTALL:
856                 case SessionParams.MODE_INHERIT_EXISTING:
857                     break;
858                 default:
859                     throw new IllegalArgumentException("Invalid install mode: " + params.mode);
860             }
861 
862             // If caller requested explicit location, validity check it, otherwise
863             // resolve the best internal or adopted location.
864             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
865                 if (!InstallLocationUtils.fitsOnInternal(mContext, params)) {
866                     throw new IOException("No suitable internal storage available");
867                 }
868             } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
869                 // For now, installs to adopted media are treated as internal from
870                 // an install flag point-of-view.
871                 params.installFlags |= PackageManager.INSTALL_INTERNAL;
872             } else {
873                 params.installFlags |= PackageManager.INSTALL_INTERNAL;
874 
875                 // Resolve best location for install, based on combination of
876                 // requested install flags, delta size, and manifest settings.
877                 final long ident = Binder.clearCallingIdentity();
878                 try {
879                     params.volumeUuid = InstallLocationUtils.resolveInstallVolume(mContext, params);
880                 } finally {
881                     Binder.restoreCallingIdentity(ident);
882                 }
883             }
884         }
885 
886         final int sessionId;
887         final PackageInstallerSession session;
888         synchronized (mSessions) {
889             // Check that the installer does not have too many active sessions.
890             final int activeCount = getSessionCount(mSessions, callingUid);
891             if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
892                     == PackageManager.PERMISSION_GRANTED) {
893                 if (activeCount >= MAX_ACTIVE_SESSIONS_WITH_PERMISSION) {
894                     throw new IllegalStateException(
895                             "Too many active sessions for UID " + callingUid);
896                 }
897             } else if (activeCount >= MAX_ACTIVE_SESSIONS_NO_PERMISSION) {
898                 throw new IllegalStateException(
899                         "Too many active sessions for UID " + callingUid);
900             }
901             final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
902             if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
903                 throw new IllegalStateException(
904                         "Too many historical sessions for UID " + callingUid);
905             }
906 
907             sessionId = allocateSessionIdLocked();
908         }
909 
910         final long createdMillis = System.currentTimeMillis();
911         // We're staging to exactly one location
912         File stageDir = null;
913         String stageCid = null;
914         if (!params.isMultiPackage) {
915             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
916                 stageDir = buildSessionDir(sessionId, params);
917             } else {
918                 stageCid = buildExternalStageCid(sessionId);
919             }
920         }
921 
922         // reset the force queryable param if it's not called by an approved caller.
923         if (params.forceQueryableOverride) {
924             if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
925                 params.forceQueryableOverride = false;
926             }
927         }
928         int requestedInstallerPackageUid = INVALID_UID;
929         if (requestedInstallerPackageName != null) {
930             requestedInstallerPackageUid = snapshot.getPackageUid(requestedInstallerPackageName,
931                     0 /* flags */, userId);
932         }
933         if (requestedInstallerPackageUid == INVALID_UID) {
934             // Requested installer package is invalid, reset it
935             requestedInstallerPackageName = null;
936         }
937 
938         final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
939         if (dpmi != null && dpmi.isUserOrganizationManaged(userId)) {
940             params.installFlags |= PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE;
941         }
942 
943         if (isApex || mContext.checkCallingOrSelfPermission(
944                 Manifest.permission.ENFORCE_UPDATE_OWNERSHIP) == PackageManager.PERMISSION_DENIED) {
945             params.installFlags &= ~PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP;
946         }
947 
948         InstallSource installSource = InstallSource.create(installerPackageName,
949                 originatingPackageName, requestedInstallerPackageName, requestedInstallerPackageUid,
950                 requestedInstallerPackageName, installerAttributionTag, params.packageSource);
951         session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
952                 mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
953                 userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
954                 null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
955                 false, false, false, PackageManager.INSTALL_UNKNOWN, "");
956 
957         synchronized (mSessions) {
958             mSessions.put(sessionId, session);
959         }
960         mPm.addInstallerPackageName(session.getInstallSource());
961 
962         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
963 
964         mSettingsWriteRequest.schedule();
965         if (LOGD) {
966             Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged);
967         }
968         return sessionId;
969     }
970 
971     private boolean isStagedInstallerAllowed(String installerName) {
972         return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName);
973     }
974 
975     @Override
976     public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
977         synchronized (mSessions) {
978             final PackageInstallerSession session = mSessions.get(sessionId);
979             if (session == null || !isCallingUidOwner(session)) {
980                 throw new SecurityException("Caller has no access to session " + sessionId);
981             }
982 
983             // Defensively resize giant app icons
984             if (appIcon != null) {
985                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
986                         Context.ACTIVITY_SERVICE);
987                 final int iconSize = am.getLauncherLargeIconSize();
988                 if ((appIcon.getWidth() > iconSize * 2)
989                         || (appIcon.getHeight() > iconSize * 2)) {
990                     appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
991                 }
992             }
993 
994             session.params.appIcon = appIcon;
995             session.params.appIconLastModified = -1;
996 
997             mInternalCallback.onSessionBadgingChanged(session);
998         }
999     }
1000 
1001     @Override
1002     public void updateSessionAppLabel(int sessionId, String appLabel) {
1003         synchronized (mSessions) {
1004             final PackageInstallerSession session = mSessions.get(sessionId);
1005             if (session == null || !isCallingUidOwner(session)) {
1006                 throw new SecurityException("Caller has no access to session " + sessionId);
1007             }
1008             if (!appLabel.equals(session.params.appLabel)) {
1009                 session.params.appLabel = appLabel;
1010                 mInternalCallback.onSessionBadgingChanged(session);
1011             }
1012         }
1013     }
1014 
1015     @Override
1016     public void abandonSession(int sessionId) {
1017         synchronized (mSessions) {
1018             final PackageInstallerSession session = mSessions.get(sessionId);
1019             if (session == null || !isCallingUidOwner(session)) {
1020                 throw new SecurityException("Caller has no access to session " + sessionId);
1021             }
1022             session.abandon();
1023         }
1024     }
1025 
1026     @Override
1027     public IPackageInstallerSession openSession(int sessionId) {
1028         try {
1029             return openSessionInternal(sessionId);
1030         } catch (IOException e) {
1031             throw ExceptionUtils.wrap(e);
1032         }
1033     }
1034 
1035     private boolean checkOpenSessionAccess(final PackageInstallerSession session) {
1036         if (session == null) {
1037             return false;
1038         }
1039         if (isCallingUidOwner(session)) {
1040             return true;
1041         }
1042         // Package verifiers have access to openSession for sealed sessions.
1043         if (session.isSealed() && mContext.checkCallingOrSelfPermission(
1044                 android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)
1045                 == PackageManager.PERMISSION_GRANTED) {
1046             return true;
1047         }
1048         return false;
1049     }
1050 
1051     private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
1052         synchronized (mSessions) {
1053             final PackageInstallerSession session = mSessions.get(sessionId);
1054             if (!checkOpenSessionAccess(session)) {
1055                 throw new SecurityException("Caller has no access to session " + sessionId);
1056             }
1057             session.open();
1058             return session;
1059         }
1060     }
1061 
1062     @GuardedBy("mSessions")
1063     private int allocateSessionIdLocked() {
1064         int n = 0;
1065         int sessionId;
1066         do {
1067             sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1068             if (!mAllocatedSessions.get(sessionId, false)) {
1069                 mAllocatedSessions.put(sessionId, true);
1070                 return sessionId;
1071             }
1072         } while (n++ < 32);
1073 
1074         throw new IllegalStateException("Failed to allocate session ID");
1075     }
1076 
1077     static boolean isStageName(String name) {
1078         final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
1079         final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
1080         final boolean isLegacyContainer = name.startsWith("smdl2tmp");
1081         return isFile || isContainer || isLegacyContainer;
1082     }
1083 
1084     static int tryParseSessionId(@NonNull String tmpSessionDir)
1085             throws IllegalArgumentException {
1086         if (!tmpSessionDir.startsWith("vmdl") || !tmpSessionDir.endsWith(".tmp")) {
1087             throw new IllegalArgumentException("Not a temporary session directory");
1088         }
1089         String sessionId = tmpSessionDir.substring("vmdl".length(),
1090                 tmpSessionDir.length() - ".tmp".length());
1091         return Integer.parseInt(sessionId);
1092     }
1093 
1094     private static boolean isValidPackageName(@NonNull String packageName) {
1095         if (packageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) {
1096             return false;
1097         }
1098         // "android" is a valid package name
1099         var errorMessage = FrameworkParsingPackageUtils.validateName(
1100                 packageName, /* requireSeparator= */ false, /* requireFilename */ true);
1101         if (errorMessage != null) {
1102             return false;
1103         }
1104         return true;
1105     }
1106 
1107     private File getTmpSessionDir(String volumeUuid) {
1108         return Environment.getDataAppDirectory(volumeUuid);
1109     }
1110 
1111     private File buildTmpSessionDir(int sessionId, String volumeUuid) {
1112         final File sessionStagingDir = getTmpSessionDir(volumeUuid);
1113         return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp");
1114     }
1115 
1116     private File buildSessionDir(int sessionId, SessionParams params) {
1117         if (params.isStaged || (params.installFlags & PackageManager.INSTALL_APEX) != 0) {
1118             final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
1119             return new File(sessionStagingDir, "session_" + sessionId);
1120         }
1121         final File result = buildTmpSessionDir(sessionId, params.volumeUuid);
1122         if (DEBUG && !Objects.equals(tryParseSessionId(result.getName()), sessionId)) {
1123             throw new RuntimeException(
1124                     "session folder format is off: " + result.getName() + " (" + sessionId + ")");
1125         }
1126         return result;
1127     }
1128 
1129     static void prepareStageDir(File stageDir) throws IOException {
1130         if (stageDir.exists()) {
1131             throw new IOException("Session dir already exists: " + stageDir);
1132         }
1133 
1134         try {
1135             Os.mkdir(stageDir.getAbsolutePath(), 0775);
1136             Os.chmod(stageDir.getAbsolutePath(), 0775);
1137         } catch (ErrnoException e) {
1138             // This purposefully throws if directory already exists
1139             throw new IOException("Failed to prepare session dir: " + stageDir, e);
1140         }
1141 
1142         if (!SELinux.restorecon(stageDir)) {
1143             String path = stageDir.getCanonicalPath();
1144             String ctx = SELinux.fileSelabelLookup(path);
1145             boolean success = SELinux.setFileContext(path, ctx);
1146             Slog.e(TAG,
1147                     "Failed to SELinux.restorecon session dir, path: [" + path + "], ctx: [" + ctx
1148                             + "]. Retrying via SELinux.fileSelabelLookup/SELinux.setFileContext: "
1149                             + (success ? "SUCCESS" : "FAILURE"));
1150             if (!success) {
1151                 throw new IOException("Failed to restorecon session dir: " + stageDir);
1152             }
1153         }
1154     }
1155 
1156     private String buildExternalStageCid(int sessionId) {
1157         return "smdl" + sessionId + ".tmp";
1158     }
1159 
1160     private boolean shouldFilterSession(@NonNull Computer snapshot, int uid, SessionInfo info) {
1161         if (info == null) {
1162             return false;
1163         }
1164         return uid != info.getInstallerUid()
1165                 && !snapshot.canQueryPackage(uid, info.getAppPackageName());
1166     }
1167 
1168     @Override
1169     public SessionInfo getSessionInfo(int sessionId) {
1170         final int callingUid = Binder.getCallingUid();
1171         final SessionInfo result;
1172         synchronized (mSessions) {
1173             final PackageInstallerSession session = mSessions.get(sessionId);
1174             result = (session != null && !(session.isStaged() && session.isDestroyed()))
1175                     ? session.generateInfoForCaller(true /* includeIcon */, callingUid)
1176                     : null;
1177         }
1178         return shouldFilterSession(mPm.snapshotComputer(), callingUid, result) ? null : result;
1179     }
1180 
1181     @Override
1182     public ParceledListSlice<SessionInfo> getStagedSessions() {
1183         final int callingUid = Binder.getCallingUid();
1184         final List<SessionInfo> result = new ArrayList<>();
1185         synchronized (mSessions) {
1186             for (int i = 0; i < mSessions.size(); i++) {
1187                 final PackageInstallerSession session = mSessions.valueAt(i);
1188                 if (session.isStaged() && !session.isDestroyed()) {
1189                     result.add(session.generateInfoForCaller(false /* includeIcon */, callingUid));
1190                 }
1191             }
1192         }
1193         final Computer snapshot = mPm.snapshotComputer();
1194         result.removeIf(info -> shouldFilterSession(snapshot, callingUid, info));
1195         return new ParceledListSlice<>(result);
1196     }
1197 
1198     @Override
1199     public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
1200         final int callingUid = Binder.getCallingUid();
1201         final Computer snapshot = mPm.snapshotComputer();
1202         snapshot.enforceCrossUserPermission(callingUid, userId, true, false, "getAllSessions");
1203 
1204         final List<SessionInfo> result = new ArrayList<>();
1205         synchronized (mSessions) {
1206             for (int i = 0; i < mSessions.size(); i++) {
1207                 final PackageInstallerSession session = mSessions.valueAt(i);
1208                 if (session.userId == userId && !session.hasParentSessionId()
1209                         && !(session.isStaged() && session.isDestroyed())) {
1210                     result.add(session.generateInfoForCaller(false /* includeIcon */, callingUid));
1211                 }
1212             }
1213         }
1214         result.removeIf(info -> shouldFilterSession(snapshot, callingUid, info));
1215         return new ParceledListSlice<>(result);
1216     }
1217 
1218     @Override
1219     public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
1220         final Computer snapshot = mPm.snapshotComputer();
1221         final int callingUid = Binder.getCallingUid();
1222         snapshot.enforceCrossUserPermission(callingUid, userId, true, false, "getMySessions");
1223         mAppOps.checkPackage(callingUid, installerPackageName);
1224 
1225         final List<SessionInfo> result = new ArrayList<>();
1226         synchronized (mSessions) {
1227             for (int i = 0; i < mSessions.size(); i++) {
1228                 final PackageInstallerSession session = mSessions.valueAt(i);
1229 
1230                 SessionInfo info =
1231                         session.generateInfoForCaller(false /*withIcon*/, Process.SYSTEM_UID);
1232                 if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
1233                         && session.userId == userId && !session.hasParentSessionId()
1234                         && isCallingUidOwner(session)) {
1235                     result.add(info);
1236                 }
1237             }
1238         }
1239         return new ParceledListSlice<>(result);
1240     }
1241 
1242     @Override
1243     public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
1244                 IntentSender statusReceiver, int userId) {
1245         final Computer snapshot = mPm.snapshotComputer();
1246         final int callingUid = Binder.getCallingUid();
1247         snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
1248         if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
1249             mAppOps.checkPackage(callingUid, callerPackageName);
1250         }
1251 
1252         // Check whether the caller is device owner or affiliated profile owner, in which case we do
1253         // it silently.
1254         DevicePolicyManagerInternal dpmi =
1255                 LocalServices.getService(DevicePolicyManagerInternal.class);
1256         final boolean canSilentlyInstallPackage =
1257                 dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
1258 
1259         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
1260                 statusReceiver, versionedPackage.getPackageName(),
1261                 canSilentlyInstallPackage, userId);
1262         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
1263                     == PackageManager.PERMISSION_GRANTED) {
1264             // Sweet, call straight through!
1265             mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
1266         } else if (canSilentlyInstallPackage) {
1267             // Allow the device owner and affiliated profile owner to silently delete packages
1268             // Need to clear the calling identity to get DELETE_PACKAGES permission
1269             final long ident = Binder.clearCallingIdentity();
1270             try {
1271                 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
1272             } finally {
1273                 Binder.restoreCallingIdentity(ident);
1274             }
1275             DevicePolicyEventLogger
1276                     .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE)
1277                     .setAdmin(callerPackageName)
1278                     .write();
1279         } else {
1280             ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId);
1281             if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
1282                 mContext.enforceCallingOrSelfPermission(Manifest.permission.REQUEST_DELETE_PACKAGES,
1283                         null);
1284             }
1285 
1286             // Take a short detour to confirm with user
1287             final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
1288             intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
1289             intent.putExtra(PackageInstaller.EXTRA_CALLBACK,
1290                     new PackageManager.UninstallCompleteCallback(adapter.getBinder().asBinder()));
1291             adapter.onUserActionRequired(intent);
1292         }
1293     }
1294 
1295     @Override
1296     public void uninstallExistingPackage(VersionedPackage versionedPackage,
1297             String callerPackageName, IntentSender statusReceiver, int userId) {
1298         final int callingUid = Binder.getCallingUid();
1299         mContext.enforceCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES, null);
1300         final Computer snapshot = mPm.snapshotComputer();
1301         snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
1302         if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
1303             mAppOps.checkPackage(callingUid, callerPackageName);
1304         }
1305 
1306         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
1307                 statusReceiver, versionedPackage.getPackageName(), false, userId);
1308         mPm.deleteExistingPackageAsUser(versionedPackage, adapter.getBinder(), userId);
1309     }
1310 
1311     @Override
1312     public void installExistingPackage(String packageName, int installFlags, int installReason,
1313             IntentSender statusReceiver, int userId, List<String> allowListedPermissions) {
1314         final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
1315 
1316         var result = installPackageHelper.installExistingPackageAsUser(packageName, userId,
1317                 installFlags, installReason, allowListedPermissions, statusReceiver);
1318 
1319         int returnCode = result.first;
1320         IntentSender onCompleteSender = result.second;
1321         if (onCompleteSender != null) {
1322             InstallPackageHelper.onInstallComplete(returnCode, mContext, onCompleteSender);
1323         }
1324     }
1325 
1326     @Override
1327     public void setPermissionsResult(int sessionId, boolean accepted) {
1328         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
1329 
1330         synchronized (mSessions) {
1331             PackageInstallerSession session = mSessions.get(sessionId);
1332             if (session != null) {
1333                 session.setPermissionsResult(accepted);
1334             }
1335         }
1336     }
1337 
1338     private boolean isValidForInstallConstraints(PackageStateInternal ps,
1339             String installerPackageName) {
1340         return TextUtils.equals(ps.getInstallSource().mInstallerPackageName, installerPackageName)
1341                 || TextUtils.equals(ps.getInstallSource().mUpdateOwnerPackageName,
1342                 installerPackageName);
1343     }
1344 
1345     private CompletableFuture<InstallConstraintsResult> checkInstallConstraintsInternal(
1346             String installerPackageName, List<String> packageNames,
1347             InstallConstraints constraints, long timeoutMillis) {
1348         Objects.requireNonNull(packageNames);
1349         Objects.requireNonNull(constraints);
1350 
1351         final var snapshot = mPm.snapshotComputer();
1352         final int callingUid = Binder.getCallingUid();
1353         final var callingPackageName = snapshot.getNameForUid(callingUid);
1354         if (!TextUtils.equals(callingPackageName, installerPackageName)) {
1355             throw new SecurityException("The installerPackageName set by the caller doesn't match "
1356                     + "the caller's own package name.");
1357         }
1358         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) {
1359             for (var packageName : packageNames) {
1360                 var ps = snapshot.getPackageStateInternal(packageName);
1361                 if (ps == null || !isValidForInstallConstraints(ps, installerPackageName)) {
1362                     throw new SecurityException("Caller has no access to package " + packageName);
1363                 }
1364             }
1365         }
1366 
1367         return mGentleUpdateHelper.checkInstallConstraints(
1368                 packageNames, constraints, timeoutMillis);
1369     }
1370 
1371     @Override
1372     public void checkInstallConstraints(String installerPackageName, List<String> packageNames,
1373             InstallConstraints constraints, RemoteCallback callback) {
1374         Objects.requireNonNull(callback);
1375         var future = checkInstallConstraintsInternal(
1376                 installerPackageName, packageNames, constraints, /*timeoutMillis=*/0);
1377         future.thenAccept(result -> {
1378             var b = new Bundle();
1379             b.putParcelable("result", result);
1380             callback.sendResult(b);
1381         });
1382     }
1383 
1384     @Override
1385     public void waitForInstallConstraints(String installerPackageName, List<String> packageNames,
1386             InstallConstraints constraints, IntentSender callback, long timeoutMillis) {
1387         Objects.requireNonNull(callback);
1388         if (timeoutMillis < 0 || timeoutMillis > MAX_INSTALL_CONSTRAINTS_TIMEOUT_MILLIS) {
1389             throw new IllegalArgumentException("Invalid timeoutMillis=" + timeoutMillis);
1390         }
1391         var future = checkInstallConstraintsInternal(
1392                 installerPackageName, packageNames, constraints, timeoutMillis);
1393         future.thenAccept(result -> {
1394             final var intent = new Intent();
1395             intent.putExtra(Intent.EXTRA_PACKAGES, packageNames.toArray(new String[0]));
1396             intent.putExtra(PackageInstaller.EXTRA_INSTALL_CONSTRAINTS, constraints);
1397             intent.putExtra(PackageInstaller.EXTRA_INSTALL_CONSTRAINTS_RESULT, result);
1398             try {
1399                 final BroadcastOptions options = BroadcastOptions.makeBasic();
1400                 options.setPendingIntentBackgroundActivityLaunchAllowed(false);
1401                 callback.sendIntent(mContext, 0, intent, null /* onFinished*/,
1402                         null /* handler */, null /* requiredPermission */, options.toBundle());
1403             } catch (SendIntentException ignore) {
1404             }
1405         });
1406     }
1407 
1408     @Override
1409     public void registerCallback(IPackageInstallerCallback callback, int userId) {
1410         final Computer snapshot = mPm.snapshotComputer();
1411         snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
1412                 "registerCallback");
1413         registerCallback(callback, eventUserId -> userId == eventUserId);
1414     }
1415 
1416     /**
1417      * Assume permissions already checked and caller's identity cleared
1418      */
1419     public void registerCallback(IPackageInstallerCallback callback, IntPredicate userCheck) {
1420         mCallbacks.register(callback, new BroadcastCookie(Binder.getCallingUid(), userCheck));
1421     }
1422 
1423     @Override
1424     public void unregisterCallback(IPackageInstallerCallback callback) {
1425         mCallbacks.unregister(callback);
1426     }
1427 
1428     @Override
1429     public PackageInstallerSession getSession(int sessionId) {
1430         synchronized (mSessions) {
1431             return mSessions.get(sessionId);
1432         }
1433     }
1434 
1435     @Override
1436     public PackageSessionVerifier getSessionVerifier() {
1437         return mSessionVerifier;
1438     }
1439 
1440     @Override
1441     public GentleUpdateHelper getGentleUpdateHelper() {
1442         return mGentleUpdateHelper;
1443     }
1444 
1445     @Override
1446     public void bypassNextStagedInstallerCheck(boolean value) {
1447         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) {
1448             throw new SecurityException("Caller not allowed to bypass staged installer check");
1449         }
1450         mBypassNextStagedInstallerCheck = value;
1451     }
1452 
1453     @Override
1454     public void bypassNextAllowedApexUpdateCheck(boolean value) {
1455         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) {
1456             throw new SecurityException("Caller not allowed to bypass allowed apex update check");
1457         }
1458         mBypassNextAllowedApexUpdateCheck = value;
1459     }
1460 
1461     @Override
1462     public void disableVerificationForUid(int uid) {
1463         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) {
1464             throw new SecurityException("Operation not allowed for caller");
1465         }
1466         mDisableVerificationForUid = uid;
1467     }
1468 
1469     /**
1470      * Set an installer to allow for the unlimited silent updates.
1471      */
1472     @Override
1473     public void setAllowUnlimitedSilentUpdates(@Nullable String installerPackageName) {
1474         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) {
1475             throw new SecurityException("Caller not allowed to unlimite silent updates");
1476         }
1477         mSilentUpdatePolicy.setAllowUnlimitedSilentUpdates(installerPackageName);
1478     }
1479 
1480     /**
1481      * Set the silent updates throttle time in seconds.
1482      */
1483     @Override
1484     public void setSilentUpdatesThrottleTime(long throttleTimeInSeconds) {
1485         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) {
1486             throw new SecurityException("Caller not allowed to set silent updates throttle time");
1487         }
1488         mSilentUpdatePolicy.setSilentUpdatesThrottleTime(throttleTimeInSeconds);
1489     }
1490 
1491     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
1492             int installerUid) {
1493         int count = 0;
1494         final int size = sessions.size();
1495         for (int i = 0; i < size; i++) {
1496             final PackageInstallerSession session = sessions.valueAt(i);
1497             if (session.getInstallerUid() == installerUid) {
1498                 count++;
1499             }
1500         }
1501         return count;
1502     }
1503 
1504     private boolean isCallingUidOwner(PackageInstallerSession session) {
1505         final int callingUid = Binder.getCallingUid();
1506         if (callingUid == Process.ROOT_UID) {
1507             return true;
1508         } else {
1509             return (session != null) && (callingUid == session.getInstallerUid());
1510         }
1511     }
1512 
1513     private boolean shouldFilterSession(@NonNull Computer snapshot, int uid, int sessionId) {
1514         final PackageInstallerSession session = getSession(sessionId);
1515         if (session == null) {
1516             return false;
1517         }
1518         return uid != session.getInstallerUid()
1519                 && !snapshot.canQueryPackage(uid, session.getPackageName());
1520     }
1521 
1522     static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
1523         private final Context mContext;
1524         private final IntentSender mTarget;
1525         private final String mPackageName;
1526         private final Notification mNotification;
1527 
1528         public PackageDeleteObserverAdapter(Context context, IntentSender target,
1529                 String packageName, boolean showNotification, int userId) {
1530             mContext = context;
1531             mTarget = target;
1532             mPackageName = packageName;
1533             if (showNotification) {
1534                 mNotification = buildSuccessNotification(mContext,
1535                         getDeviceOwnerDeletedPackageMsg(),
1536                         packageName,
1537                         userId);
1538             } else {
1539                 mNotification = null;
1540             }
1541         }
1542 
1543         private String getDeviceOwnerDeletedPackageMsg() {
1544             final long ident = Binder.clearCallingIdentity();
1545             try {
1546                 DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
1547                 return dpm.getResources().getString(PACKAGE_DELETED_BY_DO,
1548                         () -> mContext.getString(R.string.package_deleted_device_owner));
1549             } finally {
1550                 Binder.restoreCallingIdentity(ident);
1551             }
1552         }
1553 
1554         @Override
1555         public void onUserActionRequired(Intent intent) {
1556             if (mTarget == null) {
1557                 return;
1558             }
1559             final Intent fillIn = new Intent();
1560             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
1561             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1562                     PackageInstaller.STATUS_PENDING_USER_ACTION);
1563             fillIn.putExtra(Intent.EXTRA_INTENT, intent);
1564             try {
1565                 final BroadcastOptions options = BroadcastOptions.makeBasic();
1566                 options.setPendingIntentBackgroundActivityLaunchAllowed(false);
1567                 mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/,
1568                         null /* handler */, null /* requiredPermission */, options.toBundle());
1569             } catch (SendIntentException ignored) {
1570             }
1571         }
1572 
1573         @Override
1574         public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
1575             if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
1576                 NotificationManager notificationManager = (NotificationManager)
1577                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
1578                 notificationManager.notify(basePackageName,
1579                         SystemMessage.NOTE_PACKAGE_STATE,
1580                         mNotification);
1581             }
1582             if (mTarget == null) {
1583                 return;
1584             }
1585             final Intent fillIn = new Intent();
1586             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
1587             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1588                     PackageManager.deleteStatusToPublicStatus(returnCode));
1589             fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
1590                     PackageManager.deleteStatusToString(returnCode, msg));
1591             fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
1592             try {
1593                 final BroadcastOptions options = BroadcastOptions.makeBasic();
1594                 options.setPendingIntentBackgroundActivityLaunchAllowed(false);
1595                 mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/,
1596                         null /* handler */, null /* requiredPermission */, options.toBundle());
1597             } catch (SendIntentException ignored) {
1598             }
1599         }
1600     }
1601 
1602     /**
1603      * Build a notification for package installation / deletion by device owners that is shown if
1604      * the operation succeeds.
1605      */
1606     static Notification buildSuccessNotification(Context context, String contentText,
1607             String basePackageName, int userId) {
1608         PackageInfo packageInfo = null;
1609         try {
1610             packageInfo = AppGlobals.getPackageManager().getPackageInfo(
1611                     basePackageName, PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, userId);
1612         } catch (RemoteException ignored) {
1613         }
1614         if (packageInfo == null || packageInfo.applicationInfo == null) {
1615             Slog.w(TAG, "Notification not built for package: " + basePackageName);
1616             return null;
1617         }
1618         PackageManager pm = context.getPackageManager();
1619         Bitmap packageIcon = ImageUtils.buildScaledBitmap(
1620                 packageInfo.applicationInfo.loadIcon(pm),
1621                 context.getResources().getDimensionPixelSize(
1622                         android.R.dimen.notification_large_icon_width),
1623                 context.getResources().getDimensionPixelSize(
1624                         android.R.dimen.notification_large_icon_height));
1625         CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
1626         return new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
1627                 .setSmallIcon(R.drawable.ic_check_circle_24px)
1628                 .setColor(context.getResources().getColor(
1629                         R.color.system_notification_accent_color))
1630                 .setContentTitle(packageLabel)
1631                 .setContentText(contentText)
1632                 .setStyle(new Notification.BigTextStyle().bigText(contentText))
1633                 .setLargeIcon(packageIcon)
1634                 .build();
1635     }
1636 
1637     public static <E> ArraySet<E> newArraySet(E... elements) {
1638         final ArraySet<E> set = new ArraySet<E>();
1639         if (elements != null) {
1640             set.ensureCapacity(elements.length);
1641             Collections.addAll(set, elements);
1642         }
1643         return set;
1644     }
1645 
1646     private static final class BroadcastCookie {
1647         public final int callingUid;
1648         public final IntPredicate userCheck;
1649 
1650         BroadcastCookie(int callingUid, IntPredicate userCheck) {
1651             this.callingUid = callingUid;
1652             this.userCheck = userCheck;
1653         }
1654     }
1655 
1656     private class Callbacks extends Handler {
1657         private static final int MSG_SESSION_CREATED = 1;
1658         private static final int MSG_SESSION_BADGING_CHANGED = 2;
1659         private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
1660         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
1661         private static final int MSG_SESSION_FINISHED = 5;
1662 
1663         private final RemoteCallbackList<IPackageInstallerCallback>
1664                 mCallbacks = new RemoteCallbackList<>();
1665 
1666         public Callbacks(Looper looper) {
1667             super(looper);
1668         }
1669 
1670         public void register(IPackageInstallerCallback callback, BroadcastCookie cookie) {
1671             mCallbacks.register(callback, cookie);
1672         }
1673 
1674         public void unregister(IPackageInstallerCallback callback) {
1675             mCallbacks.unregister(callback);
1676         }
1677 
1678         @Override
1679         public void handleMessage(Message msg) {
1680             final int sessionId = msg.arg1;
1681             final int userId = msg.arg2;
1682             final int n = mCallbacks.beginBroadcast();
1683             final Computer snapshot = mPm.snapshotComputer();
1684             for (int i = 0; i < n; i++) {
1685                 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
1686                 final BroadcastCookie cookie = (BroadcastCookie) mCallbacks.getBroadcastCookie(i);
1687                 if (cookie.userCheck.test(userId)
1688                         && !shouldFilterSession(snapshot, cookie.callingUid, sessionId)) {
1689                     try {
1690                         invokeCallback(callback, msg);
1691                     } catch (RemoteException ignored) {
1692                     }
1693                 }
1694             }
1695             mCallbacks.finishBroadcast();
1696         }
1697 
1698         private void invokeCallback(IPackageInstallerCallback callback, Message msg)
1699                 throws RemoteException {
1700             final int sessionId = msg.arg1;
1701             switch (msg.what) {
1702                 case MSG_SESSION_CREATED:
1703                     callback.onSessionCreated(sessionId);
1704                     break;
1705                 case MSG_SESSION_BADGING_CHANGED:
1706                     callback.onSessionBadgingChanged(sessionId);
1707                     break;
1708                 case MSG_SESSION_ACTIVE_CHANGED:
1709                     callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
1710                     break;
1711                 case MSG_SESSION_PROGRESS_CHANGED:
1712                     callback.onSessionProgressChanged(sessionId, (float) msg.obj);
1713                     break;
1714                 case MSG_SESSION_FINISHED:
1715                     callback.onSessionFinished(sessionId, (boolean) msg.obj);
1716                     break;
1717             }
1718         }
1719 
1720         private void notifySessionCreated(int sessionId, int userId) {
1721             obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
1722         }
1723 
1724         private void notifySessionBadgingChanged(int sessionId, int userId) {
1725             obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
1726         }
1727 
1728         private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
1729             obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
1730         }
1731 
1732         private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
1733             obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
1734         }
1735 
1736         public void notifySessionFinished(int sessionId, int userId, boolean success) {
1737             obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
1738         }
1739     }
1740 
1741     static class ParentChildSessionMap {
1742         private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap;
1743 
1744         private final Comparator<PackageInstallerSession> mSessionCreationComparator =
1745                 Comparator.comparingLong(
1746                         (PackageInstallerSession sess) -> sess != null ? sess.createdMillis : -1)
1747                         .thenComparingInt(sess -> sess != null ? sess.sessionId : -1);
1748 
1749         ParentChildSessionMap() {
1750             mSessionMap = new TreeMap<>(mSessionCreationComparator);
1751         }
1752 
1753         boolean containsSession() {
1754             return !(mSessionMap.isEmpty());
1755         }
1756 
1757         private void addParentSession(PackageInstallerSession session) {
1758             if (!mSessionMap.containsKey(session)) {
1759                 mSessionMap.put(session, new TreeSet<>(mSessionCreationComparator));
1760             }
1761         }
1762 
1763         private void addChildSession(PackageInstallerSession session,
1764                 PackageInstallerSession parentSession) {
1765             addParentSession(parentSession);
1766             mSessionMap.get(parentSession).add(session);
1767         }
1768 
1769         void addSession(PackageInstallerSession session,
1770                 PackageInstallerSession parentSession) {
1771             if (session.hasParentSessionId()) {
1772                 addChildSession(session, parentSession);
1773             } else {
1774                 addParentSession(session);
1775             }
1776         }
1777 
1778         void dump(String tag, IndentingPrintWriter pw) {
1779             pw.println(tag + " install sessions:");
1780             pw.increaseIndent();
1781 
1782             for (Map.Entry<PackageInstallerSession, TreeSet<PackageInstallerSession>> entry
1783                     : mSessionMap.entrySet()) {
1784                 PackageInstallerSession parentSession = entry.getKey();
1785                 if (parentSession != null) {
1786                     pw.print(tag + " ");
1787                     parentSession.dump(pw);
1788                     pw.println();
1789                     pw.increaseIndent();
1790                 }
1791 
1792                 for (PackageInstallerSession childSession : entry.getValue()) {
1793                     pw.print(tag + " Child ");
1794                     childSession.dump(pw);
1795                     pw.println();
1796                 }
1797 
1798                 pw.decreaseIndent();
1799             }
1800 
1801             pw.println();
1802             pw.decreaseIndent();
1803         }
1804     }
1805 
1806     void dump(IndentingPrintWriter pw) {
1807         synchronized (mSessions) {
1808             ParentChildSessionMap activeSessionMap = new ParentChildSessionMap();
1809             ParentChildSessionMap orphanedChildSessionMap = new ParentChildSessionMap();
1810             ParentChildSessionMap finalizedSessionMap = new ParentChildSessionMap();
1811 
1812             int N = mSessions.size();
1813             for (int i = 0; i < N; i++) {
1814                 final PackageInstallerSession session = mSessions.valueAt(i);
1815 
1816                 final PackageInstallerSession rootSession = session.hasParentSessionId()
1817                         ? getSession(session.getParentSessionId())
1818                         : session;
1819                 // Do not print orphaned child sessions as active install sessions
1820                 if (rootSession == null) {
1821                     orphanedChildSessionMap.addSession(session, rootSession);
1822                     continue;
1823                 }
1824 
1825                 // Do not print finalized staged session as active install sessions
1826                 if (rootSession.isStagedAndInTerminalState()) {
1827                     finalizedSessionMap.addSession(session, rootSession);
1828                     continue;
1829                 }
1830 
1831                 activeSessionMap.addSession(session, rootSession);
1832             }
1833 
1834             activeSessionMap.dump("Active", pw);
1835 
1836             if (orphanedChildSessionMap.containsSession()) {
1837                 // Presence of orphaned sessions indicate leak in cleanup for multi-package and
1838                 // should be cleaned up.
1839                 orphanedChildSessionMap.dump("Orphaned", pw);
1840             }
1841 
1842             finalizedSessionMap.dump("Finalized", pw);
1843 
1844             pw.println("Historical install sessions:");
1845             pw.increaseIndent();
1846             N = mHistoricalSessions.size();
1847             for (int i = 0; i < N; i++) {
1848                 pw.print(mHistoricalSessions.get(i));
1849                 pw.println();
1850             }
1851             pw.println();
1852             pw.decreaseIndent();
1853 
1854             pw.println("Legacy install sessions:");
1855             pw.increaseIndent();
1856             pw.println(mLegacySessions.toString());
1857             pw.println();
1858             pw.decreaseIndent();
1859         }
1860         mSilentUpdatePolicy.dump(pw);
1861         mGentleUpdateHelper.dump(pw);
1862     }
1863 
1864     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
1865     public class InternalCallback {
1866         public void onSessionBadgingChanged(PackageInstallerSession session) {
1867             mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
1868             mSettingsWriteRequest.schedule();
1869         }
1870 
1871         public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
1872             mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId,
1873                     active);
1874         }
1875 
1876         public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
1877             mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId,
1878                     progress);
1879         }
1880 
1881         public void onSessionChanged(PackageInstallerSession session) {
1882             session.markUpdated();
1883             mSettingsWriteRequest.schedule();
1884             // TODO(b/210359798): Remove the session.isStaged() check. Some apps assume this
1885             // broadcast is sent by only staged sessions and call isStagedSessionApplied() without
1886             // checking if it is a staged session or not and cause exception.
1887             if (mOkToSendBroadcasts && !session.isDestroyed() && session.isStaged()) {
1888                 // we don't scrub the data here as this is sent only to the installer several
1889                 // privileged system packages
1890                 sendSessionUpdatedBroadcast(
1891                         session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID),
1892                         session.userId);
1893             }
1894         }
1895 
1896         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
1897             mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
1898 
1899             mInstallHandler.post(new Runnable() {
1900                 @Override
1901                 public void run() {
1902                     if (session.isStaged() && !success) {
1903                         mStagingManager.abortSession(session.mStagedSession);
1904                     }
1905                     synchronized (mSessions) {
1906                         // Child sessions will be removed along with its parent as a whole
1907                         if (!session.hasParentSessionId()) {
1908                             // Retain policy:
1909                             // 1. Don't keep non-staged sessions
1910                             // 2. Don't keep explicitly abandoned sessions
1911                             // 3. Don't keep sessions that fail validation (isCommitted() is false)
1912                             boolean shouldRemove = !session.isStaged() || session.isDestroyed()
1913                                     || !session.isCommitted();
1914                             if (shouldRemove) {
1915                                 removeActiveSession(session);
1916                             }
1917                         }
1918 
1919                         final File appIconFile = buildAppIconFile(session.sessionId);
1920                         if (appIconFile.exists()) {
1921                             appIconFile.delete();
1922                         }
1923 
1924                         mSettingsWriteRequest.runNow();
1925                     }
1926                 }
1927             });
1928         }
1929 
1930         public void onSessionPrepared(PackageInstallerSession session) {
1931             // We prepared the destination to write into; we want to persist
1932             // this, but it's not critical enough to block for.
1933             mSettingsWriteRequest.schedule();
1934         }
1935 
1936         public void onSessionSealedBlocking(PackageInstallerSession session) {
1937             // It's very important that we block until we've recorded the
1938             // session as being sealed, since we never want to allow mutation
1939             // after sealing.
1940             mSettingsWriteRequest.runNow();
1941         }
1942     }
1943 
1944     /**
1945      * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing
1946      * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}.
1947      */
1948     private void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo,
1949             int userId) {
1950         if (TextUtils.isEmpty(sessionInfo.installerPackageName)) {
1951             return;
1952         }
1953         Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED)
1954                 .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
1955                 .setPackage(sessionInfo.installerPackageName);
1956         mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
1957     }
1958 
1959     /**
1960      * Abandon unfinished sessions if the installer package has been uninstalled.
1961      * @param installerAppId the app ID of the installer package that has been uninstalled.
1962      * @param userId the user that has the installer package uninstalled.
1963      */
1964     void onInstallerPackageDeleted(int installerAppId, int userId) {
1965         synchronized (mSessions) {
1966             for (int i = 0; i < mSessions.size(); i++) {
1967                 final PackageInstallerSession session = mSessions.valueAt(i);
1968                 if (!matchesInstaller(session, installerAppId, userId)) {
1969                     continue;
1970                 }
1971                 // Find parent session and only abandon parent session if installer matches
1972                 PackageInstallerSession root = !session.hasParentSessionId()
1973                         ? session : mSessions.get(session.getParentSessionId());
1974                 if (root != null && matchesInstaller(root, installerAppId, userId)
1975                         && !root.isDestroyed()) {
1976                     root.abandon();
1977                 }
1978             }
1979         }
1980     }
1981 
1982     private boolean matchesInstaller(PackageInstallerSession session, int installerAppId,
1983             int userId) {
1984         final int installerUid = session.getInstallerUid();
1985         if (installerAppId == UserHandle.USER_ALL) {
1986             return UserHandle.getAppId(installerUid) == installerAppId;
1987         } else {
1988             return UserHandle.getUid(userId, installerAppId) == installerUid;
1989         }
1990     }
1991 }
1992