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

Commit f935a703 authored by Svet Ganov's avatar Svet Ganov
Browse files

Allow clearing instant app meta-data

The package manager has an API to clear the data of an app
which does not work for uninstalled instant apps for which
we store some meta-data (icon, title, cookie). This change
allows clearing the data of an uninstalled instant app.

Test: Instant cookie CTS tests use this API

bug:64517837

Change-Id: Ia929fead71b5ae786e88ddd0fa8e8a490d970dd0
parent df421a8b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -2907,6 +2907,8 @@ public class ActivityManager {
    /**
     * @hide
     */
    @RequiresPermission(anyOf={Manifest.permission.CLEAR_APP_USER_DATA,
            Manifest.permission.ACCESS_INSTANT_APPS})
    public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
        try {
            return getService().clearApplicationUserData(packageName,
+6 −1
Original line number Diff line number Diff line
@@ -2207,7 +2207,12 @@ public class Intent implements Parcelable, Cloneable {
     * Note that the cleared package does <em>not</em>
     * receive this broadcast. The data contains the name of the package.
     * <ul>
     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. If the
     *      package whose data was cleared is an uninstalled instant app, then the UID
     *      will be -1. The platform keeps some meta-data associated with instant apps
     *      after they are uninstalled.
     * <li> {@link #EXTRA_PACKAGE_NAME} containing the package name only if the cleared
     *      data was for an instant app.
     * </ul>
     *
     * <p class="note">This is a protected intent that can only be sent
+7 −1
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.os.Bundle;
@@ -372,4 +371,11 @@ public abstract class PackageManagerInternal {

    /** Whether the binder caller can access instant apps. */
    public abstract boolean canAccessInstantApps(int callingUid, int userId);

    /**
     * Returns {@code true} if a given package has instant application meta-data.
     * Otherwise, returns {@code false}. Meta-data is state (eg. cookie, app icon, etc)
     * associated with an instant app. It may be kept after the instant app has been uninstalled.
     */
    public abstract boolean hasInstantApplicationMetadata(String packageName, int userId);
}
+69 −40
Original line number Diff line number Diff line
@@ -5842,26 +5842,53 @@ public class ActivityManagerService extends IActivityManager.Stub
        enforceNotIsolatedCaller("clearApplicationUserData");
        int uid = Binder.getCallingUid();
        int pid = Binder.getCallingPid();
        userId = mUserController.handleIncomingUser(pid, uid, userId, false,
        final int resolvedUserId = mUserController.handleIncomingUser(pid, uid, userId, false,
                ALLOW_FULL_ONLY, "clearApplicationUserData", null);
        final ApplicationInfo appInfo;
        final boolean isInstantApp;
        long callingId = Binder.clearCallingIdentity();
        try {
            IPackageManager pm = AppGlobals.getPackageManager();
            int pkgUid = -1;
            synchronized(this) {
                // Instant packages are not protected
                if (getPackageManagerInternalLocked().isPackageDataProtected(
                        userId, packageName)) {
                        resolvedUserId, packageName)) {
                    throw new SecurityException(
                            "Cannot clear data for a protected package: " + packageName);
                }
                ApplicationInfo applicationInfo = null;
                try {
                    pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES, userId);
                    applicationInfo = pm.getApplicationInfo(packageName,
                            MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
                } catch (RemoteException e) {
                    /* ignore */
                }
                if (pkgUid == -1) {
                appInfo = applicationInfo;
                final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid;
                if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA,
                        pid, uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
                    throw new SecurityException("PID " + pid + " does not have permission "
                            + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
                            + " of package " + packageName);
                }
                final boolean hasInstantMetadata = getPackageManagerInternalLocked()
                        .hasInstantApplicationMetadata(packageName, resolvedUserId);
                final boolean isUninstalledAppWithoutInstantMetadata =
                        (appInfo == null && !hasInstantMetadata);
                isInstantApp = (appInfo != null && appInfo.isInstantApp())
                        || hasInstantMetadata;
                final boolean canAccessInstantApps = checkComponentPermission(
                        permission.ACCESS_INSTANT_APPS, pid, uid, -1, true)
                        == PackageManager.PERMISSION_GRANTED;
                if (isUninstalledAppWithoutInstantMetadata || (isInstantApp
                        && !canAccessInstantApps)) {
                    Slog.w(TAG, "Invalid packageName: " + packageName);
                    if (observer != null) {
                        try {
@@ -5872,45 +5899,45 @@ public class ActivityManagerService extends IActivityManager.Stub
                    }
                    return false;
                }
                if (uid == pkgUid || checkComponentPermission(
                        android.Manifest.permission.CLEAR_APP_USER_DATA,
                        pid, uid, -1, true)
                        == PackageManager.PERMISSION_GRANTED) {
                    forceStopPackageLocked(packageName, pkgUid, "clear data");
                } else {
                    throw new SecurityException("PID " + pid + " does not have permission "
                            + android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
                                    + " of package " + packageName);
                }
                if (appInfo != null) {
                    forceStopPackageLocked(packageName, appInfo.uid, "clear data");
                    // Remove all tasks match the cleared application package and user
                    for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
                        final TaskRecord tr = mRecentTasks.get(i);
                        final String taskPackageName =
                                tr.getBaseIntent().getComponent().getPackageName();
                    if (tr.userId != userId) continue;
                        if (tr.userId != resolvedUserId) continue;
                        if (!taskPackageName.equals(packageName)) continue;
                    mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
                        mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
                                REMOVE_FROM_RECENTS);
                    }
                }
            }
            final int pkgUidF = pkgUid;
            final int userIdF = userId;
            final IPackageDataObserver localObserver = new IPackageDataObserver.Stub() {
                @Override
                public void onRemoveCompleted(String packageName, boolean succeeded)
                        throws RemoteException {
                    if (appInfo != null) {
                        synchronized (ActivityManagerService.this) {
                        finishForceStopPackageLocked(packageName, pkgUidF);
                            finishForceStopPackageLocked(packageName, appInfo.uid);
                        }
                    }
                    final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
                            Uri.fromParts("package", packageName, null));
                    intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                    intent.putExtra(Intent.EXTRA_UID, pkgUidF);
                    intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(pkgUidF));
                    broadcastIntentInPackage("android", SYSTEM_UID, intent,
                            null, null, 0, null, null, null, null, false, false, userIdF);
                    intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1);
                    intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
                    if (isInstantApp) {
                        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
                        broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
                                null, null, permission.ACCESS_INSTANT_APPS, null, false, false,
                                resolvedUserId);
                    } else {
                        broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
                                null, null, null, null, false, false, resolvedUserId);
                    }
                    if (observer != null) {
                        observer.onRemoveCompleted(packageName, succeeded);
@@ -5920,16 +5947,18 @@ public class ActivityManagerService extends IActivityManager.Stub
            try {
                // Clear application user data
                pm.clearApplicationUserData(packageName, localObserver, userId);
                pm.clearApplicationUserData(packageName, localObserver, resolvedUserId);
                if (appInfo != null) {
                    synchronized (this) {
                        // Remove all permissions granted from/to this package
                    removeUriPermissionsForPackageLocked(packageName, userId, true);
                        removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true);
                    }
                    // Reset notification settings.
                    INotificationManager inm = NotificationManager.getService();
                inm.clearData(packageName, pkgUidF, uid == pkgUidF);
                    inm.clearData(packageName, appInfo.uid, uid == appInfo.uid);
                }
            } catch (RemoteException e) {
            }
        } finally {
+33 −1
Original line number Diff line number Diff line
@@ -504,6 +504,11 @@ class InstantAppRegistry {
        }
    }

    boolean hasInstantApplicationMetadataLPr(String packageName, int userId) {
        return hasUninstalledInstantAppStateLPr(packageName, userId)
                || hasInstantAppMetadataLPr(packageName, userId);
    }

    public void deleteInstantApplicationMetadataLPw(@NonNull String packageName,
            @UserIdInt int userId) {
        removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
@@ -547,6 +552,33 @@ class InstantAppRegistry {
        }
    }

    private boolean hasUninstalledInstantAppStateLPr(String packageName, @UserIdInt int userId) {
        if (mUninstalledInstantApps == null) {
            return false;
        }
        final List<UninstalledInstantAppState> uninstalledAppStates =
                mUninstalledInstantApps.get(userId);
        if (uninstalledAppStates == null) {
            return false;
        }
        final int appCount = uninstalledAppStates.size();
        for (int i = 0; i < appCount; i++) {
            final UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
            if (packageName.equals(uninstalledAppState.mInstantAppInfo.getPackageName())) {
                return true;
            }
        }
        return false;
    }

    private boolean hasInstantAppMetadataLPr(String packageName, @UserIdInt int userId) {
        final File instantAppDir = getInstantApplicationDir(packageName, userId);
        return new File(instantAppDir, INSTANT_APP_METADATA_FILE).exists()
                || new File(instantAppDir, INSTANT_APP_ICON_FILE).exists()
                || new File(instantAppDir, INSTANT_APP_ANDROID_ID_FILE).exists()
                || peekInstantCookieFile(packageName, userId) != null;
    }

    void pruneInstantApps() {
        final long maxInstalledCacheDuration = Settings.Global.getLong(
                mService.mContext.getContentResolver(),
Loading