FileDocCategorySizeDatePackage
PackageInstallerService.javaAPI DocAndroid 5.1 API44146Thu Mar 12 22:22:42 GMT 2015com.android.server.pm

PackageInstallerService

public class PackageInstallerService extends IPackageInstaller.Stub

Fields Summary
private static final String
TAG
private static final boolean
LOGD
private static final String
TAG_SESSIONS
XML constants used in {@link #mSessionsFile}
private static final String
TAG_SESSION
private static final String
ATTR_SESSION_ID
private static final String
ATTR_USER_ID
private static final String
ATTR_INSTALLER_PACKAGE_NAME
private static final String
ATTR_INSTALLER_UID
private static final String
ATTR_CREATED_MILLIS
private static final String
ATTR_SESSION_STAGE_DIR
private static final String
ATTR_SESSION_STAGE_CID
private static final String
ATTR_PREPARED
private static final String
ATTR_SEALED
private static final String
ATTR_MODE
private static final String
ATTR_INSTALL_FLAGS
private static final String
ATTR_INSTALL_LOCATION
private static final String
ATTR_SIZE_BYTES
private static final String
ATTR_APP_PACKAGE_NAME
private static final String
ATTR_APP_ICON
private static final String
ATTR_APP_LABEL
private static final String
ATTR_ORIGINATING_URI
private static final String
ATTR_REFERRER_URI
private static final String
ATTR_ABI_OVERRIDE
private static final long
MAX_AGE_MILLIS
Automatically destroy sessions older than this
private static final long
MAX_ACTIVE_SESSIONS
Upper bound on number of active sessions for a UID
private static final long
MAX_HISTORICAL_SESSIONS
Upper bound on number of historical sessions for a UID
private final android.content.Context
mContext
private final PackageManagerService
mPm
private final android.app.AppOpsManager
mAppOps
private final File
mStagingDir
private final android.os.HandlerThread
mInstallThread
private final android.os.Handler
mInstallHandler
private final Callbacks
mCallbacks
private final android.util.AtomicFile
mSessionsFile
File storing persisted {@link #mSessions} metadata.
private final File
mSessionsDir
Directory storing persisted {@link #mSessions} metadata which is too heavy to store directly in {@link #mSessionsFile}.
private final InternalCallback
mInternalCallback
private final Random
mRandom
Used for generating session IDs. Since this is created at boot time, normal random might be predictable.
private final android.util.SparseArray
mSessions
private final android.util.SparseArray
mHistoricalSessions
Historical sessions kept around for debugging purposes
private final android.util.SparseBooleanArray
mLegacySessions
Sessions allocated to legacy users
private static final FilenameFilter
sStageFilter
Constructors Summary
public PackageInstallerService(android.content.Context context, PackageManagerService pm, File stagingDir)


           
        mContext = context;
        mPm = pm;
        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);

        mStagingDir = stagingDir;

        mInstallThread = new HandlerThread(TAG);
        mInstallThread.start();

        mInstallHandler = new Handler(mInstallThread.getLooper());

        mCallbacks = new Callbacks(mInstallThread.getLooper());

        mSessionsFile = new AtomicFile(
                new File(Environment.getSystemSecureDirectory(), "install_sessions.xml"));
        mSessionsDir = new File(Environment.getSystemSecureDirectory(), "install_sessions");
        mSessionsDir.mkdirs();

        synchronized (mSessions) {
            readSessionsLocked();

            final ArraySet<File> unclaimedStages = Sets.newArraySet(
                    mStagingDir.listFiles(sStageFilter));
            final ArraySet<File> unclaimedIcons = Sets.newArraySet(
                    mSessionsDir.listFiles());

            // Ignore stages and icons claimed by active sessions
            for (int i = 0; i < mSessions.size(); i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                unclaimedStages.remove(session.stageDir);
                unclaimedIcons.remove(buildAppIconFile(session.sessionId));
            }

            // Clean up orphaned staging directories
            for (File stage : unclaimedStages) {
                Slog.w(TAG, "Deleting orphan stage " + stage);
                if (stage.isDirectory()) {
                    FileUtils.deleteContents(stage);
                }
                stage.delete();
            }

            // Clean up orphaned icons
            for (File icon : unclaimedIcons) {
                Slog.w(TAG, "Deleting orphan icon " + icon);
                icon.delete();
            }
        }
    
Methods Summary
public voidabandonSession(int sessionId)

        synchronized (mSessions) {
            final PackageInstallerSession session = mSessions.get(sessionId);
            if (session == null || !isCallingUidOwner(session)) {
                throw new SecurityException("Caller has no access to session " + sessionId);
            }
            session.abandon();
        }
    
public java.lang.StringallocateExternalStageCidLegacy()

        synchronized (mSessions) {
            final int sessionId = allocateSessionIdLocked();
            mLegacySessions.put(sessionId, true);
            return "smdl" + sessionId + ".tmp";
        }
    
public java.io.FileallocateInternalStageDirLegacy()

        synchronized (mSessions) {
            try {
                final int sessionId = allocateSessionIdLocked();
                mLegacySessions.put(sessionId, true);
                final File stageDir = buildInternalStageDir(sessionId);
                prepareInternalStageDir(stageDir);
                return stageDir;
            } catch (IllegalStateException e) {
                throw new IOException(e);
            }
        }
    
private intallocateSessionIdLocked()

        int n = 0;
        int sessionId;
        do {
            sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
            if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null
                    && !mLegacySessions.get(sessionId, false)) {
                return sessionId;
            }
        } while (n++ < 32);

        throw new IllegalStateException("Failed to allocate session ID");
    
private java.io.FilebuildAppIconFile(int sessionId)

        return new File(mSessionsDir, "app_icon." + sessionId + ".png");
    
private java.lang.StringbuildExternalStageCid(int sessionId)

        return "smdl" + sessionId + ".tmp";
    
private java.io.FilebuildInternalStageDir(int sessionId)

        return new File(mStagingDir, "vmdl" + sessionId + ".tmp");
    
public intcreateSession(android.content.pm.PackageInstaller.SessionParams params, java.lang.String installerPackageName, int userId)

        try {
            return createSessionInternal(params, installerPackageName, userId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    
private intcreateSessionInternal(android.content.pm.PackageInstaller.SessionParams params, java.lang.String installerPackageName, int userId)

        final int callingUid = Binder.getCallingUid();
        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");

        if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            throw new SecurityException("User restriction prevents installing");
        }

        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
            params.installFlags |= PackageManager.INSTALL_FROM_ADB;

        } else {
            mAppOps.checkPackage(callingUid, installerPackageName);

            params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
            params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
            params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
        }

        // Defensively resize giant app icons
        if (params.appIcon != null) {
            final ActivityManager am = (ActivityManager) mContext.getSystemService(
                    Context.ACTIVITY_SERVICE);
            final int iconSize = am.getLauncherLargeIconSize();
            if ((params.appIcon.getWidth() > iconSize * 2)
                    || (params.appIcon.getHeight() > iconSize * 2)) {
                params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
                        true);
            }
        }

        if (params.mode == SessionParams.MODE_FULL_INSTALL
                || params.mode == SessionParams.MODE_INHERIT_EXISTING) {
            // Resolve best location for install, based on combination of
            // requested install flags, delta size, and manifest settings.
            final long ident = Binder.clearCallingIdentity();
            try {
                final int resolved = PackageHelper.resolveInstallLocation(mContext,
                        params.appPackageName, params.installLocation, params.sizeBytes,
                        params.installFlags);

                if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
                    params.setInstallFlagsInternal();
                } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                    params.setInstallFlagsExternal();
                } else {
                    throw new IOException("No storage with enough free space; res=" + resolved);
                }
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        } else {
            throw new IllegalArgumentException("Invalid install mode: " + params.mode);
        }

        final int sessionId;
        final PackageInstallerSession session;
        synchronized (mSessions) {
            // Sanity check that installer isn't going crazy
            final int activeCount = getSessionCount(mSessions, callingUid);
            if (activeCount >= MAX_ACTIVE_SESSIONS) {
                throw new IllegalStateException(
                        "Too many active sessions for UID " + callingUid);
            }
            final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
                throw new IllegalStateException(
                        "Too many historical sessions for UID " + callingUid);
            }

            final long createdMillis = System.currentTimeMillis();
            sessionId = allocateSessionIdLocked();

            // We're staging to exactly one location
            File stageDir = null;
            String stageCid = null;
            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
                stageDir = buildInternalStageDir(sessionId);
            } else {
                stageCid = buildExternalStageCid(sessionId);
            }

            session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
                    mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
                    params, createdMillis, stageDir, stageCid, false, false);
            mSessions.put(sessionId, session);
        }

        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
        writeSessionsAsync();
        return sessionId;
    
