Use the new loader to show app data usage details.

- this is for showing the detail usage (total, background, and
foreground) for a specific app for each billing cycle.

Bug: 111751694
Test: make RunSettingsRoboTests
Change-Id: I8e02872a4204b682089ea117811b50966e785c55
This commit is contained in:
Doris Ling
2018-10-02 14:54:21 -07:00
parent 4b2a053bed
commit 3c5850ee62
2 changed files with 89 additions and 58 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2018 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at * except in compliance with the License. You may obtain a copy of the License at
@@ -22,13 +22,8 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate; import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.IconDrawableFactory; import android.util.IconDrawableFactory;
@@ -51,11 +46,13 @@ import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.net.ChartData; import com.android.settingslib.net.NetworkCycleDataForUid;
import com.android.settingslib.net.ChartDataLoaderCompat; import com.android.settingslib.net.NetworkCycleDataForUidLoader;
import com.android.settingslib.net.UidDetail; import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider; import com.android.settingslib.net.UidDetailProvider;
import java.util.List;
public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenceChangeListener, public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenceChangeListener,
DataSaverBackend.Listener { DataSaverBackend.Listener {
@@ -73,7 +70,7 @@ public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenc
private static final String KEY_CYCLE = "cycle"; private static final String KEY_CYCLE = "cycle";
private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver"; private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver";
private static final int LOADER_CHART_DATA = 2; private static final int LOADER_APP_USAGE_DATA = 2;
private static final int LOADER_APP_PREF = 3; private static final int LOADER_APP_PREF = 3;
private PackageManager mPackageManager; private PackageManager mPackageManager;
@@ -88,14 +85,10 @@ public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenc
private Drawable mIcon; private Drawable mIcon;
private CharSequence mLabel; private CharSequence mLabel;
private String mPackageName; private String mPackageName;
private INetworkStatsSession mStatsSession;
private CycleAdapter mCycleAdapter; private CycleAdapter mCycleAdapter;
private long mStart; private List<NetworkCycleDataForUid> mUsageData;
private long mEnd;
private ChartData mChartData;
private NetworkTemplate mTemplate; private NetworkTemplate mTemplate;
private NetworkPolicy mPolicy;
private AppItem mAppItem; private AppItem mAppItem;
private Intent mAppSettingsIntent; private Intent mAppSettingsIntent;
private SpinnerPreference mCycle; private SpinnerPreference mCycle;
@@ -108,12 +101,6 @@ public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenc
mPackageManager = getPackageManager(); mPackageManager = getPackageManager();
final Bundle args = getArguments(); final Bundle args = getArguments();
try {
mStatsSession = services.mStatsService.openSession();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
mAppItem = (args != null) ? (AppItem) args.getParcelable(ARG_APP_ITEM) : null; mAppItem = (args != null) ? (AppItem) args.getParcelable(ARG_APP_ITEM) : null;
mTemplate = (args != null) ? (NetworkTemplate) args.getParcelable(ARG_NETWORK_TEMPLATE) mTemplate = (args != null) ? (NetworkTemplate) args.getParcelable(ARG_NETWORK_TEMPLATE)
: null; : null;
@@ -208,21 +195,13 @@ public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenc
} }
} }
@Override
public void onDestroy() {
TrafficStats.closeQuietly(mStatsSession);
super.onDestroy();
}
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (mDataSaverBackend != null) { if (mDataSaverBackend != null) {
mDataSaverBackend.addListener(this); mDataSaverBackend.addListener(this);
} }
mPolicy = services.mPolicyEditor.getPolicy(mTemplate); getLoaderManager().restartLoader(LOADER_APP_USAGE_DATA, null /* args */, mUidDataCallbacks);
getLoaderManager().restartLoader(LOADER_CHART_DATA,
ChartDataLoaderCompat.buildArgs(mTemplate, mAppItem), mChartDataCallbacks);
updatePrefs(); updatePrefs();
} }
@@ -300,19 +279,17 @@ public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenc
} }
} }
private void bindData() { @VisibleForTesting
void bindData(int position) {
final long backgroundBytes, foregroundBytes; final long backgroundBytes, foregroundBytes;
if (mChartData == null || mStart == 0) { if (mUsageData == null || position >= mUsageData.size()) {
backgroundBytes = foregroundBytes = 0; backgroundBytes = foregroundBytes = 0;
mCycle.setVisible(false); mCycle.setVisible(false);
} else { } else {
mCycle.setVisible(true); mCycle.setVisible(true);
final long now = System.currentTimeMillis(); final NetworkCycleDataForUid data = mUsageData.get(position);
NetworkStatsHistory.Entry entry = null; backgroundBytes = data.getBackgroudUsage();
entry = mChartData.detailDefault.getValues(mStart, mEnd, now, entry); foregroundBytes = data.getForegroudUsage();
backgroundBytes = entry.rxBytes + entry.txBytes;
entry = mChartData.detailForeground.getValues(mStart, mEnd, now, entry);
foregroundBytes = entry.rxBytes + entry.txBytes;
} }
final long totalBytes = backgroundBytes + foregroundBytes; final long totalBytes = backgroundBytes + foregroundBytes;
final Context context = getContext(); final Context context = getContext();
@@ -376,11 +353,7 @@ public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenc
new AdapterView.OnItemSelectedListener() { new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
final CycleAdapter.CycleItem cycle = (CycleAdapter.CycleItem) mCycle.getSelectedItem(); bindData(position);
mStart = cycle.start;
mEnd = cycle.end;
bindData();
} }
@Override @Override
@@ -389,24 +362,30 @@ public class AppDataUsageV2 extends DataUsageBaseFragment implements OnPreferenc
} }
}; };
private final LoaderManager.LoaderCallbacks<ChartData> mChartDataCallbacks = private final LoaderManager.LoaderCallbacks<List<NetworkCycleDataForUid>> mUidDataCallbacks =
new LoaderManager.LoaderCallbacks<ChartData>() { new LoaderManager.LoaderCallbacks<List<NetworkCycleDataForUid>>() {
@Override @Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) { public Loader<List<NetworkCycleDataForUid>> onCreateLoader(int id, Bundle args) {
return new ChartDataLoaderCompat(getActivity(), mStatsSession, args); return NetworkCycleDataForUidLoader.builder(getContext())
} .setUid(mAppItem.key)
.setRetrieveDetail(true)
.setNetworkTemplate(mTemplate)
.setSubscriberId(mTemplate.getSubscriberId())
.build();
}
@Override @Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) { public void onLoadFinished(Loader<List<NetworkCycleDataForUid>> loader,
mChartData = data; List<NetworkCycleDataForUid> data) {
mCycleAdapter.updateCycleList(mPolicy, mChartData); mUsageData = data;
bindData(); mCycleAdapter.updateCycleList(data);
} bindData(0 /* position */);
}
@Override @Override
public void onLoaderReset(Loader<ChartData> loader) { public void onLoaderReset(Loader<List<NetworkCycleDataForUid>> loader) {
} }
}; };
private final LoaderManager.LoaderCallbacks<ArraySet<Preference>> mAppPrefCallbacks = private final LoaderManager.LoaderCallbacks<ArraySet<Preference>> mAppPrefCallbacks =
new LoaderManager.LoaderCallbacks<ArraySet<Preference>>() { new LoaderManager.LoaderCallbacks<ArraySet<Preference>>() {

View File

@@ -19,6 +19,7 @@ package com.android.settings.datausage;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
@@ -28,12 +29,14 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.NetworkPolicyManager; import android.net.NetworkPolicyManager;
import android.os.Bundle; import android.os.Bundle;
import android.util.ArraySet; import android.util.ArraySet;
import android.view.View; import android.view.View;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
@@ -45,6 +48,7 @@ import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.AppItem; import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.net.NetworkCycleDataForUid;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@@ -57,6 +61,9 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers; import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {ShadowEntityHeaderController.class, ShadowRestrictedLockUtilsInternal.class}) @Config(shadows = {ShadowEntityHeaderController.class, ShadowRestrictedLockUtilsInternal.class})
public class AppDataUsageV2Test { public class AppDataUsageV2Test {
@@ -172,4 +179,49 @@ public class AppDataUsageV2Test {
verify(restrictBackgroundPref).setDisabledByAdmin(any(EnforcedAdmin.class)); verify(restrictBackgroundPref).setDisabledByAdmin(any(EnforcedAdmin.class));
verify(unrestrictedDataPref).setDisabledByAdmin(any(EnforcedAdmin.class)); verify(unrestrictedDataPref).setDisabledByAdmin(any(EnforcedAdmin.class));
} }
@Test
public void bindData_noAppUsageData_shouldHideCycleSpinner() {
mFragment = spy(new AppDataUsageV2());
final SpinnerPreference cycle = mock(SpinnerPreference.class);
ReflectionHelpers.setField(mFragment, "mCycle", cycle);
final Preference preference = mock(Preference.class);
ReflectionHelpers.setField(mFragment, "mBackgroundUsage", preference);
ReflectionHelpers.setField(mFragment, "mForegroundUsage", preference);
ReflectionHelpers.setField(mFragment, "mTotalUsage", preference);
doReturn(RuntimeEnvironment.application).when(mFragment).getContext();
mFragment.bindData(0 /* position */);
verify(cycle).setVisible(false);
}
@Test
public void bindData_hasAppUsageData_shouldShowCycleSpinnerAndUpdateUsageSummary() {
mFragment = spy(new AppDataUsageV2());
final Context context = RuntimeEnvironment.application;
doReturn(context).when(mFragment).getContext();
final long backgroundBytes = 1234L;
final long foregroundBytes = 5678L;
final List<NetworkCycleDataForUid> appUsage = new ArrayList<>();
appUsage.add(new NetworkCycleDataForUid.Builder()
.setBackgroundUsage(backgroundBytes).setForegroundUsage(foregroundBytes).build());
ReflectionHelpers.setField(mFragment, "mUsageData", appUsage);
final Preference backgroundPref = mock(Preference.class);
ReflectionHelpers.setField(mFragment, "mBackgroundUsage", backgroundPref);
final Preference foregroundPref = mock(Preference.class);
ReflectionHelpers.setField(mFragment, "mForegroundUsage", foregroundPref);
final Preference totalPref = mock(Preference.class);
ReflectionHelpers.setField(mFragment, "mTotalUsage", totalPref);
final SpinnerPreference cycle = mock(SpinnerPreference.class);
ReflectionHelpers.setField(mFragment, "mCycle", cycle);
mFragment.bindData(0 /* position */);
verify(cycle).setVisible(true);
verify(totalPref).setSummary(
DataUsageUtils.formatDataUsage(context, backgroundBytes + foregroundBytes));
verify(backgroundPref).setSummary(DataUsageUtils.formatDataUsage(context, backgroundBytes));
verify(foregroundPref).setSummary(DataUsageUtils.formatDataUsage(context, foregroundBytes));
}
} }