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:
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user