voiddump(com.android.internal.util.IndentingPrintWriter pw)

        synchronized (mSessions) {
            pw.println("Active install sessions:");
            pw.increaseIndent();
            int N = mSessions.size();
            for (int i = 0; i < N; i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                session.dump(pw);
                pw.println();
            }
            pw.println();
            pw.decreaseIndent();

            pw.println("Historical install sessions:");
            pw.increaseIndent();
            N = mHistoricalSessions.size();
            for (int i = 0; i < N; i++) {
                final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
                session.dump(pw);
                pw.println();
            }
            pw.println();
            pw.decreaseIndent();

            pw.println("Legacy install sessions:");
            pw.increaseIndent();
            pw.println(mLegacySessions.toString());
            pw.decreaseIndent();
        }
    
public android.content.pm.ParceledListSlicegetAllSessions(int userId)

        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");

        final List<SessionInfo> result = new ArrayList<>();
        synchronized (mSessions) {
            for (int i = 0; i < mSessions.size(); i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                if (session.userId == userId) {
                    result.add(session.generateInfo());
                }
            }
        }
        return new ParceledListSlice<>(result);
    
public android.content.pm.ParceledListSlicegetMySessions(java.lang.String installerPackageName, int userId)

        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
        mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);

        final List<SessionInfo> result = new ArrayList<>();
        synchronized (mSessions) {
            for (int i = 0; i < mSessions.size(); i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                if (Objects.equals(session.installerPackageName, installerPackageName)
                        && session.userId == userId) {
                    result.add(session.generateInfo());
                }
            }
        }
        return new ParceledListSlice<>(result);
    
