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