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