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