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