Merge "Move unused apps count calculation to bg thread" into sc-dev am: 603cd6c44c

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/14795269

Change-Id: I690efa597ac55bb601d7bf2608ee3e8b56d6e045
This commit is contained in:
Kevin Han
2021-06-04 23:44:09 +00:00
committed by Automerger Merge Worker
3 changed files with 116 additions and 11 deletions

View File

@@ -69,6 +69,10 @@ public class AppDashboardFragment extends DashboardFragment {
use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
mAppsPreferenceController = use(AppsPreferenceController.class);
mAppsPreferenceController.setFragment(this /* fragment */);
final HibernatedAppsPreferenceController hibernatedAppsPreferenceController =
use(HibernatedAppsPreferenceController.class);
getSettingsLifecycle().addObserver(hibernatedAppsPreferenceController);
}
@Override

View File

@@ -30,40 +30,111 @@ import android.content.pm.PackageManager;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* A preference controller handling the logic for updating summary of hibernated apps.
*/
public final class HibernatedAppsPreferenceController extends BasePreferenceController {
public final class HibernatedAppsPreferenceController extends BasePreferenceController
implements LifecycleObserver {
private static final String TAG = "HibernatedAppsPrefController";
private static final String PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS =
"auto_revoke_unused_threshold_millis2";
private static final long DEFAULT_UNUSED_THRESHOLD_MS = TimeUnit.DAYS.toMillis(90);
private PreferenceScreen mScreen;
private int mUnusedCount = 0;
private boolean mLoadingUnusedApps;
private final Executor mBackgroundExecutor;
private final Executor mMainExecutor;
public HibernatedAppsPreferenceController(Context context, String preferenceKey) {
this(context, preferenceKey, Executors.newSingleThreadExecutor(),
context.getMainExecutor());
}
@VisibleForTesting
HibernatedAppsPreferenceController(Context context, String preferenceKey,
Executor bgExecutor, Executor mainExecutor) {
super(context, preferenceKey);
mBackgroundExecutor = bgExecutor;
mMainExecutor = mainExecutor;
}
@Override
public int getAvailabilityStatus() {
return isHibernationEnabled() && getNumHibernated() > 0
return isHibernationEnabled() && mUnusedCount > 0
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
public CharSequence getSummary() {
final int numHibernated = getNumHibernated();
return mContext.getResources().getQuantityString(
R.plurals.unused_apps_summary, numHibernated, numHibernated);
R.plurals.unused_apps_summary, mUnusedCount, mUnusedCount);
}
private int getNumHibernated() {
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mScreen = screen;
}
/**
* On lifecycle resume event.
*/
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
updatePreference();
}
private void updatePreference() {
if (mScreen == null) {
return;
}
if (!mLoadingUnusedApps) {
loadUnusedCount(unusedCount -> {
mUnusedCount = unusedCount;
mLoadingUnusedApps = false;
mMainExecutor.execute(() -> {
super.displayPreference(mScreen);
Preference pref = mScreen.findPreference(mPreferenceKey);
refreshSummary(pref);
});
});
mLoadingUnusedApps = true;
}
}
/**
* Asynchronously load the count of unused apps.
*
* @param callback callback to call when the number of unused apps is calculated
*/
private void loadUnusedCount(@NonNull UnusedCountLoadedCallback callback) {
mBackgroundExecutor.execute(() -> {
final int unusedCount = getUnusedCount();
callback.onUnusedCountLoaded(unusedCount);
});
}
@WorkerThread
private int getUnusedCount() {
// TODO(b/187465752): Find a way to export this logic from PermissionController module
final PackageManager pm = mContext.getPackageManager();
final AppHibernationManager ahm = mContext.getSystemService(AppHibernationManager.class);
@@ -71,6 +142,7 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont
int numHibernated = hibernatedPackages.size();
// Also need to count packages that are auto revoked but not hibernated.
int numAutoRevoked = 0;
final UsageStatsManager usm = mContext.getSystemService(UsageStatsManager.class);
final long now = System.currentTimeMillis();
final long unusedThreshold = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
@@ -97,17 +169,24 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont
for (String perm : pi.requestedPermissions) {
if ((pm.getPermissionFlags(perm, packageName, mContext.getUser())
& PackageManager.FLAG_PERMISSION_AUTO_REVOKED) != 0) {
numHibernated++;
numAutoRevoked++;
break;
}
}
}
}
return numHibernated;
return numHibernated + numAutoRevoked;
}
private static boolean isHibernationEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED, false);
}
/**
* Callback for when we've determined the number of unused apps.
*/
private interface UnusedCountLoadedCallback {
void onUnusedCountLoaded(int unusedCount);
}
}

View File

@@ -41,9 +41,13 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.os.Looper;
import android.os.RemoteException;
import android.provider.DeviceConfig;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -67,12 +71,16 @@ public class HibernatedAppsPreferenceControllerTest {
AppHibernationManager mAppHibernationManager;
@Mock
IUsageStatsManager mIUsageStatsManager;
PreferenceScreen mPreferenceScreen;
private static final String KEY = "key";
private Context mContext;
private HibernatedAppsPreferenceController mController;
@Before
public void setUp() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
MockitoAnnotations.initMocks(this);
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
"true", false);
@@ -82,7 +90,15 @@ public class HibernatedAppsPreferenceControllerTest {
.thenReturn(mAppHibernationManager);
when(mContext.getSystemService(UsageStatsManager.class)).thenReturn(
new UsageStatsManager(mContext, mIUsageStatsManager));
mController = new HibernatedAppsPreferenceController(mContext, KEY);
PreferenceManager manager = new PreferenceManager(mContext);
mPreferenceScreen = manager.createPreferenceScreen(mContext);
Preference preference = mock(Preference.class);
when(preference.getKey()).thenReturn(KEY);
mPreferenceScreen.addPreference(preference);
mController = new HibernatedAppsPreferenceController(mContext, KEY,
command -> command.run(), command -> command.run());
}
@Test
@@ -100,7 +116,9 @@ public class HibernatedAppsPreferenceControllerTest {
Arrays.asList(hibernatedPkg, new PackageInfo()));
when(mContext.getResources()).thenReturn(mock(Resources.class));
mController.getSummary();
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mContext.getResources()).getQuantityString(anyInt(), eq(1), eq(1));
}
@@ -111,7 +129,9 @@ public class HibernatedAppsPreferenceControllerTest {
Arrays.asList(autoRevokedPkg, new PackageInfo()));
when(mContext.getResources()).thenReturn(mock(Resources.class));
mController.getSummary();
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mContext.getResources()).getQuantityString(anyInt(), eq(1), eq(1));
}
@@ -123,7 +143,9 @@ public class HibernatedAppsPreferenceControllerTest {
Arrays.asList(usedAutoRevokedPkg, new PackageInfo()));
when(mContext.getResources()).thenReturn(mock(Resources.class));
mController.getSummary();
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mContext.getResources()).getQuantityString(anyInt(), eq(0), eq(0));
}