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 org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20 import static org.xmlpull.v1.XmlPullParser.START_TAG;
21 
22 import android.Manifest;
23 import android.app.ActivityManager;
24 import android.app.AppGlobals;
25 import android.app.AppOpsManager;
26 import android.app.Notification;
27 import android.app.NotificationManager;
28 import android.app.PackageDeleteObserver;
29 import android.app.admin.DevicePolicyEventLogger;
30 import android.app.admin.DevicePolicyManagerInternal;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentSender;
34 import android.content.IntentSender.SendIntentException;
35 import android.content.pm.ApplicationInfo;
36 import android.content.pm.IPackageInstaller;
37 import android.content.pm.IPackageInstallerCallback;
38 import android.content.pm.IPackageInstallerSession;
39 import android.content.pm.PackageInfo;
40 import android.content.pm.PackageInstaller;
41 import android.content.pm.PackageInstaller.SessionInfo;
42 import android.content.pm.PackageInstaller.SessionParams;
43 import android.content.pm.PackageManager;
44 import android.content.pm.ParceledListSlice;
45 import android.content.pm.VersionedPackage;
46 import android.graphics.Bitmap;
47 import android.net.Uri;
48 import android.os.Binder;
49 import android.os.Build;
50 import android.os.Bundle;
51 import android.os.Environment;
52 import android.os.Handler;
53 import android.os.HandlerThread;
54 import android.os.Looper;
55 import android.os.Message;
56 import android.os.Process;
57 import android.os.RemoteCallbackList;
58 import android.os.RemoteException;
59 import android.os.SELinux;
60 import android.os.UserManager;
61 import android.os.storage.StorageManager;
62 import android.stats.devicepolicy.DevicePolicyEnums;
63 import android.system.ErrnoException;
64 import android.system.Os;
65 import android.text.TextUtils;
66 import android.text.format.DateUtils;
67 import android.util.ArraySet;
68 import android.util.AtomicFile;
69 import android.util.ExceptionUtils;
70 import android.util.Slog;
71 import android.util.SparseArray;
72 import android.util.SparseBooleanArray;
73 import android.util.SparseIntArray;
74 import android.util.Xml;
75 
76 import com.android.internal.R;
77 import com.android.internal.annotations.GuardedBy;
78 import com.android.internal.content.PackageHelper;
79 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
80 import com.android.internal.notification.SystemNotificationChannels;
81 import com.android.internal.util.FastXmlSerializer;
82 import com.android.internal.util.ImageUtils;
83 import com.android.internal.util.IndentingPrintWriter;
84 import com.android.server.IoThread;
85 import com.android.server.LocalServices;
86 import com.android.server.pm.permission.PermissionManagerServiceInternal;
87 
88 import libcore.io.IoUtils;
89 
90 import org.xmlpull.v1.XmlPullParser;
91 import org.xmlpull.v1.XmlPullParserException;
92 import org.xmlpull.v1.XmlSerializer;
93 
94 import java.io.CharArrayWriter;
95 import java.io.File;
96 import java.io.FileInputStream;
97 import java.io.FileNotFoundException;
98 import java.io.FileOutputStream;
99 import java.io.FilenameFilter;
100 import java.io.IOException;
101 import java.nio.charset.StandardCharsets;
102 import java.security.SecureRandom;
103 import java.util.ArrayList;
104 import java.util.Collections;
105 import java.util.List;
106 import java.util.Objects;
107 import java.util.Random;
108 import java.util.function.IntPredicate;
109 
110 /** The service responsible for installing packages. */
111 public class PackageInstallerService extends IPackageInstaller.Stub implements
112         PackageSessionProvider {
113     private static final String TAG = "PackageInstaller";
114     private static final boolean LOGD = false;
115 
116     // TODO: remove outstanding sessions when installer package goes away
117     // TODO: notify listeners in other users when package has been installed there
118     // TODO: purge expired sessions periodically in addition to at reboot
119 
120     /** XML constants used in {@link #mSessionsFile} */
121     private static final String TAG_SESSIONS = "sessions";
122 
123     /** Automatically destroy sessions older than this */
124     private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
125     /** Automatically destroy staged sessions that have not changed state in this time */
126     private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 7 * DateUtils.DAY_IN_MILLIS;
127     /** Upper bound on number of active sessions for a UID */
128     private static final long MAX_ACTIVE_SESSIONS = 1024;
129     /** Upper bound on number of historical sessions for a UID */
130     private static final long MAX_HISTORICAL_SESSIONS = 1048576;
131 
132     private final Context mContext;
133     private final PackageManagerService mPm;
134     private final ApexManager mApexManager;
135     private final StagingManager mStagingManager;
136     private final PermissionManagerServiceInternal mPermissionManager;
137 
138     private AppOpsManager mAppOps;
139 
140     private final HandlerThread mInstallThread;
141     private final Handler mInstallHandler;
142 
143     private final Callbacks mCallbacks;
144 
145     private volatile boolean mOkToSendBroadcasts = false;
146 
147     /**
148      * File storing persisted {@link #mSessions} metadata.
149      */
150     private final AtomicFile mSessionsFile;
151 
152     /**
153      * Directory storing persisted {@link #mSessions} metadata which is too
154      * heavy to store directly in {@link #mSessionsFile}.
155      */
156     private final File mSessionsDir;
157 
158     private final InternalCallback mInternalCallback = new InternalCallback();
159 
160     /**
161      * Used for generating session IDs. Since this is created at boot time,
162      * normal random might be predictable.
163      */
164     private final Random mRandom = new SecureRandom();
165 
166     /** All sessions allocated */
167     @GuardedBy("mSessions")
168     private final SparseBooleanArray mAllocatedSessions = new SparseBooleanArray();
169 
170     @GuardedBy("mSessions")
171     private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
172 
173     /** Historical sessions kept around for debugging purposes */
174     @GuardedBy("mSessions")
175     private final List<String> mHistoricalSessions = new ArrayList<>();
176 
177     @GuardedBy("mSessions")
178     private final SparseIntArray mHistoricalSessionsByInstaller = new SparseIntArray();
179 
180     /** Sessions allocated to legacy users */
181     @GuardedBy("mSessions")
182     private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
183 
184     private static final FilenameFilter sStageFilter = new FilenameFilter() {
185         @Override
186         public boolean accept(File dir, String name) {
187             return isStageName(name);
188         }
189     };
190 
PackageInstallerService(Context context, PackageManagerService pm, ApexManager am)191     public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) {
192         mContext = context;
193         mPm = pm;
194         mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class);
195 
196         mInstallThread = new HandlerThread(TAG);
197         mInstallThread.start();
198 
199         mInstallHandler = new Handler(mInstallThread.getLooper());
200 
201         mCallbacks = new Callbacks(mInstallThread.getLooper());
202 
203         mSessionsFile = new AtomicFile(
204                 new File(Environment.getDataSystemDirectory(), "install_sessions.xml"),
205                 "package-session");
206         mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
207         mSessionsDir.mkdirs();
208 
209         mApexManager = am;
210 
211         mStagingManager = new StagingManager(this, am, context);
212     }
213 
okToSendBroadcasts()214     boolean okToSendBroadcasts()  {
215         return mOkToSendBroadcasts;
216     }
217 
systemReady()218     public void systemReady() {
219         mAppOps = mContext.getSystemService(AppOpsManager.class);
220 
221         synchronized (mSessions) {
222             readSessionsLocked();
223 
224             reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL);
225 
226             final ArraySet<File> unclaimedIcons = newArraySet(
227                     mSessionsDir.listFiles());
228 
229             // Ignore stages and icons claimed by active sessions
230             for (int i = 0; i < mSessions.size(); i++) {
231                 final PackageInstallerSession session = mSessions.valueAt(i);
232                 unclaimedIcons.remove(buildAppIconFile(session.sessionId));
233             }
234 
235             // Clean up orphaned icons
236             for (File icon : unclaimedIcons) {
237                 Slog.w(TAG, "Deleting orphan icon " + icon);
238                 icon.delete();
239             }
240 
241             // Invalid sessions might have been marked while parsing. Re-write the database with
242             // the updated information.
243             writeSessionsLocked();
244 
245         }
246     }
247 
restoreAndApplyStagedSessionIfNeeded()248     void restoreAndApplyStagedSessionIfNeeded() {
249         List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>();
250         synchronized (mSessions) {
251             for (int i = 0; i < mSessions.size(); i++) {
252                 final PackageInstallerSession session = mSessions.valueAt(i);
253                 if (session.isStaged()) {
254                     stagedSessionsToRestore.add(session);
255                 }
256             }
257         }
258         // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
259         // atomic install which needs to query sessions, which requires lock on mSessions.
260         boolean isDeviceUpgrading = mPm.isDeviceUpgrading();
261         for (PackageInstallerSession session : stagedSessionsToRestore) {
262             if (!session.isStagedAndInTerminalState() && session.hasParentSessionId()
263                     && getSession(session.getParentSessionId()) == null) {
264                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
265                         "An orphan staged session " + session.sessionId + " is found, "
266                                 + "parent " + session.getParentSessionId() + " is missing");
267             }
268             mStagingManager.restoreSession(session, isDeviceUpgrading);
269         }
270         // Broadcasts are not sent while we restore sessions on boot, since no processes would be
271         // ready to listen to them. From now on, we greedily assume that broadcasts requests are
272         // safe to send out. The worst that can happen is that a broadcast is attempted before
273         // ActivityManagerService completes its own systemReady(), in which case it will be rejected
274         // with an otherwise harmless exception.
275         // A more appropriate way to do this would be to wait until the correct  boot phase is
276         // reached, but since we are not a SystemService we can't override onBootPhase.
277         // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable
278         // way either.
279         mOkToSendBroadcasts = true;
280     }
281 
282     @GuardedBy("mSessions")
reconcileStagesLocked(String volumeUuid)283     private void reconcileStagesLocked(String volumeUuid) {
284         final File stagingDir = getTmpSessionDir(volumeUuid);
285         final ArraySet<File> unclaimedStages = newArraySet(
286                 stagingDir.listFiles(sStageFilter));
287 
288         // Ignore stages claimed by active sessions
289         for (int i = 0; i < mSessions.size(); i++) {
290             final PackageInstallerSession session = mSessions.valueAt(i);
291             unclaimedStages.remove(session.stageDir);
292         }
293 
294         // Clean up orphaned staging directories
295         for (File stage : unclaimedStages) {
296             Slog.w(TAG, "Deleting orphan stage " + stage);
297             synchronized (mPm.mInstallLock) {
298                 mPm.removeCodePathLI(stage);
299             }
300         }
301     }
302 
onPrivateVolumeMounted(String volumeUuid)303     public void onPrivateVolumeMounted(String volumeUuid) {
304         synchronized (mSessions) {
305             reconcileStagesLocked(volumeUuid);
306         }
307     }
308 
isStageName(String name)309     public static boolean isStageName(String name) {
310         final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
311         final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
312         final boolean isLegacyContainer = name.startsWith("smdl2tmp");
313         return isFile || isContainer || isLegacyContainer;
314     }
315 
316     @Deprecated
allocateStageDirLegacy(String volumeUuid, boolean isEphemeral)317     public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
318         synchronized (mSessions) {
319             try {
320                 final int sessionId = allocateSessionIdLocked();
321                 mLegacySessions.put(sessionId, true);
322                 final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
323                 prepareStageDir(sessionStageDir);
324                 return sessionStageDir;
325             } catch (IllegalStateException e) {
326                 throw new IOException(e);
327             }
328         }
329     }
330 
331     @Deprecated
allocateExternalStageCidLegacy()332     public String allocateExternalStageCidLegacy() {
333         synchronized (mSessions) {
334             final int sessionId = allocateSessionIdLocked();
335             mLegacySessions.put(sessionId, true);
336             return "smdl" + sessionId + ".tmp";
337         }
338     }
339 
340     @GuardedBy("mSessions")
readSessionsLocked()341     private void readSessionsLocked() {
342         if (LOGD) Slog.v(TAG, "readSessionsLocked()");
343 
344         mSessions.clear();
345 
346         FileInputStream fis = null;
347         try {
348             fis = mSessionsFile.openRead();
349             final XmlPullParser in = Xml.newPullParser();
350             in.setInput(fis, StandardCharsets.UTF_8.name());
351 
352             int type;
353             while ((type = in.next()) != END_DOCUMENT) {
354                 if (type == START_TAG) {
355                     final String tag = in.getName();
356                     if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
357                         final PackageInstallerSession session;
358                         try {
359                             session = PackageInstallerSession.readFromXml(in, mInternalCallback,
360                                     mContext, mPm, mInstallThread.getLooper(), mStagingManager,
361                                     mSessionsDir, this);
362                         } catch (Exception e) {
363                             Slog.e(TAG, "Could not read session", e);
364                             continue;
365                         }
366 
367                         final long age = System.currentTimeMillis() - session.createdMillis;
368                         final long timeSinceUpdate =
369                                 System.currentTimeMillis() - session.getUpdatedMillis();
370                         final boolean valid;
371                         if (session.isStaged()) {
372                             if (timeSinceUpdate >= MAX_TIME_SINCE_UPDATE_MILLIS
373                                     && session.isStagedAndInTerminalState()) {
374                                 valid = false;
375                             } else {
376                                 valid = true;
377                             }
378                         } else if (age >= MAX_AGE_MILLIS) {
379                             Slog.w(TAG, "Abandoning old session created at "
380                                         + session.createdMillis);
381                             valid = false;
382                         } else {
383                             valid = true;
384                         }
385 
386                         if (valid) {
387                             mSessions.put(session.sessionId, session);
388                         } else {
389                             // Since this is early during boot we don't send
390                             // any observer events about the session, but we
391                             // keep details around for dumpsys.
392                             addHistoricalSessionLocked(session);
393                         }
394                         mAllocatedSessions.put(session.sessionId, true);
395                     }
396                 }
397             }
398         } catch (FileNotFoundException e) {
399             // Missing sessions are okay, probably first boot
400         } catch (IOException | XmlPullParserException e) {
401             Slog.wtf(TAG, "Failed reading install sessions", e);
402         } finally {
403             IoUtils.closeQuietly(fis);
404         }
405         // After all of the sessions were loaded, they are ready to be sealed and validated
406         for (int i = 0; i < mSessions.size(); ++i) {
407             PackageInstallerSession session = mSessions.valueAt(i);
408             session.sealAndValidateIfNecessary();
409         }
410     }
411 
412     @GuardedBy("mSessions")
addHistoricalSessionLocked(PackageInstallerSession session)413     private void addHistoricalSessionLocked(PackageInstallerSession session) {
414         CharArrayWriter writer = new CharArrayWriter();
415         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "    ");
416         session.dump(pw);
417         mHistoricalSessions.add(writer.toString());
418 
419         int installerUid = session.getInstallerUid();
420         // Increment the number of sessions by this installerUid.
421         mHistoricalSessionsByInstaller.put(installerUid,
422                 mHistoricalSessionsByInstaller.get(installerUid) + 1);
423     }
424 
425     @GuardedBy("mSessions")
writeSessionsLocked()426     private void writeSessionsLocked() {
427         if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
428 
429         FileOutputStream fos = null;
430         try {
431             fos = mSessionsFile.startWrite();
432 
433             XmlSerializer out = new FastXmlSerializer();
434             out.setOutput(fos, StandardCharsets.UTF_8.name());
435             out.startDocument(null, true);
436             out.startTag(null, TAG_SESSIONS);
437             final int size = mSessions.size();
438             for (int i = 0; i < size; i++) {
439                 final PackageInstallerSession session = mSessions.valueAt(i);
440                 session.write(out, mSessionsDir);
441             }
442             out.endTag(null, TAG_SESSIONS);
443             out.endDocument();
444 
445             mSessionsFile.finishWrite(fos);
446         } catch (IOException e) {
447             if (fos != null) {
448                 mSessionsFile.failWrite(fos);
449             }
450         }
451     }
452 
buildAppIconFile(int sessionId)453     private File buildAppIconFile(int sessionId) {
454         return new File(mSessionsDir, "app_icon." + sessionId + ".png");
455     }
456 
writeSessionsAsync()457     private void writeSessionsAsync() {
458         IoThread.getHandler().post(new Runnable() {
459             @Override
460             public void run() {
461                 synchronized (mSessions) {
462                     writeSessionsLocked();
463                 }
464             }
465         });
466     }
467 
468     @Override
createSession(SessionParams params, String installerPackageName, int userId)469     public int createSession(SessionParams params, String installerPackageName, int userId) {
470         try {
471             return createSessionInternal(params, installerPackageName, userId);
472         } catch (IOException e) {
473             throw ExceptionUtils.wrap(e);
474         }
475     }
476 
createSessionInternal(SessionParams params, String installerPackageName, int userId)477     private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
478             throws IOException {
479         final int callingUid = Binder.getCallingUid();
480         mPermissionManager.enforceCrossUserPermission(
481                 callingUid, userId, true, true, "createSession");
482 
483         if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
484             throw new SecurityException("User restriction prevents installing");
485         }
486 
487         if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
488             params.installFlags |= PackageManager.INSTALL_FROM_ADB;
489 
490         } else {
491             // Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
492             // caller.
493             if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) !=
494                     PackageManager.PERMISSION_GRANTED) {
495                 mAppOps.checkPackage(callingUid, installerPackageName);
496             }
497 
498             params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
499             params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
500             params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
501             params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
502             if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
503                     && !mPm.isCallerVerifier(callingUid)) {
504                 params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
505             }
506         }
507 
508         if (Build.IS_DEBUGGABLE || isDowngradeAllowedForCaller(callingUid)) {
509             params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
510         } else {
511             params.installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
512             params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
513         }
514 
515         if (callingUid != Process.SYSTEM_UID) {
516             // Only system_server can use INSTALL_DISABLE_VERIFICATION.
517             params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
518         }
519 
520         boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
521         if (params.isStaged || isApex) {
522             mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG);
523         }
524 
525         if (isApex) {
526             if (!mApexManager.isApexSupported()) {
527                 throw new IllegalArgumentException(
528                     "This device doesn't support the installation of APEX files");
529             }
530             if (!params.isStaged) {
531                 throw new IllegalArgumentException(
532                     "APEX files can only be installed as part of a staged session.");
533             }
534         }
535 
536         if (!params.isMultiPackage) {
537             // Only system components can circumvent runtime permissions when installing.
538             if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
539                     && mContext.checkCallingOrSelfPermission(Manifest.permission
540                     .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
541                 throw new SecurityException("You need the "
542                         + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
543                         + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
544             }
545 
546             // Defensively resize giant app icons
547             if (params.appIcon != null) {
548                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
549                         Context.ACTIVITY_SERVICE);
550                 final int iconSize = am.getLauncherLargeIconSize();
551                 if ((params.appIcon.getWidth() > iconSize * 2)
552                         || (params.appIcon.getHeight() > iconSize * 2)) {
553                     params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
554                             true);
555                 }
556             }
557 
558             switch (params.mode) {
559                 case SessionParams.MODE_FULL_INSTALL:
560                 case SessionParams.MODE_INHERIT_EXISTING:
561                     break;
562                 default:
563                     throw new IllegalArgumentException("Invalid install mode: " + params.mode);
564             }
565 
566             // If caller requested explicit location, sanity check it, otherwise
567             // resolve the best internal or adopted location.
568             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
569                 if (!PackageHelper.fitsOnInternal(mContext, params)) {
570                     throw new IOException("No suitable internal storage available");
571                 }
572             } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
573                 // For now, installs to adopted media are treated as internal from
574                 // an install flag point-of-view.
575                 params.installFlags |= PackageManager.INSTALL_INTERNAL;
576             } else {
577                 params.installFlags |= PackageManager.INSTALL_INTERNAL;
578 
579                 // Resolve best location for install, based on combination of
580                 // requested install flags, delta size, and manifest settings.
581                 final long ident = Binder.clearCallingIdentity();
582                 try {
583                     params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
584                 } finally {
585                     Binder.restoreCallingIdentity(ident);
586                 }
587             }
588         }
589 
590         final int sessionId;
591         final PackageInstallerSession session;
592         synchronized (mSessions) {
593             // Sanity check that installer isn't going crazy
594             final int activeCount = getSessionCount(mSessions, callingUid);
595             if (activeCount >= MAX_ACTIVE_SESSIONS) {
596                 throw new IllegalStateException(
597                         "Too many active sessions for UID " + callingUid);
598             }
599             final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
600             if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
601                 throw new IllegalStateException(
602                         "Too many historical sessions for UID " + callingUid);
603             }
604 
605             sessionId = allocateSessionIdLocked();
606         }
607 
608         final long createdMillis = System.currentTimeMillis();
609         // We're staging to exactly one location
610         File stageDir = null;
611         String stageCid = null;
612         if (!params.isMultiPackage) {
613             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
614                 stageDir = buildSessionDir(sessionId, params);
615             } else {
616                 stageCid = buildExternalStageCid(sessionId);
617             }
618         }
619         session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
620                 mInstallThread.getLooper(), mStagingManager, sessionId, userId,
621                 installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
622                 false, false, null, SessionInfo.INVALID_ID, false, false, false,
623                 SessionInfo.STAGED_SESSION_NO_ERROR, "");
624 
625         synchronized (mSessions) {
626             mSessions.put(sessionId, session);
627         }
628         if (params.isStaged) {
629             mStagingManager.createSession(session);
630         }
631 
632         if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
633             mCallbacks.notifySessionCreated(session.sessionId, session.userId);
634         }
635         writeSessionsAsync();
636         return sessionId;
637     }
638 
isDowngradeAllowedForCaller(int callingUid)639     private boolean isDowngradeAllowedForCaller(int callingUid) {
640         return callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID
641                 || callingUid == Process.SHELL_UID;
642     }
643 
644     @Override
updateSessionAppIcon(int sessionId, Bitmap appIcon)645     public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
646         synchronized (mSessions) {
647             final PackageInstallerSession session = mSessions.get(sessionId);
648             if (session == null || !isCallingUidOwner(session)) {
649                 throw new SecurityException("Caller has no access to session " + sessionId);
650             }
651 
652             // Defensively resize giant app icons
653             if (appIcon != null) {
654                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
655                         Context.ACTIVITY_SERVICE);
656                 final int iconSize = am.getLauncherLargeIconSize();
657                 if ((appIcon.getWidth() > iconSize * 2)
658                         || (appIcon.getHeight() > iconSize * 2)) {
659                     appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
660                 }
661             }
662 
663             session.params.appIcon = appIcon;
664             session.params.appIconLastModified = -1;
665 
666             mInternalCallback.onSessionBadgingChanged(session);
667         }
668     }
669 
670     @Override
updateSessionAppLabel(int sessionId, String appLabel)671     public void updateSessionAppLabel(int sessionId, String appLabel) {
672         synchronized (mSessions) {
673             final PackageInstallerSession session = mSessions.get(sessionId);
674             if (session == null || !isCallingUidOwner(session)) {
675                 throw new SecurityException("Caller has no access to session " + sessionId);
676             }
677             session.params.appLabel = appLabel;
678             mInternalCallback.onSessionBadgingChanged(session);
679         }
680     }
681 
682     @Override
abandonSession(int sessionId)683     public void abandonSession(int sessionId) {
684         synchronized (mSessions) {
685             final PackageInstallerSession session = mSessions.get(sessionId);
686             if (session == null || !isCallingUidOwner(session)) {
687                 throw new SecurityException("Caller has no access to session " + sessionId);
688             }
689             session.abandon();
690         }
691     }
692 
693     @Override
openSession(int sessionId)694     public IPackageInstallerSession openSession(int sessionId) {
695         try {
696             return openSessionInternal(sessionId);
697         } catch (IOException e) {
698             throw ExceptionUtils.wrap(e);
699         }
700     }
701 
openSessionInternal(int sessionId)702     private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
703         synchronized (mSessions) {
704             final PackageInstallerSession session = mSessions.get(sessionId);
705             if (session == null || !isCallingUidOwner(session)) {
706                 throw new SecurityException("Caller has no access to session " + sessionId);
707             }
708             session.open();
709             return session;
710         }
711     }
712 
713     @GuardedBy("mSessions")
allocateSessionIdLocked()714     private int allocateSessionIdLocked() {
715         int n = 0;
716         int sessionId;
717         do {
718             sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
719             if (!mAllocatedSessions.get(sessionId, false)) {
720                 mAllocatedSessions.put(sessionId, true);
721                 return sessionId;
722             }
723         } while (n++ < 32);
724 
725         throw new IllegalStateException("Failed to allocate session ID");
726     }
727 
getTmpSessionDir(String volumeUuid)728     private File getTmpSessionDir(String volumeUuid) {
729         return Environment.getDataAppDirectory(volumeUuid);
730     }
731 
buildTmpSessionDir(int sessionId, String volumeUuid)732     private File buildTmpSessionDir(int sessionId, String volumeUuid) {
733         final File sessionStagingDir = getTmpSessionDir(volumeUuid);
734         return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp");
735     }
736 
buildSessionDir(int sessionId, SessionParams params)737     private File buildSessionDir(int sessionId, SessionParams params) {
738         if (params.isStaged) {
739             final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
740             return new File(sessionStagingDir, "session_" + sessionId);
741         }
742         return buildTmpSessionDir(sessionId, params.volumeUuid);
743     }
744 
prepareStageDir(File stageDir)745     static void prepareStageDir(File stageDir) throws IOException {
746         if (stageDir.exists()) {
747             throw new IOException("Session dir already exists: " + stageDir);
748         }
749 
750         try {
751             Os.mkdir(stageDir.getAbsolutePath(), 0775);
752             Os.chmod(stageDir.getAbsolutePath(), 0775);
753         } catch (ErrnoException e) {
754             // This purposefully throws if directory already exists
755             throw new IOException("Failed to prepare session dir: " + stageDir, e);
756         }
757 
758         if (!SELinux.restorecon(stageDir)) {
759             throw new IOException("Failed to restorecon session dir: " + stageDir);
760         }
761     }
762 
buildExternalStageCid(int sessionId)763     private String buildExternalStageCid(int sessionId) {
764         return "smdl" + sessionId + ".tmp";
765     }
766 
767     @Override
getSessionInfo(int sessionId)768     public SessionInfo getSessionInfo(int sessionId) {
769         synchronized (mSessions) {
770             final PackageInstallerSession session = mSessions.get(sessionId);
771             return session != null ? session.generateInfo() : null;
772         }
773     }
774 
775     @Override
getStagedSessions()776     public ParceledListSlice<SessionInfo> getStagedSessions() {
777         return mStagingManager.getSessions();
778     }
779 
780     @Override
getAllSessions(int userId)781     public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
782         mPermissionManager.enforceCrossUserPermission(
783                 Binder.getCallingUid(), userId, true, false, "getAllSessions");
784 
785         final List<SessionInfo> result = new ArrayList<>();
786         synchronized (mSessions) {
787             for (int i = 0; i < mSessions.size(); i++) {
788                 final PackageInstallerSession session = mSessions.valueAt(i);
789                 if (session.userId == userId && !session.hasParentSessionId()) {
790                     result.add(session.generateInfo(false));
791                 }
792             }
793         }
794         return new ParceledListSlice<>(result);
795     }
796 
797     @Override
getMySessions(String installerPackageName, int userId)798     public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
799         mPermissionManager.enforceCrossUserPermission(
800                 Binder.getCallingUid(), userId, true, false, "getMySessions");
801         mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
802 
803         final List<SessionInfo> result = new ArrayList<>();
804         synchronized (mSessions) {
805             for (int i = 0; i < mSessions.size(); i++) {
806                 final PackageInstallerSession session = mSessions.valueAt(i);
807 
808                 SessionInfo info = session.generateInfo(false);
809                 if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
810                         && session.userId == userId && !session.hasParentSessionId()) {
811                     result.add(info);
812                 }
813             }
814         }
815         return new ParceledListSlice<>(result);
816     }
817 
818     @Override
uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, IntentSender statusReceiver, int userId)819     public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
820                 IntentSender statusReceiver, int userId) {
821         final int callingUid = Binder.getCallingUid();
822         mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
823         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
824             mAppOps.checkPackage(callingUid, callerPackageName);
825         }
826 
827         // Check whether the caller is device owner or affiliated profile owner, in which case we do
828         // it silently.
829         DevicePolicyManagerInternal dpmi =
830                 LocalServices.getService(DevicePolicyManagerInternal.class);
831         final boolean canSilentlyInstallPackage =
832                 dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
833 
834         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
835                 statusReceiver, versionedPackage.getPackageName(),
836                 canSilentlyInstallPackage, userId);
837         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
838                     == PackageManager.PERMISSION_GRANTED) {
839             // Sweet, call straight through!
840             mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
841         } else if (canSilentlyInstallPackage) {
842             // Allow the device owner and affiliated profile owner to silently delete packages
843             // Need to clear the calling identity to get DELETE_PACKAGES permission
844             long ident = Binder.clearCallingIdentity();
845             try {
846                 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
847             } finally {
848                 Binder.restoreCallingIdentity(ident);
849             }
850             DevicePolicyEventLogger
851                     .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE)
852                     .setAdmin(callerPackageName)
853                     .write();
854         } else {
855             ApplicationInfo appInfo = mPm.getApplicationInfo(callerPackageName, 0, userId);
856             if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
857                 mContext.enforceCallingOrSelfPermission(Manifest.permission.REQUEST_DELETE_PACKAGES,
858                         null);
859             }
860 
861             // Take a short detour to confirm with user
862             final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
863             intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
864             intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
865             adapter.onUserActionRequired(intent);
866         }
867     }
868 
869     @Override
installExistingPackage(String packageName, int installFlags, int installReason, IntentSender statusReceiver, int userId, List<String> whiteListedPermissions)870     public void installExistingPackage(String packageName, int installFlags, int installReason,
871             IntentSender statusReceiver, int userId, List<String> whiteListedPermissions) {
872         mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason,
873                 whiteListedPermissions, statusReceiver);
874     }
875 
876     @Override
setPermissionsResult(int sessionId, boolean accepted)877     public void setPermissionsResult(int sessionId, boolean accepted) {
878         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
879 
880         synchronized (mSessions) {
881             PackageInstallerSession session = mSessions.get(sessionId);
882             if (session != null) {
883                 session.setPermissionsResult(accepted);
884             }
885         }
886     }
887 
888     @Override
registerCallback(IPackageInstallerCallback callback, int userId)889     public void registerCallback(IPackageInstallerCallback callback, int userId) {
890         mPermissionManager.enforceCrossUserPermission(
891                 Binder.getCallingUid(), userId, true, false, "registerCallback");
892         registerCallback(callback, eventUserId -> userId == eventUserId);
893     }
894 
895     /**
896      * Assume permissions already checked and caller's identity cleared
897      */
registerCallback(IPackageInstallerCallback callback, IntPredicate userCheck)898     public void registerCallback(IPackageInstallerCallback callback, IntPredicate userCheck) {
899         mCallbacks.register(callback, userCheck);
900     }
901 
902     @Override
unregisterCallback(IPackageInstallerCallback callback)903     public void unregisterCallback(IPackageInstallerCallback callback) {
904         mCallbacks.unregister(callback);
905     }
906 
907     @Override
getSession(int sessionId)908     public PackageInstallerSession getSession(int sessionId) {
909         synchronized (mSessions) {
910             return mSessions.get(sessionId);
911         }
912     }
913 
getSessionCount(SparseArray<PackageInstallerSession> sessions, int installerUid)914     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
915             int installerUid) {
916         int count = 0;
917         final int size = sessions.size();
918         for (int i = 0; i < size; i++) {
919             final PackageInstallerSession session = sessions.valueAt(i);
920             if (session.getInstallerUid() == installerUid) {
921                 count++;
922             }
923         }
924         return count;
925     }
926 
isCallingUidOwner(PackageInstallerSession session)927     private boolean isCallingUidOwner(PackageInstallerSession session) {
928         final int callingUid = Binder.getCallingUid();
929         if (callingUid == Process.ROOT_UID) {
930             return true;
931         } else {
932             return (session != null) && (callingUid == session.getInstallerUid());
933         }
934     }
935 
936     static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
937         private final Context mContext;
938         private final IntentSender mTarget;
939         private final String mPackageName;
940         private final Notification mNotification;
941 
PackageDeleteObserverAdapter(Context context, IntentSender target, String packageName, boolean showNotification, int userId)942         public PackageDeleteObserverAdapter(Context context, IntentSender target,
943                 String packageName, boolean showNotification, int userId) {
944             mContext = context;
945             mTarget = target;
946             mPackageName = packageName;
947             if (showNotification) {
948                 mNotification = buildSuccessNotification(mContext,
949                         mContext.getResources().getString(R.string.package_deleted_device_owner),
950                         packageName,
951                         userId);
952             } else {
953                 mNotification = null;
954             }
955         }
956 
957         @Override
onUserActionRequired(Intent intent)958         public void onUserActionRequired(Intent intent) {
959             if (mTarget == null) {
960                 return;
961             }
962             final Intent fillIn = new Intent();
963             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
964             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
965                     PackageInstaller.STATUS_PENDING_USER_ACTION);
966             fillIn.putExtra(Intent.EXTRA_INTENT, intent);
967             try {
968                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
969             } catch (SendIntentException ignored) {
970             }
971         }
972 
973         @Override
onPackageDeleted(String basePackageName, int returnCode, String msg)974         public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
975             if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
976                 NotificationManager notificationManager = (NotificationManager)
977                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
978                 notificationManager.notify(basePackageName,
979                         SystemMessage.NOTE_PACKAGE_STATE,
980                         mNotification);
981             }
982             if (mTarget == null) {
983                 return;
984             }
985             final Intent fillIn = new Intent();
986             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
987             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
988                     PackageManager.deleteStatusToPublicStatus(returnCode));
989             fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
990                     PackageManager.deleteStatusToString(returnCode, msg));
991             fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
992             try {
993                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
994             } catch (SendIntentException ignored) {
995             }
996         }
997     }
998 
sendOnUserActionRequired(Context context, IntentSender target, int sessionId, Intent intent)999     static void sendOnUserActionRequired(Context context, IntentSender target, int sessionId,
1000             Intent intent) {
1001         final Intent fillIn = new Intent();
1002         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
1003         fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1004                 PackageInstaller.STATUS_PENDING_USER_ACTION);
1005         fillIn.putExtra(Intent.EXTRA_INTENT, intent);
1006         try {
1007             target.sendIntent(context, 0, fillIn, null, null);
1008         } catch (SendIntentException ignored) {
1009         }
1010     }
1011 
sendOnPackageInstalled(Context context, IntentSender target, int sessionId, boolean showNotification, int userId, String basePackageName, int returnCode, String msg, Bundle extras)1012     static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
1013             boolean showNotification, int userId, String basePackageName, int returnCode,
1014             String msg, Bundle extras) {
1015         if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
1016             boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
1017             Notification notification = buildSuccessNotification(context,
1018                     context.getResources()
1019                             .getString(update ? R.string.package_updated_device_owner :
1020                                     R.string.package_installed_device_owner),
1021                     basePackageName,
1022                     userId);
1023             if (notification != null) {
1024                 NotificationManager notificationManager = (NotificationManager)
1025                         context.getSystemService(Context.NOTIFICATION_SERVICE);
1026                 notificationManager.notify(basePackageName,
1027                         SystemMessage.NOTE_PACKAGE_STATE,
1028                         notification);
1029             }
1030         }
1031         final Intent fillIn = new Intent();
1032         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
1033         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
1034         fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1035                 PackageManager.installStatusToPublicStatus(returnCode));
1036         fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
1037                 PackageManager.installStatusToString(returnCode, msg));
1038         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
1039         if (extras != null) {
1040             final String existing = extras.getString(
1041                     PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
1042             if (!TextUtils.isEmpty(existing)) {
1043                 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
1044             }
1045         }
1046         try {
1047             target.sendIntent(context, 0, fillIn, null, null);
1048         } catch (SendIntentException ignored) {
1049         }
1050     }
1051 
1052     /**
1053      * Build a notification for package installation / deletion by device owners that is shown if
1054      * the operation succeeds.
1055      */
buildSuccessNotification(Context context, String contentText, String basePackageName, int userId)1056     private static Notification buildSuccessNotification(Context context, String contentText,
1057             String basePackageName, int userId) {
1058         PackageInfo packageInfo = null;
1059         try {
1060             packageInfo = AppGlobals.getPackageManager().getPackageInfo(
1061                     basePackageName, PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
1062         } catch (RemoteException ignored) {
1063         }
1064         if (packageInfo == null || packageInfo.applicationInfo == null) {
1065             Slog.w(TAG, "Notification not built for package: " + basePackageName);
1066             return null;
1067         }
1068         PackageManager pm = context.getPackageManager();
1069         Bitmap packageIcon = ImageUtils.buildScaledBitmap(
1070                 packageInfo.applicationInfo.loadIcon(pm),
1071                 context.getResources().getDimensionPixelSize(
1072                         android.R.dimen.notification_large_icon_width),
1073                 context.getResources().getDimensionPixelSize(
1074                         android.R.dimen.notification_large_icon_height));
1075         CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
1076         return new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
1077                 .setSmallIcon(R.drawable.ic_check_circle_24px)
1078                 .setColor(context.getResources().getColor(
1079                         R.color.system_notification_accent_color))
1080                 .setContentTitle(packageLabel)
1081                 .setContentText(contentText)
1082                 .setStyle(new Notification.BigTextStyle().bigText(contentText))
1083                 .setLargeIcon(packageIcon)
1084                 .build();
1085     }
1086 
newArraySet(E... elements)1087     public static <E> ArraySet<E> newArraySet(E... elements) {
1088         final ArraySet<E> set = new ArraySet<E>();
1089         if (elements != null) {
1090             set.ensureCapacity(elements.length);
1091             Collections.addAll(set, elements);
1092         }
1093         return set;
1094     }
1095 
1096     private static class Callbacks extends Handler {
1097         private static final int MSG_SESSION_CREATED = 1;
1098         private static final int MSG_SESSION_BADGING_CHANGED = 2;
1099         private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
1100         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
1101         private static final int MSG_SESSION_FINISHED = 5;
1102 
1103         private final RemoteCallbackList<IPackageInstallerCallback>
1104                 mCallbacks = new RemoteCallbackList<>();
1105 
Callbacks(Looper looper)1106         public Callbacks(Looper looper) {
1107             super(looper);
1108         }
1109 
register(IPackageInstallerCallback callback, IntPredicate userCheck)1110         public void register(IPackageInstallerCallback callback, IntPredicate userCheck) {
1111             mCallbacks.register(callback, userCheck);
1112         }
1113 
unregister(IPackageInstallerCallback callback)1114         public void unregister(IPackageInstallerCallback callback) {
1115             mCallbacks.unregister(callback);
1116         }
1117 
1118         @Override
handleMessage(Message msg)1119         public void handleMessage(Message msg) {
1120             final int userId = msg.arg2;
1121             final int n = mCallbacks.beginBroadcast();
1122             for (int i = 0; i < n; i++) {
1123                 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
1124                 final IntPredicate userCheck = (IntPredicate) mCallbacks.getBroadcastCookie(i);
1125                 if (userCheck.test(userId)) {
1126                     try {
1127                         invokeCallback(callback, msg);
1128                     } catch (RemoteException ignored) {
1129                     }
1130                 }
1131             }
1132             mCallbacks.finishBroadcast();
1133         }
1134 
invokeCallback(IPackageInstallerCallback callback, Message msg)1135         private void invokeCallback(IPackageInstallerCallback callback, Message msg)
1136                 throws RemoteException {
1137             final int sessionId = msg.arg1;
1138             switch (msg.what) {
1139                 case MSG_SESSION_CREATED:
1140                     callback.onSessionCreated(sessionId);
1141                     break;
1142                 case MSG_SESSION_BADGING_CHANGED:
1143                     callback.onSessionBadgingChanged(sessionId);
1144                     break;
1145                 case MSG_SESSION_ACTIVE_CHANGED:
1146                     callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
1147                     break;
1148                 case MSG_SESSION_PROGRESS_CHANGED:
1149                     callback.onSessionProgressChanged(sessionId, (float) msg.obj);
1150                     break;
1151                 case MSG_SESSION_FINISHED:
1152                     callback.onSessionFinished(sessionId, (boolean) msg.obj);
1153                     break;
1154             }
1155         }
1156 
notifySessionCreated(int sessionId, int userId)1157         private void notifySessionCreated(int sessionId, int userId) {
1158             obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
1159         }
1160 
notifySessionBadgingChanged(int sessionId, int userId)1161         private void notifySessionBadgingChanged(int sessionId, int userId) {
1162             obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
1163         }
1164 
notifySessionActiveChanged(int sessionId, int userId, boolean active)1165         private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
1166             obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
1167         }
1168 
notifySessionProgressChanged(int sessionId, int userId, float progress)1169         private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
1170             obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
1171         }
1172 
notifySessionFinished(int sessionId, int userId, boolean success)1173         public void notifySessionFinished(int sessionId, int userId, boolean success) {
1174             obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
1175         }
1176     }
1177 
dump(IndentingPrintWriter pw)1178     void dump(IndentingPrintWriter pw) {
1179         synchronized (mSessions) {
1180             pw.println("Active install sessions:");
1181             pw.increaseIndent();
1182             int N = mSessions.size();
1183             for (int i = 0; i < N; i++) {
1184                 final PackageInstallerSession session = mSessions.valueAt(i);
1185                 session.dump(pw);
1186                 pw.println();
1187             }
1188             pw.println();
1189             pw.decreaseIndent();
1190 
1191             pw.println("Historical install sessions:");
1192             pw.increaseIndent();
1193             N = mHistoricalSessions.size();
1194             for (int i = 0; i < N; i++) {
1195                 pw.print(mHistoricalSessions.get(i));
1196                 pw.println();
1197             }
1198             pw.println();
1199             pw.decreaseIndent();
1200 
1201             pw.println("Legacy install sessions:");
1202             pw.increaseIndent();
1203             pw.println(mLegacySessions.toString());
1204             pw.decreaseIndent();
1205         }
1206     }
1207 
1208     class InternalCallback {
onSessionBadgingChanged(PackageInstallerSession session)1209         public void onSessionBadgingChanged(PackageInstallerSession session) {
1210             if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
1211                 mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
1212             }
1213 
1214             writeSessionsAsync();
1215         }
1216 
onSessionActiveChanged(PackageInstallerSession session, boolean active)1217         public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
1218             if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
1219                 mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId,
1220                         active);
1221             }
1222         }
1223 
onSessionProgressChanged(PackageInstallerSession session, float progress)1224         public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
1225             if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
1226                 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId,
1227                         progress);
1228             }
1229         }
1230 
onStagedSessionChanged(PackageInstallerSession session)1231         public void onStagedSessionChanged(PackageInstallerSession session) {
1232             session.markUpdated();
1233             writeSessionsAsync();
1234             if (mOkToSendBroadcasts) {
1235                 mPm.sendSessionUpdatedBroadcast(session.generateInfo(false),
1236                         session.userId);
1237             }
1238         }
1239 
onSessionFinished(final PackageInstallerSession session, boolean success)1240         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
1241             if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
1242                 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
1243             }
1244 
1245             mInstallHandler.post(new Runnable() {
1246                 @Override
1247                 public void run() {
1248                     if (session.isStaged()) {
1249                         if (!success) {
1250                             mStagingManager.abortSession(session);
1251                         }
1252                     }
1253                     synchronized (mSessions) {
1254                         if (!session.isStaged() || !success) {
1255                             mSessions.remove(session.sessionId);
1256                         }
1257                         addHistoricalSessionLocked(session);
1258 
1259                         final File appIconFile = buildAppIconFile(session.sessionId);
1260                         if (appIconFile.exists()) {
1261                             appIconFile.delete();
1262                         }
1263 
1264                         writeSessionsLocked();
1265                     }
1266                 }
1267             });
1268         }
1269 
onSessionPrepared(PackageInstallerSession session)1270         public void onSessionPrepared(PackageInstallerSession session) {
1271             // We prepared the destination to write into; we want to persist
1272             // this, but it's not critical enough to block for.
1273             writeSessionsAsync();
1274         }
1275 
onSessionSealedBlocking(PackageInstallerSession session)1276         public void onSessionSealedBlocking(PackageInstallerSession session) {
1277             // It's very important that we block until we've recorded the
1278             // session as being sealed, since we never want to allow mutation
1279             // after sealing.
1280             synchronized (mSessions) {
1281                 writeSessionsLocked();
1282             }
1283         }
1284     }
1285 }
1286