diff options
| author | 2018-09-19 13:25:37 -0700 | |
|---|---|---|
| committer | 2018-09-26 13:28:50 -0700 | |
| commit | 0d8e4b43a3520f1a0b890cfa7f78558c0054461a (patch) | |
| tree | dce8cd760a6d16dbce52081c3e0b50bbe19e8eca | |
| parent | 16cd61459c97d8456d77da18cae947af46f73887 (diff) | |
Add loader for netork cycle data.
- Add the loader to retrieve usage data for each billing cycle.
- Rename NetworkStatsSummaryLoader and change its param from
subscription id to subscriber id.
Bug: 111751694
Test: make RunSettingsLibRoboTests
Change-Id: I0b68a0805222098bd6d52e762887cef2fcea667a
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java | 5 | ||||
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java | 69 | ||||
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java | 219 | ||||
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java (renamed from packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java) | 23 | ||||
| -rw-r--r-- | packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java | 123 |
5 files changed, 425 insertions, 14 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java index c3241bbd2123..74bd97f40ff7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java @@ -36,7 +36,12 @@ import com.android.settingslib.AppItem; /** * Loader for historical chart data for both network and UID details. + * + * Deprecated in favor of {@link NetworkCycleDataLoader} + * + * @deprecated */ +@Deprecated public class ChartDataLoaderCompat extends AsyncTaskLoader<ChartData> { private static final String KEY_TEMPLATE = "template"; private static final String KEY_APP = "app"; diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java new file mode 100644 index 000000000000..2d8c0de42ba4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.net; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Data structure representing usage data in a billing cycle. + */ +public class NetworkCycleData { + public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1); + public long startTime; + public long endTime; + public long totalUsage; + public List<NetworkCycleData> usageBuckets; + + private NetworkCycleData(Builder builder) { + startTime = builder.mStart; + endTime = builder.mEnd; + totalUsage = builder.mTotalUsage; + usageBuckets = builder.mUsageBuckets; + } + + public static class Builder { + private long mStart; + private long mEnd; + private long mTotalUsage; + private List<NetworkCycleData> mUsageBuckets; + + public Builder setStartTime(long start) { + mStart = start; + return this; + } + + public Builder setEndTime(long end) { + mEnd = end; + return this; + } + + public Builder setTotalUsage(long total) { + mTotalUsage = total; + return this; + } + + public Builder setUsageBuckets(List<NetworkCycleData> buckets) { + mUsageBuckets = buckets; + return this; + } + + public NetworkCycleData build() { + return new NetworkCycleData(this); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java new file mode 100644 index 000000000000..80e13563d74b --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.net; + +import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; +import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; + +import android.app.usage.NetworkStats; +import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; +import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.net.TrafficStats; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.text.format.DateUtils; +import android.util.Log; +import android.util.Pair; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.loader.content.AsyncTaskLoader; + +/** + * Loader for network data usage history. It returns a list of usage data per billing cycle. + */ +public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleData>> { + private static final String TAG = "CycleDataSummaryLoader"; + private final NetworkStatsManager mNetworkStatsManager; + private final String mSubId; + private final int mNetworkType; + private final NetworkPolicy mPolicy; + private final NetworkTemplate mNetworkTemplate; + @VisibleForTesting + final INetworkStatsService mNetworkStatsService; + + private NetworkCycleDataLoader(Builder builder) { + super(builder.mContext); + mPolicy = builder.mPolicy; + mSubId = builder.mSubId; + mNetworkType = builder.mNetworkType; + mNetworkTemplate = builder.mNetworkTemplate; + mNetworkStatsManager = (NetworkStatsManager) + builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); + mNetworkStatsService = INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + } + + @Override + protected void onStartLoading() { + super.onStartLoading(); + forceLoad(); + } + + @Override + public List<NetworkCycleData> loadInBackground() { + if (mPolicy == null) { + return loadFourWeeksData(); + } + final List<NetworkCycleData> data = new ArrayList<>(); + final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator = NetworkPolicyManager + .cycleIterator(mPolicy); + while (iterator.hasNext()) { + final Pair<ZonedDateTime, ZonedDateTime> cycle = iterator.next(); + final long cycleStart = cycle.first.toInstant().toEpochMilli(); + final long cycleEnd = cycle.second.toInstant().toEpochMilli(); + getUsage(cycleStart, cycleEnd, data); + } + return data; + } + + @Override + protected void onStopLoading() { + super.onStopLoading(); + cancelLoad(); + } + + @Override + protected void onReset() { + super.onReset(); + cancelLoad(); + } + + @VisibleForTesting + List<NetworkCycleData> loadFourWeeksData() { + final List<NetworkCycleData> data = new ArrayList<>(); + try { + final INetworkStatsSession networkSession = mNetworkStatsService.openSession(); + final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork( + mNetworkTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES); + final long historyStart = networkHistory.getStart(); + final long historyEnd = networkHistory.getEnd(); + + long cycleEnd = historyEnd; + while (cycleEnd > historyStart) { + final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4); + getUsage(cycleStart, cycleEnd, data); + cycleEnd = cycleStart; + } + + TrafficStats.closeQuietly(networkSession); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + return data; + } + + @VisibleForTesting + void getUsage(long start, long end, @NonNull List<NetworkCycleData> data) { + try { + final NetworkStats stats = mNetworkStatsManager.querySummary( + mNetworkType, mSubId, start, end); + final long total = getTotalUsage(stats); + if (total > 0L) { + data.add(new NetworkCycleData.Builder() + .setStartTime(start) + .setEndTime(end) + .setTotalUsage(total) + .setUsageBuckets(getUsageBuckets(start, end)) + .build()); + } + } catch (RemoteException e) { + Log.e(TAG, "Exception querying network detail.", e); + } + } + + private long getTotalUsage(NetworkStats stats) { + long bytes = 0L; + if (stats != null) { + final NetworkStats.Bucket bucket = new NetworkStats.Bucket(); + while (stats.hasNextBucket() && stats.getNextBucket(bucket)) { + bytes += bucket.getRxBytes() + bucket.getTxBytes(); + } + stats.close(); + } + return bytes; + } + + private List<NetworkCycleData> getUsageBuckets(long start, long end) { + final List<NetworkCycleData> data = new ArrayList<>(); + long bucketStart = start; + long bucketEnd = start + NetworkCycleData.BUCKET_DURATION_MS; + while (bucketEnd <= end) { + long usage = 0L; + try { + final NetworkStats stats = mNetworkStatsManager.querySummary( + mNetworkType, mSubId, bucketStart, bucketEnd); + usage = getTotalUsage(stats); + } catch (RemoteException e) { + Log.e(TAG, "Exception querying network detail.", e); + } + data.add(new NetworkCycleData.Builder() + .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build()); + bucketStart = bucketEnd; + bucketEnd += NetworkCycleData.BUCKET_DURATION_MS; + } + return data; + } + + public static class Builder { + private final Context mContext; + private NetworkPolicy mPolicy; + private String mSubId; + private int mNetworkType; + private NetworkTemplate mNetworkTemplate; + + public Builder(Context context) { + mContext = context; + } + + public Builder setNetworkPolicy(NetworkPolicy policy) { + mPolicy = policy; + return this; + } + + public Builder setSubscriberId(String subId) { + mSubId = subId; + return this; + } + + public Builder setNetworkType(int networkType) { + mNetworkType = networkType; + return this; + } + + public Builder setNetworkTemplate(NetworkTemplate template) { + mNetworkTemplate = template; + return this; + } + + public NetworkCycleDataLoader build() { + return new NetworkCycleDataLoader(this); + } + } + +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java index a070b2a5f768..34e6097ea46e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsDetailLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkStatsSummaryLoader.java @@ -20,25 +20,23 @@ import android.app.usage.NetworkStatsManager; import android.app.usage.NetworkStats; import android.content.Context; import android.os.RemoteException; -import android.telephony.TelephonyManager; import android.util.Log; import androidx.loader.content.AsyncTaskLoader; /** - * Loader for retrieving the network stats details for all UIDs. + * Loader for retrieving the network stats summary for all UIDs. */ -public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { +public class NetworkStatsSummaryLoader extends AsyncTaskLoader<NetworkStats> { private static final String TAG = "NetworkDetailLoader"; private final NetworkStatsManager mNetworkStatsManager; - private final TelephonyManager mTelephonyManager; private final long mStart; private final long mEnd; - private final int mSubId; + private final String mSubId; private final int mNetworkType; - private NetworkStatsDetailLoader(Builder builder) { + private NetworkStatsSummaryLoader(Builder builder) { super(builder.mContext); mStart = builder.mStart; mEnd = builder.mEnd; @@ -46,8 +44,6 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { mNetworkType = builder.mNetworkType; mNetworkStatsManager = (NetworkStatsManager) builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); - mTelephonyManager = - (TelephonyManager) builder.mContext.getSystemService(Context.TELEPHONY_SERVICE); } @Override @@ -59,8 +55,7 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { @Override public NetworkStats loadInBackground() { try { - return mNetworkStatsManager.queryDetails( - mNetworkType, mTelephonyManager.getSubscriberId(mSubId), mStart, mEnd); + return mNetworkStatsManager.querySummary(mNetworkType, mSubId, mStart, mEnd); } catch (RemoteException e) { Log.e(TAG, "Exception querying network detail.", e); return null; @@ -83,7 +78,7 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { private final Context mContext; private long mStart; private long mEnd; - private int mSubId; + private String mSubId; private int mNetworkType; public Builder(Context context) { @@ -100,7 +95,7 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { return this; } - public Builder setSubscriptionId(int subId) { + public Builder setSubscriberId(String subId) { mSubId = subId; return this; } @@ -110,8 +105,8 @@ public class NetworkStatsDetailLoader extends AsyncTaskLoader<NetworkStats> { return this; } - public NetworkStatsDetailLoader build() { - return new NetworkStatsDetailLoader(this); + public NetworkStatsSummaryLoader build() { + return new NetworkStatsSummaryLoader(this); } } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java new file mode 100644 index 000000000000..4c4207b23cab --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.net; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Matchers.nullable; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; +import android.net.NetworkPolicy; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.os.RemoteException; +import android.text.format.DateUtils; +import android.util.Range; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.util.ReflectionHelpers; + +import java.time.ZonedDateTime; +import java.util.Iterator; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class NetworkCycleDataLoaderTest { + + @Mock + private NetworkStatsManager mNetworkStatsManager; + @Mock + private Context mContext; + @Mock + private NetworkPolicy mPolicy; + @Mock + private Iterator<Range<ZonedDateTime>> mIterator; + @Mock + private INetworkStatsService mNetworkStatsService; + + private NetworkCycleDataLoader mLoader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE)) + .thenReturn(mNetworkStatsManager); + when(mPolicy.cycleIterator()).thenReturn(mIterator); + } + + @Test + public void loadInBackground_noNetworkPolicy_shouldLoad4WeeksData() { + mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build()); + doReturn(null).when(mLoader).loadFourWeeksData(); + + mLoader.loadInBackground(); + + verify(mLoader).loadFourWeeksData(); + } + + @Test + public void loadInBackground_shouldQueryNetworkSummary() throws RemoteException { + final int networkType = ConnectivityManager.TYPE_MOBILE; + final String subId = "TestSubscriber"; + final ZonedDateTime now = ZonedDateTime.now(); + final Range<ZonedDateTime> cycle = new Range<>(now, now); + // mock 1 cycle data. + // hasNext() will be called internally in next(), hence setting it to return true twice. + when(mIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false); + when(mIterator.next()).thenReturn(cycle); + mLoader = new NetworkCycleDataLoader.Builder(mContext) + .setNetworkPolicy(mPolicy).setNetworkType(networkType).setSubscriberId(subId).build(); + + mLoader.loadInBackground(); + + verify(mNetworkStatsManager).querySummary(eq(networkType), eq(subId), anyLong(), anyLong()); + } + + @Test + public void loadFourWeeksData_shouldGetUsageForLast4Weeks() throws RemoteException { + mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build()); + ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService); + final INetworkStatsSession networkSession = mock(INetworkStatsSession.class); + when(mNetworkStatsService.openSession()).thenReturn(networkSession); + final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class); + when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt())).thenReturn(networkHistory); + final long now = System.currentTimeMillis(); + final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4); + when(networkHistory.getStart()).thenReturn(fourWeeksAgo); + when(networkHistory.getEnd()).thenReturn(now); + + mLoader.loadFourWeeksData(); + + verify(mLoader).getUsage(eq(fourWeeksAgo), eq(now), any()); + } +} |