Home | History | Annotate | Download | only in pm
      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 
    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 
    246     public void systemReady() {
    247         mAppOps = mContext.getSystemService(AppOpsManager.class);
    248     }
    249 
    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 
    270     public void onPrivateVolumeMounted(String volumeUuid) {
    271         synchronized (mSessions) {
    272             reconcileStagesLocked(volumeUuid, false /*isEphemeral*/);
    273         }
    274     }
    275 
    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 
    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
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    553     private File buildAppIconFile(int sessionId) {
    554         return new File(mSessionsDir, "app_icon." + sessionId + ".png");
    555     }
    556 
    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
    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 
    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
    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
    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
    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
    752     public IPackageInstallerSession openSession(int sessionId) {
    753         try {
    754             return openSessionInternal(sessionId);
    755         } catch (IOException e) {
    756             throw ExceptionUtils.wrap(e);
    757         }
    758     }
    759 
    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 
    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 
    785     private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
    786         if (isEphemeral) {
    787             return Environment.getDataAppEphemeralDirectory(volumeUuid);
    788         }
    789         return Environment.getDataAppDirectory(volumeUuid);
    790     }
    791 
    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 
    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 
    815     private String buildExternalStageCid(int sessionId) {
    816         return "smdl" + sessionId + ".tmp";
    817     }
    818 
    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
    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
    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
    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
    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         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
    874             mAppOps.checkPackage(callingUid, callerPackageName);
    875         }
    876 
    877         // Check whether the caller is device owner, in which case we do it silently.
    878         DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
    879                 Context.DEVICE_POLICY_SERVICE);
    880         boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
    881                 callerPackageName);
    882 
    883         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
    884                 statusReceiver, packageName, isDeviceOwner, userId);
    885         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
    886                     == PackageManager.PERMISSION_GRANTED) {
    887             // Sweet, call straight through!
    888             mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
    889         } else if (isDeviceOwner) {
    890             // Allow the DeviceOwner to silently delete packages
    891             // Need to clear the calling identity to get DELETE_PACKAGES permission
    892             long ident = Binder.clearCallingIdentity();
    893             try {
    894                 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
    895             } finally {
    896                 Binder.restoreCallingIdentity(ident);
    897             }
    898         } else {
    899             // Take a short detour to confirm with user
    900             final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
    901             intent.setData(Uri.fromParts("package", packageName, null));
    902             intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
    903             adapter.onUserActionRequired(intent);
    904         }
    905     }
    906 
    907     @Override
    908     public void setPermissionsResult(int sessionId, boolean accepted) {
    909         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
    910 
    911         synchronized (mSessions) {
    912             PackageInstallerSession session = mSessions.get(sessionId);
    913             if (session != null) {
    914                 session.setPermissionsResult(accepted);
    915             }
    916         }
    917     }
    918 
    919     @Override
    920     public void registerCallback(IPackageInstallerCallback callback, int userId) {
    921         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
    922         mCallbacks.register(callback, userId);
    923     }
    924 
    925     @Override
    926     public void unregisterCallback(IPackageInstallerCallback callback) {
    927         mCallbacks.unregister(callback);
    928     }
    929 
    930     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
    931             int installerUid) {
    932         int count = 0;
    933         final int size = sessions.size();
    934         for (int i = 0; i < size; i++) {
    935             final PackageInstallerSession session = sessions.valueAt(i);
    936             if (session.installerUid == installerUid) {
    937                 count++;
    938             }
    939         }
    940         return count;
    941     }
    942 
    943     private boolean isCallingUidOwner(PackageInstallerSession session) {
    944         final int callingUid = Binder.getCallingUid();
    945         if (callingUid == Process.ROOT_UID) {
    946             return true;
    947         } else {
    948             return (session != null) && (callingUid == session.installerUid);
    949         }
    950     }
    951 
    952     static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
    953         private final Context mContext;
    954         private final IntentSender mTarget;
    955         private final String mPackageName;
    956         private final Notification mNotification;
    957 
    958         public PackageDeleteObserverAdapter(Context context, IntentSender target,
    959                 String packageName, boolean showNotification, int userId) {
    960             mContext = context;
    961             mTarget = target;
    962             mPackageName = packageName;
    963             if (showNotification) {
    964                 mNotification = buildSuccessNotification(mContext,
    965                         mContext.getResources().getString(R.string.package_deleted_device_owner),
    966                         packageName,
    967                         userId);
    968             } else {
    969                 mNotification = null;
    970             }
    971         }
    972 
    973         @Override
    974         public void onUserActionRequired(Intent intent) {
    975             final Intent fillIn = new Intent();
    976             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
    977             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
    978                     PackageInstaller.STATUS_PENDING_USER_ACTION);
    979             fillIn.putExtra(Intent.EXTRA_INTENT, intent);
    980             try {
    981                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
    982             } catch (SendIntentException ignored) {
    983             }
    984         }
    985 
    986         @Override
    987         public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
    988             if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
    989                 NotificationManager notificationManager = (NotificationManager)
    990                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    991                 notificationManager.notify(basePackageName, 0, mNotification);
    992             }
    993             final Intent fillIn = new Intent();
    994             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
    995             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
    996                     PackageManager.deleteStatusToPublicStatus(returnCode));
    997             fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
    998                     PackageManager.deleteStatusToString(returnCode, msg));
    999             fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
   1000             try {
   1001                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
   1002             } catch (SendIntentException ignored) {
   1003             }
   1004         }
   1005     }
   1006 
   1007     static class PackageInstallObserverAdapter extends PackageInstallObserver {
   1008         private final Context mContext;
   1009         private final IntentSender mTarget;
   1010         private final int mSessionId;
   1011         private final boolean mShowNotification;
   1012         private final int mUserId;
   1013 
   1014         public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
   1015                 boolean showNotification, int userId) {
   1016             mContext = context;
   1017             mTarget = target;
   1018             mSessionId = sessionId;
   1019             mShowNotification = showNotification;
   1020             mUserId = userId;
   1021         }
   1022 
   1023         @Override
   1024         public void onUserActionRequired(Intent intent) {
   1025             final Intent fillIn = new Intent();
   1026             fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
   1027             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
   1028                     PackageInstaller.STATUS_PENDING_USER_ACTION);
   1029             fillIn.putExtra(Intent.EXTRA_INTENT, intent);
   1030             try {
   1031                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
   1032             } catch (SendIntentException ignored) {
   1033             }
   1034         }
   1035 
   1036         @Override
   1037         public void onPackageInstalled(String basePackageName, int returnCode, String msg,
   1038                 Bundle extras) {
   1039             if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
   1040                 boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
   1041                 Notification notification = buildSuccessNotification(mContext,
   1042                         mContext.getResources()
   1043                                 .getString(update ? R.string.package_updated_device_owner :
   1044                                         R.string.package_installed_device_owner),
   1045                         basePackageName,
   1046                         mUserId);
   1047                 if (notification != null) {
   1048                     NotificationManager notificationManager = (NotificationManager)
   1049                             mContext.getSystemService(Context.NOTIFICATION_SERVICE);
   1050                     notificationManager.notify(basePackageName, 0, notification);
   1051                 }
   1052             }
   1053             final Intent fillIn = new Intent();
   1054             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
   1055             fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
   1056             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
   1057                     PackageManager.installStatusToPublicStatus(returnCode));
   1058             fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
   1059                     PackageManager.installStatusToString(returnCode, msg));
   1060             fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
   1061             if (extras != null) {
   1062                 final String existing = extras.getString(
   1063                         PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
   1064                 if (!TextUtils.isEmpty(existing)) {
   1065                     fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
   1066                 }
   1067             }
   1068             try {
   1069                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
   1070             } catch (SendIntentException ignored) {
   1071             }
   1072         }
   1073     }
   1074 
   1075     /**
   1076      * Build a notification for package installation / deletion by device owners that is shown if
   1077      * the operation succeeds.
   1078      */
   1079     private static Notification buildSuccessNotification(Context context, String contentText,
   1080             String basePackageName, int userId) {
   1081         PackageInfo packageInfo = null;
   1082         try {
   1083             packageInfo = AppGlobals.getPackageManager().getPackageInfo(
   1084                     basePackageName, 0, userId);
   1085         } catch (RemoteException ignored) {
   1086         }
   1087         if (packageInfo == null || packageInfo.applicationInfo == null) {
   1088             Slog.w(TAG, "Notification not built for package: " + basePackageName);
   1089             return null;
   1090         }
   1091         PackageManager pm = context.getPackageManager();
   1092         Bitmap packageIcon = ImageUtils.buildScaledBitmap(
   1093                 packageInfo.applicationInfo.loadIcon(pm),
   1094                 context.getResources().getDimensionPixelSize(
   1095                         android.R.dimen.notification_large_icon_width),
   1096                 context.getResources().getDimensionPixelSize(
   1097                         android.R.dimen.notification_large_icon_height));
   1098         CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
   1099         return new Notification.Builder(context)
   1100                 .setSmallIcon(R.drawable.ic_check_circle_24px)
   1101                 .setColor(context.getResources().getColor(
   1102                         R.color.system_notification_accent_color))
   1103                 .setContentTitle(packageLabel)
   1104                 .setContentText(contentText)
   1105                 .setStyle(new Notification.BigTextStyle().bigText(contentText))
   1106                 .setLargeIcon(packageIcon)
   1107                 .build();
   1108     }
   1109 
   1110     public static <E> ArraySet<E> newArraySet(E... elements) {
   1111         final ArraySet<E> set = new ArraySet<E>();
   1112         if (elements != null) {
   1113             set.ensureCapacity(elements.length);
   1114             Collections.addAll(set, elements);
   1115         }
   1116         return set;
   1117     }
   1118 
   1119     private static class Callbacks extends Handler {
   1120         private static final int MSG_SESSION_CREATED = 1;
   1121         private static final int MSG_SESSION_BADGING_CHANGED = 2;
   1122         private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
   1123         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
   1124         private static final int MSG_SESSION_FINISHED = 5;
   1125 
   1126         private final RemoteCallbackList<IPackageInstallerCallback>
   1127                 mCallbacks = new RemoteCallbackList<>();
   1128 
   1129         public Callbacks(Looper looper) {
   1130             super(looper);
   1131         }
   1132 
   1133         public void register(IPackageInstallerCallback callback, int userId) {
   1134             mCallbacks.register(callback, new UserHandle(userId));
   1135         }
   1136 
   1137         public void unregister(IPackageInstallerCallback callback) {
   1138             mCallbacks.unregister(callback);
   1139         }
   1140 
   1141         @Override
   1142         public void handleMessage(Message msg) {
   1143             final int userId = msg.arg2;
   1144             final int n = mCallbacks.beginBroadcast();
   1145             for (int i = 0; i < n; i++) {
   1146                 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
   1147                 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
   1148                 // TODO: dispatch notifications for slave profiles
   1149                 if (userId == user.getIdentifier()) {
   1150                     try {
   1151                         invokeCallback(callback, msg);
   1152                     } catch (RemoteException ignored) {
   1153                     }
   1154                 }
   1155             }
   1156             mCallbacks.finishBroadcast();
   1157         }
   1158 
   1159         private void invokeCallback(IPackageInstallerCallback callback, Message msg)
   1160                 throws RemoteException {
   1161             final int sessionId = msg.arg1;
   1162             switch (msg.what) {
   1163                 case MSG_SESSION_CREATED:
   1164                     callback.onSessionCreated(sessionId);
   1165                     break;
   1166                 case MSG_SESSION_BADGING_CHANGED:
   1167                     callback.onSessionBadgingChanged(sessionId);
   1168                     break;
   1169                 case MSG_SESSION_ACTIVE_CHANGED:
   1170                     callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
   1171                     break;
   1172                 case MSG_SESSION_PROGRESS_CHANGED:
   1173                     callback.onSessionProgressChanged(sessionId, (float) msg.obj);
   1174                     break;
   1175                 case MSG_SESSION_FINISHED:
   1176                     callback.onSessionFinished(sessionId, (boolean) msg.obj);
   1177                     break;
   1178             }
   1179         }
   1180 
   1181         private void notifySessionCreated(int sessionId, int userId) {
   1182             obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
   1183         }
   1184 
   1185         private void notifySessionBadgingChanged(int sessionId, int userId) {
   1186             obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
   1187         }
   1188 
   1189         private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
   1190             obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
   1191         }
   1192 
   1193         private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
   1194             obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
   1195         }
   1196 
   1197         public void notifySessionFinished(int sessionId, int userId, boolean success) {
   1198             obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
   1199         }
   1200     }
   1201 
   1202     void dump(IndentingPrintWriter pw) {
   1203         synchronized (mSessions) {
   1204             pw.println("Active install sessions:");
   1205             pw.increaseIndent();
   1206             int N = mSessions.size();
   1207             for (int i = 0; i < N; i++) {
   1208                 final PackageInstallerSession session = mSessions.valueAt(i);
   1209                 session.dump(pw);
   1210                 pw.println();
   1211             }
   1212             pw.println();
   1213             pw.decreaseIndent();
   1214 
   1215             pw.println("Historical install sessions:");
   1216             pw.increaseIndent();
   1217             N = mHistoricalSessions.size();
   1218             for (int i = 0; i < N; i++) {
   1219                 final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
   1220                 session.dump(pw);
   1221                 pw.println();
   1222             }
   1223             pw.println();
   1224             pw.decreaseIndent();
   1225 
   1226             pw.println("Legacy install sessions:");
   1227             pw.increaseIndent();
   1228             pw.println(mLegacySessions.toString());
   1229             pw.decreaseIndent();
   1230         }
   1231     }
   1232 
   1233     class InternalCallback {
   1234         public void onSessionBadgingChanged(PackageInstallerSession session) {
   1235             mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
   1236             writeSessionsAsync();
   1237         }
   1238 
   1239         public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
   1240             mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
   1241         }
   1242 
   1243         public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
   1244             mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
   1245         }
   1246 
   1247         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
   1248             mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
   1249 
   1250             mInstallHandler.post(new Runnable() {
   1251                 @Override
   1252                 public void run() {
   1253                     synchronized (mSessions) {
   1254                         mSessions.remove(session.sessionId);
   1255                         mHistoricalSessions.put(session.sessionId, session);
   1256 
   1257                         final File appIconFile = buildAppIconFile(session.sessionId);
   1258                         if (appIconFile.exists()) {
   1259                             appIconFile.delete();
   1260                         }
   1261 
   1262                         writeSessionsLocked();
   1263                     }
   1264                 }
   1265             });
   1266         }
   1267 
   1268         public void onSessionPrepared(PackageInstallerSession session) {
   1269             // We prepared the destination to write into; we want to persist
   1270             // this, but it's not critical enough to block for.
   1271             writeSessionsAsync();
   1272         }
   1273 
   1274         public void onSessionSealedBlocking(PackageInstallerSession session) {
   1275             // It's very important that we block until we've recorded the
   1276             // session as being sealed, since we never want to allow mutation
   1277             // after sealing.
   1278             synchronized (mSessions) {
   1279                 writeSessionsLocked();
   1280             }
   1281         }
   1282     }
   1283 }
   1284