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

Commit 1f99d6a4 authored by Zhen Zhang's avatar Zhen Zhang
Browse files

Update summary based on hibernated apps number

Update the summary of "unused apps" based on how many apps are
hibernated or auto revoked.

Test: HibernatedAppsPreferenceControllerTest
Bug: 181172051

Change-Id: I274eb3c0ada3d063937368232f31e52c20287552
parent 1908c535
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.READ_DREAM_STATE" />
    <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION" />
    <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" />

    <application android:label="@string/settings_label"
            android:icon="@drawable/ic_launcher_settings"
+28 −4
Original line number Diff line number Diff line
@@ -20,15 +20,19 @@ import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;

import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;

import android.apphibernation.AppHibernationManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.provider.DeviceConfig;

import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;

import java.util.List;

/**
 * A preference controller handling the logic for updating summary of hibernated apps.
 * TODO(b/181172051): add intent to launch Auto Revoke UI in app_and_notification.xml
 */
public final class HibernatedAppsPreferenceController extends BasePreferenceController {
    private static final String TAG = "HibernatedAppsPrefController";
@@ -39,7 +43,8 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont

    @Override
    public int getAvailabilityStatus() {
        return isHibernationEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
        return isHibernationEnabled() && getNumHibernated() > 0
                ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
    }

    @Override
@@ -50,8 +55,27 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont
    }

    private int getNumHibernated() {
        //TODO(b/181172051): hook into hibernation service to get the number of hibernated apps.
        return 0;
        final PackageManager pm = mContext.getPackageManager();
        final AppHibernationManager ahm = mContext.getSystemService(AppHibernationManager.class);
        final List<String> hibernatedPackages = ahm.getHibernatingPackagesForUser();
        int numHibernated = hibernatedPackages.size();

        // Also need to count packages that are auto revoked but not hibernated.
        final List<PackageInfo> packages = pm.getInstalledPackages(
                PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_PERMISSIONS);
        for (PackageInfo pi : packages) {
            final String packageName = pi.packageName;
            if (!hibernatedPackages.contains(packageName) && pi.requestedPermissions != null) {
                for (String perm : pi.requestedPermissions) {
                    if ((pm.getPermissionFlags(perm, packageName, mContext.getUser())
                            & PackageManager.FLAG_PERMISSION_AUTO_REVOKED) != 0) {
                        numHibernated++;
                        break;
                    }
                }
            }
        }
        return numHibernated;
    }

    private static boolean isHibernationEnabled() {
+3 −3
Original line number Diff line number Diff line
@@ -80,7 +80,8 @@ public final class HibernationSwitchPreferenceController extends AppInfoPreferen

    /**
     * Set the package. And also retrieve details from package manager. Some packages may be
     * exempted from hibernation by default.
     * exempted from hibernation by default. This method should only be called to initialize the
     * controller.
     * @param packageName The name of the package whose hibernation state to be managed.
     */
    void setPackage(@NonNull String packageName) {
@@ -93,8 +94,7 @@ public final class HibernationSwitchPreferenceController extends AppInfoPreferen
                        ? android.os.Build.VERSION_CODES.R
                        : android.os.Build.VERSION_CODES.Q;
        try {
            mPackageUid = packageManager.getPackageUidAsUser(
                    packageName, mContext.getUserId());
            mPackageUid = packageManager.getPackageUid(packageName, /* flags */ 0);
            mIsPackageExemptByDefault = packageManager.getTargetSdkVersion(packageName)
                    <= maxTargetSdkVersionForExemptApps;
            mIsPackageSet = true;
+63 −3
Original line number Diff line number Diff line
@@ -23,9 +23,18 @@ import static com.android.settings.core.BasePreferenceController.AVAILABLE;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.apphibernation.AppHibernationManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.provider.DeviceConfig;

import androidx.test.core.app.ApplicationProvider;
@@ -34,23 +43,40 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Arrays;

/**
 * TODO(b/181172051): test getNumberHibernated() when the API implemented
 */
@RunWith(AndroidJUnit4.class)
public class HibernatedAppsPreferenceControllerTest {

    public static final String HIBERNATED_PACKAGE_NAME = "hibernated_package";
    public static final String AUTO_REVOKED_PACKAGE_NAME = "auto_revoked_package";
    public static final String PERMISSION = "permission";
    @Mock
    PackageManager mPackageManager;
    @Mock
    AppHibernationManager mAppHibernationManager;
    private static final String KEY = "key";
    private Context mContext;
    private HibernatedAppsPreferenceController mController;
    private PackageInfo mHibernatedPackage;
    private PackageInfo mAutoRevokedPackage;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
                "true", false);
        mContext = spy(ApplicationProvider.getApplicationContext());
        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        when(mContext.getSystemService(AppHibernationManager.class))
                .thenReturn(mAppHibernationManager);
        mController = new HibernatedAppsPreferenceController(mContext, KEY);
        mHibernatedPackage =
                getHibernatedPackage(mAppHibernationManager, mPackageManager, mContext);
        mAutoRevokedPackage = getAutoRevokedPackage(mPackageManager, mContext);
    }

    @Test
@@ -60,4 +86,38 @@ public class HibernatedAppsPreferenceControllerTest {

        assertThat((mController).getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
    }

    @Test
    public void getSummary_shouldReturnCorrectly() {
        when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(
                Arrays.asList(mHibernatedPackage, mAutoRevokedPackage, new PackageInfo()));
        when(mContext.getResources()).thenReturn(mock(Resources.class));
        final int totalHibernated = 2;

        mController.getSummary();
        verify(mContext.getResources()).getQuantityString(
                anyInt(), eq(totalHibernated), eq(totalHibernated));
    }

    private static PackageInfo getHibernatedPackage(
            AppHibernationManager apm, PackageManager pm, Context context) {
        final PackageInfo pi = new PackageInfo();
        pi.packageName = HIBERNATED_PACKAGE_NAME;
        pi.requestedPermissions = new String[] {PERMISSION};
        when(apm.getHibernatingPackagesForUser()).thenReturn(Arrays.asList(pi.packageName));
        when(pm.getPermissionFlags(
                pi.requestedPermissions[0], pi.packageName, context.getUser()))
                .thenReturn(PackageManager.FLAG_PERMISSION_AUTO_REVOKED);
        return pi;
    }

    private static PackageInfo getAutoRevokedPackage(PackageManager pm, Context context) {
        final PackageInfo pi = new PackageInfo();
        pi.packageName = AUTO_REVOKED_PACKAGE_NAME;
        pi.requestedPermissions = new String[] {PERMISSION};
        when(pm.getPermissionFlags(
                pi.requestedPermissions[0], pi.packageName, context.getUser()))
                .thenReturn(PackageManager.FLAG_PERMISSION_AUTO_REVOKED);
        return pi;
    }
}