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