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