blob: 80e13563d74b616d9dc798634300d9eb29f78e2c [file] [log] [blame]
/*
* 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);
}
}
}