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