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