private static intgetSessionCount(android.util.SparseArray sessions, int installerUid)

        int count = 0;
        final int size = sessions.size();
        for (int i = 0; i < size; i++) {
            final PackageInstallerSession session = sessions.valueAt(i);
            if (session.installerUid == installerUid) {
                count++;
            }
        }
        return count;
    
public android.content.pm.PackageInstaller.SessionInfogetSessionInfo(int sessionId)

        synchronized (mSessions) {
            final PackageInstallerSession session = mSessions.get(sessionId);
            return session != null ? session.generateInfo() : null;
        }
    
private booleanisCallingUidOwner(PackageInstallerSession session)

        final int callingUid = Binder.getCallingUid();
        if (callingUid == Process.ROOT_UID) {
            return true;
        } else {
            return (session != null) && (callingUid == session.installerUid);
        }
    
public static booleanisStageName(java.lang.String name)

        final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
        final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
        final boolean isLegacyContainer = name.startsWith("smdl2tmp");
        return isFile || isContainer || isLegacyContainer;
    
public voidonSecureContainersAvailable()

        synchronized (mSessions) {
            final ArraySet<String> unclaimed = new ArraySet<>();
            for (String cid : PackageHelper.getSecureContainerList()) {
                if (isStageName(cid)) {
                    unclaimed.add(cid);
                }
            }

            // Ignore stages claimed by active sessions
            for (int i = 0; i < mSessions.size(); i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                final String cid = session.stageCid;

                if (unclaimed.remove(cid)) {
                    // Claimed by active session, mount it
                    PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
                            Process.SYSTEM_UID);
                }
            }

            // Clean up orphaned staging containers
            for (String cid : unclaimed) {
                Slog.w(TAG, "Deleting orphan container " + cid);
                PackageHelper.destroySdDir(cid);
            }
        }
    
