Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c8aeb6a authored by Songchun Fan's avatar Songchun Fan
Browse files

[pm] delete unfinished sessions when installer is uninstalled

A malicious app can create many new sessions without any permission.
Prior to this CL, those sessions might remain for days even after the
app has been uninstalled. This change makes PackageInstallerService
promptly abandon unfinished sessions as soon as the installer package is
uninstalled.

BUG: 219030339
Test: atest android.content.pm.cts.InstallSessionCleanupTest
Change-Id: I72c4a29e031cb47c16c9a0fbafdf68b75c4d6a55
parent 7605f736
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ final class DeletePackageHelper {
    }

    /**
     *  This method is an internal method that could be get invoked either
     *  This method is an internal method that could be invoked either
     *  to delete an installed package or to clean up a failed installation.
     *  After deleting an installed package, a broadcast is sent to notify any
     *  listeners that the package has been removed. For cleaning up a failed
@@ -146,6 +146,8 @@ final class DeletePackageHelper {
        int[] allUsers;
        final int freezeUser;
        final SparseArray<TempUserState> priorUserStates;

        final boolean isInstallerPackage;
        /** enabled state of the uninstalled application */
        synchronized (mPm.mLock) {
            final Computer computer = mPm.snapshotComputer();
@@ -226,6 +228,8 @@ final class DeletePackageHelper {
                freezeUser = removeUser;
                priorUserStates = null;
            }

            isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName);
        }

        synchronized (mPm.mInstallLock) {
@@ -324,6 +328,12 @@ final class DeletePackageHelper {
            }
        }

        if (res && isInstallerPackage) {
            final PackageInstallerService packageInstallerService =
                    mPm.mInjector.getPackageInstallerService();
            packageInstallerService.onInstallerPackageDeleted(uninstalledPs.getAppId(), removeUser);
        }

        return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
    }

+34 −0
Original line number Diff line number Diff line
@@ -861,6 +861,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
        synchronized (mSessions) {
            mSessions.put(sessionId, session);
        }
        mPm.addInstallerPackageName(session.getInstallSource());

        mCallbacks.notifySessionCreated(session.sessionId, session.userId);

@@ -1735,4 +1736,37 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                .setPackage(sessionInfo.installerPackageName);
        mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
    }

    /**
     * Abandon unfinished sessions if the installer package has been uninstalled.
     * @param installerAppId the app ID of the installer package that has been uninstalled.
     * @param userId the user that has the installer package uninstalled.
     */
    void onInstallerPackageDeleted(int installerAppId, int userId) {
        synchronized (mSessions) {
            for (int i = 0; i < mSessions.size(); i++) {
                final PackageInstallerSession session = mSessions.valueAt(i);
                if (!matchesInstaller(session, installerAppId, userId)) {
                    continue;
                }
                // Find parent session and only abandon parent session if installer matches
                PackageInstallerSession root = !session.hasParentSessionId()
                        ? session : mSessions.get(session.getParentSessionId());
                if (root != null && matchesInstaller(root, installerAppId, userId)
                        && !root.isDestroyed()) {
                    root.abandon();
                }
            }
        }
    }

    private boolean matchesInstaller(PackageInstallerSession session, int installerAppId,
            int userId) {
        final int installerUid = session.getInstallerUid();
        if (installerAppId == UserHandle.USER_ALL) {
            return UserHandle.getAppId(installerUid) == installerAppId;
        } else {
            return UserHandle.getUid(userId, installerAppId) == installerUid;
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -7167,4 +7167,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
    void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) {
        mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers);
    }

    void addInstallerPackageName(InstallSource installSource) {
        synchronized (mLock) {
            mSettings.addInstallerPackageNames(installSource);
        }
    }
}
+6 −2
Original line number Diff line number Diff line
@@ -380,8 +380,8 @@ public final class Settings implements Watchable, Snappable {
    private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> mPackagesSnapshot;

    /**
     * List of packages that were involved in installing other packages, i.e. are listed
     * in at least one app's InstallSource.
     * List of packages that were involved in installing other packages, i.e. packages that created
     * new sessions or are listed in at least one app's InstallSource.
     */
    @Watched
    private final WatchedArraySet<String> mInstallerPackages;
@@ -5923,4 +5923,8 @@ public final class Settings implements Watchable, Snappable {
            }
        }
    }

    boolean isInstallerPackage(@NonNull String packageName) {
        return mInstallerPackages.contains(packageName);
    }
}