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

Commit 9558cc42 authored by Sumedh Sen's avatar Sumedh Sen
Browse files

Compute app data size in case of fragile user data

If an app has fragile user data, compute the total app data size to suggest user if they want to keep the data while uninstalling.

Bug: 182205982
Test: builds successfully
Test: No CTS Tests. Flag to use new app is turned off by default

Change-Id: I592a24ea85ca4769e239610a9b2de150d89a1499
parent 4de1f637
Loading
Loading
Loading
Loading
+63 −0
Original line number Diff line number Diff line
@@ -28,11 +28,14 @@ import static com.android.packageinstaller.v2.model.uninstallstagedata.Uninstall

import android.Manifest;
import android.app.AppOpsManager;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.UninstallCompleteCallback;
@@ -42,12 +45,14 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.packageinstaller.R;
import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallAborted;
import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallReady;
import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallStage;
import com.android.packageinstaller.v2.model.uninstallstagedata.UninstallUserActionRequired;
import java.io.IOException;
import java.util.List;

public class UninstallRepository {
@@ -244,6 +249,22 @@ public class UninstallRepository {
            uarBuilder.setTitle(mTargetAppLabel.toString());
        }

        boolean suggestToKeepAppData = false;
        try {
            PackageInfo pkgInfo = mPackageManager.getPackageInfo(mTargetPackageName, 0);
            suggestToKeepAppData =
                pkgInfo.applicationInfo != null && pkgInfo.applicationInfo.hasFragileUserData();
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Cannot check hasFragileUserData for " + mTargetPackageName, e);
        }

        long appDataSize = 0;
        if (suggestToKeepAppData) {
            appDataSize = getAppDataSize(mTargetPackageName,
                mUninstallFromAllUsers ? null : mUninstalledUser);
        }
        uarBuilder.setAppDataSize(appDataSize);

        return uarBuilder.build();
    }

@@ -308,6 +329,48 @@ public class UninstallRepository {
        return customUserManager.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE);
    }

    /**
     * Get number of bytes of the app data of the package.
     *
     * @param pkg The package that might have app data.
     * @param user The user the package belongs to or {@code null} if files of all users should
     *     be counted.
     * @return The number of bytes.
     */
    private long getAppDataSize(@NonNull String pkg, @Nullable UserHandle user) {
        if (user != null) {
            return getAppDataSizeForUser(pkg, user);
        }
        // We are uninstalling from all users. Get cumulative app data size for all users.
        List<UserHandle> userHandles = mUserManager.getUserHandles(true);
        long totalAppDataSize = 0;
        int numUsers = userHandles.size();
        for (int i = 0; i < numUsers; i++) {
            totalAppDataSize += getAppDataSizeForUser(pkg, userHandles.get(i));
        }
        return totalAppDataSize;
    }

    /**
     * Get number of bytes of the app data of the package.
     *
     * @param pkg The package that might have app data.
     * @param user The user the package belongs to
     * @return The number of bytes.
     */
    private long getAppDataSizeForUser(@NonNull String pkg, @NonNull UserHandle user) {
        StorageStatsManager storageStatsManager =
            mContext.getSystemService(StorageStatsManager.class);
        try {
            StorageStats stats = storageStatsManager.queryStatsForPackage(
                mPackageManager.getApplicationInfo(pkg, 0).storageUuid, pkg, user);
            return stats.getDataBytes();
        } catch (PackageManager.NameNotFoundException | IOException | SecurityException e) {
            Log.e(TAG, "Cannot determine amount of app data for " + pkg, e);
        }
        return 0;
    }

    public static class CallerInfo {

        private final String mActivityName;
+14 −2
Original line number Diff line number Diff line
@@ -21,10 +21,12 @@ public class UninstallUserActionRequired extends UninstallStage {
    private final int mStage = UninstallStage.STAGE_USER_ACTION_REQUIRED;
    private final String mTitle;
    private final String mMessage;
    private final long mAppDataSize;

    public UninstallUserActionRequired(String title, String message) {
    public UninstallUserActionRequired(String title, String message, long appDataSize) {
        mTitle = title;
        mMessage = message;
        mAppDataSize = appDataSize;
    }

    public String getTitle() {
@@ -35,6 +37,10 @@ public class UninstallUserActionRequired extends UninstallStage {
        return mMessage;
    }

    public long getAppDataSize() {
        return mAppDataSize;
    }

    @Override
    public int getStageCode() {
        return mStage;
@@ -44,6 +50,7 @@ public class UninstallUserActionRequired extends UninstallStage {

        private String mTitle;
        private String mMessage;
        private long mAppDataSize = 0;

        public Builder setTitle(String title) {
            mTitle = title;
@@ -55,8 +62,13 @@ public class UninstallUserActionRequired extends UninstallStage {
            return this;
        }

        public Builder setAppDataSize(long appDataSize) {
            mAppDataSize = appDataSize;
            return this;
        }

        public UninstallUserActionRequired build() {
            return new UninstallUserActionRequired(mTitle, mMessage);
            return new UninstallUserActionRequired(mTitle, mMessage, mAppDataSize);
        }
    }
}