public android.content.pm.IPackageInstallerSessionopenSession(int sessionId)

        try {
            return openSessionInternal(sessionId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    
private android.content.pm.IPackageInstallerSessionopenSessionInternal(int sessionId)

        synchronized (mSessions) {
            final PackageInstallerSession session = mSessions.get(sessionId);
            if (session == null || !isCallingUidOwner(session)) {
                throw new SecurityException("Caller has no access to session " + sessionId);
            }
            session.open();
            return session;
        }
    
static voidprepareExternalStageCid(java.lang.String stageCid, long sizeBytes)

        if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
                Process.SYSTEM_UID, true) == null) {
            throw new IOException("Failed to create session cid: " + stageCid);
        }
    
static voidprepareInternalStageDir(java.io.File stageDir)

        if (stageDir.exists()) {
            throw new IOException("Session dir already exists: " + stageDir);
        }

        try {
            Os.mkdir(stageDir.getAbsolutePath(), 0755);
            Os.chmod(stageDir.getAbsolutePath(), 0755);
        } catch (ErrnoException e) {
            // This purposefully throws if directory already exists
            throw new IOException("Failed to prepare session dir: " + stageDir, e);
        }

        if (!SELinux.restorecon(stageDir)) {
            throw new IOException("Failed to restorecon session dir: " + stageDir);
        }
    
private PackageInstallerSessionreadSessionLocked(org.xmlpull.v1.XmlPullParser in)

        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
        final int userId = readIntAttribute(in, ATTR_USER_ID);
        final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID,
                mPm.getPackageUid(installerPackageName, userId));
        final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
        final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
        final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
        final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
        final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);

        final SessionParams params = new SessionParams(
                SessionParams.MODE_INVALID);
        params.mode = readIntAttribute(in, ATTR_MODE);
        params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
        params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
        params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
        params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
        params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
        params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
        params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
        params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
        params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);

        final File appIconFile = buildAppIconFile(sessionId);
        if (appIconFile.exists()) {
            params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
            params.appIconLastModified = appIconFile.lastModified();
        }

        return new PackageInstallerSession(mInternalCallback, mContext, mPm,
                mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
                params, createdMillis, stageDir, stageCid, prepared, sealed);
    
private voidreadSessionsLocked()

        if (LOGD) Slog.v(TAG, "readSessionsLocked()");

        mSessions.clear();

        FileInputStream fis = null;
        try {
            fis = mSessionsFile.openRead();
            final XmlPullParser in = Xml.newPullParser();
            in.setInput(fis, null);

            int type;
            while ((type = in.next()) != END_DOCUMENT) {
                if (type == START_TAG) {
                    final String tag = in.getName();
                    if (TAG_SESSION.equals(tag)) {
                        final PackageInstallerSession session = readSessionLocked(in);
                        final long age = System.currentTimeMillis() - session.createdMillis;

                        final boolean valid;
                        if (age >= MAX_AGE_MILLIS) {
                            Slog.w(TAG, "Abandoning old session first created at "
                                    + session.createdMillis);
                            valid = false;
                        } else if (session.stageDir != null
                                && !session.stageDir.exists()) {
                            Slog.w(TAG, "Abandoning internal session with missing stage "
                                    + session.stageDir);
                            valid = false;
                        } else {
                            valid = true;
                        }

                        if (valid) {
                            mSessions.put(session.sessionId, session);
                        } else {
                            // Since this is early during boot we don't send
                            // any observer events about the session, but we
                            // keep details around for dumpsys.
                            mHistoricalSessions.put(session.sessionId, session);
                        }
                    }
                }
            }
        } catch (FileNotFoundException e) {
            // Missing sessions are okay, probably first boot
        } catch (IOException e) {
            Slog.wtf(TAG, "Failed reading install sessions", e);
        } catch (XmlPullParserException e) {
            Slog.wtf(TAG, "Failed reading install sessions", e);
        } finally {
            IoUtils.closeQuietly(fis);
        }
    
public voidregisterCallback(android.content.pm.IPackageInstallerCallback callback, int userId)

        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
        mCallbacks.register(callback, userId);
    
public voidsetPermissionsResult(int sessionId, boolean accepted)

        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);

        synchronized (mSessions) {
            mSessions.get(sessionId).setPermissionsResult(accepted);
        }
    
public voiduninstall(java.lang.String packageName, int flags, android.content.IntentSender statusReceiver, int userId)

        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, true, "uninstall");

        final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
                statusReceiver, packageName);
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
                == PackageManager.PERMISSION_GRANTED) {
            // Sweet, call straight through!
            mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);

        } else {
            // Take a short detour to confirm with user
            final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
            intent.setData(Uri.fromParts("package", packageName, null));
            intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
            adapter.onUserActionRequired(intent);
        }
    
public voidunregisterCallback(android.content.pm.IPackageInstallerCallback callback)

        mCallbacks.unregister(callback);
    
public voidupdateSessionAppIcon(int sessionId, android.graphics.Bitmap appIcon)

        synchronized (mSessions) {
            final PackageInstallerSession session = mSessions.get(sessionId);
            if (session == null || !isCallingUidOwner(session)) {
                throw new SecurityException("Caller has no access to session " + sessionId);
            }

            // Defensively resize giant app icons
            if (appIcon != null) {
                final ActivityManager am = (ActivityManager) mContext.getSystemService(
                        Context.ACTIVITY_SERVICE);
                final int iconSize = am.getLauncherLargeIconSize();
                if ((appIcon.getWidth() > iconSize * 2)
                        || (appIcon.getHeight() > iconSize * 2)) {
                    appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
                }
            }

            session.params.appIcon = appIcon;
            session.params.appIconLastModified = -1;

            mInternalCallback.onSessionBadgingChanged(session);
        }
    
public voidupdateSessionAppLabel(int sessionId, java.lang.String appLabel)

        synchronized (mSessions) {
            final PackageInstallerSession session = mSessions.get(sessionId);
            if (session == null || !isCallingUidOwner(session)) {
                throw new SecurityException("Caller has no access to session " + sessionId);
            }
            session.params.appLabel = appLabel;
            mInternalCallback.onSessionBadgingChanged(session);
        }
    
private voidwriteSessionLocked(org.xmlpull.v1.XmlSerializer out, PackageInstallerSession session)

        final SessionParams params = session.params;

        out.startTag(null, TAG_SESSION);

        writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId);
        writeIntAttribute(out, ATTR_USER_ID, session.userId);
        writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
                session.installerPackageName);
        writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid);
        writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
        if (session.stageDir != null) {
            writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
                    session.stageDir.getAbsolutePath());
        }
        if (session.stageCid != null) {
            writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
        }
        writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
        writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());

        writeIntAttribute(out, ATTR_MODE, params.mode);
        writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
        writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
        writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
        writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
        writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
        writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
        writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
        writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);

        // Persist app icon if changed since last written
        final File appIconFile = buildAppIconFile(session.sessionId);
        if (params.appIcon == null && appIconFile.exists()) {
            appIconFile.delete();
        } else if (params.appIcon != null
                && appIconFile.lastModified() != params.appIconLastModified) {
            if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
            FileOutputStream os = null;
            try {
                os = new FileOutputStream(appIconFile);
                params.appIcon.compress(CompressFormat.PNG, 90, os);
            } catch (IOException e) {
                Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
            } finally {
                IoUtils.closeQuietly(os);
            }

            params.appIconLastModified = appIconFile.lastModified();
        }

        out.endTag(null, TAG_SESSION);
    
private voidwriteSessionsAsync()

        IoThread.getHandler().post(new Runnable() {
            @Override
            public void run() {
                synchronized (mSessions) {
                    writeSessionsLocked();
                }
            }
        });
    
private voidwriteSessionsLocked()

        if (LOGD) Slog.v(TAG, "writeSessionsLocked()");

        FileOutputStream fos = null;
        try {
            fos = mSessionsFile.startWrite();

            XmlSerializer out = new FastXmlSerializer();
            out.setOutput(fos, "utf-8");
            out.startDocument(null, true);
            out.startTag(null, TAG_SESSIONS);
            final int size = mSessions.size();
            for (int i = 0; i < size; i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                writeSessionLocked(out, session);
            }
            out.endTag(null, TAG_SESSIONS);
            out.endDocument();

            mSessionsFile.finishWrite(fos);
        } catch (IOException e) {
            if (fos != null) {
                mSessionsFile.failWrite(fos);
            }
        }