1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settingslib.net;
18 
19 import android.annotation.NonNull;
20 import android.app.usage.NetworkStats;
21 import android.app.usage.NetworkStatsManager;
22 import android.content.Context;
23 import android.net.NetworkPolicy;
24 import android.net.NetworkPolicyManager;
25 import android.net.NetworkTemplate;
26 import android.text.format.DateUtils;
27 import android.util.Pair;
28 import android.util.Range;
29 
30 import androidx.annotation.VisibleForTesting;
31 import androidx.loader.content.AsyncTaskLoader;
32 
33 import com.android.settingslib.NetworkPolicyEditor;
34 
35 import java.time.ZonedDateTime;
36 import java.util.ArrayList;
37 import java.util.Iterator;
38 
39 /**
40  * Loader for network data usage history. It returns a list of usage data per billing cycle.
41  */
42 public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
43     private static final String TAG = "NetworkCycleDataLoader";
44     protected final NetworkStatsManager mNetworkStatsManager;
45     protected final NetworkTemplate mNetworkTemplate;
46     private final NetworkPolicy mPolicy;
47     private final ArrayList<Long> mCycles;
48 
NetworkCycleDataLoader(Builder<?> builder)49     protected NetworkCycleDataLoader(Builder<?> builder) {
50         super(builder.mContext);
51         mNetworkTemplate = builder.mNetworkTemplate;
52         mCycles = builder.mCycles;
53         mNetworkStatsManager = (NetworkStatsManager)
54             builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
55         final NetworkPolicyEditor policyEditor =
56             new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext));
57         policyEditor.read();
58         mPolicy = policyEditor.getPolicy(mNetworkTemplate);
59     }
60 
61     @Override
onStartLoading()62     protected void onStartLoading() {
63         super.onStartLoading();
64         forceLoad();
65     }
66 
loadInBackground()67     public D loadInBackground() {
68         if (mCycles != null && mCycles.size() > 1) {
69             loadDataForSpecificCycles();
70         } else if (mPolicy == null) {
71             loadFourWeeksData();
72         } else {
73             loadPolicyData();
74         }
75         return getCycleUsage();
76     }
77 
78     @VisibleForTesting
loadPolicyData()79     void loadPolicyData() {
80         final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator =
81             NetworkPolicyManager.cycleIterator(mPolicy);
82         while (iterator.hasNext()) {
83             final Pair<ZonedDateTime, ZonedDateTime> cycle = iterator.next();
84             final long cycleStart = cycle.first.toInstant().toEpochMilli();
85             final long cycleEnd = cycle.second.toInstant().toEpochMilli();
86             recordUsage(cycleStart, cycleEnd);
87         }
88     }
89 
90     @Override
onStopLoading()91     protected void onStopLoading() {
92         super.onStopLoading();
93         cancelLoad();
94     }
95 
96     @Override
onReset()97     protected void onReset() {
98         super.onReset();
99         cancelLoad();
100     }
101 
102     @VisibleForTesting
loadFourWeeksData()103     void loadFourWeeksData() {
104         if (mNetworkTemplate == null) return;
105         final NetworkStats stats = mNetworkStatsManager.queryDetailsForDevice(
106                 mNetworkTemplate, Long.MIN_VALUE, Long.MAX_VALUE);
107         try {
108             final Range<Long> historyTimeRange = getTimeRangeOf(stats);
109 
110             long cycleEnd = historyTimeRange.getUpper();
111             while (cycleEnd > historyTimeRange.getLower()) {
112                 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
113                 recordUsage(cycleStart, cycleEnd);
114                 cycleEnd = cycleStart;
115             }
116         } catch (IllegalArgumentException e) {
117             // Empty history, ignore.
118         }
119     }
120 
121     @VisibleForTesting
loadDataForSpecificCycles()122     void loadDataForSpecificCycles() {
123         long cycleEnd = mCycles.get(0);
124         final int lastCycleIndex = mCycles.size() - 1;
125         for (int i = 1; i <= lastCycleIndex; i++) {
126             final long cycleStart = mCycles.get(i);
127             recordUsage(cycleStart, cycleEnd);
128             cycleEnd = cycleStart;
129         }
130     }
131 
132     @VisibleForTesting
recordUsage(long start, long end)133     abstract void recordUsage(long start, long end);
134 
getCycleUsage()135     abstract D getCycleUsage();
136 
builder(Context context)137     public static Builder<?> builder(Context context) {
138         return new Builder<NetworkCycleDataLoader>(context) {
139             @Override
140             public NetworkCycleDataLoader build() {
141                 return null;
142             }
143         };
144     }
145 
146     protected long getTotalUsage(NetworkStats stats) {
147         long bytes = 0L;
148         if (stats != null) {
149             final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
150             while (stats.hasNextBucket() && stats.getNextBucket(bucket)) {
151                 bytes += bucket.getRxBytes() + bucket.getTxBytes();
152             }
153             stats.close();
154         }
155         return bytes;
156     }
157 
158     @NonNull
159     @VisibleForTesting
160     Range getTimeRangeOf(@NonNull NetworkStats stats) {
161         long start = Long.MAX_VALUE;
162         long end = Long.MIN_VALUE;
163         while (hasNextBucket(stats)) {
164             final NetworkStats.Bucket bucket = getNextBucket(stats);
165             start = Math.min(start, bucket.getStartTimeStamp());
166             end = Math.max(end, bucket.getEndTimeStamp());
167         }
168         return new Range(start, end);
169     }
170 
171     @VisibleForTesting
172     boolean hasNextBucket(@NonNull NetworkStats stats) {
173         return stats.hasNextBucket();
174     }
175 
176     @NonNull
177     @VisibleForTesting
178     NetworkStats.Bucket getNextBucket(@NonNull NetworkStats stats) {
179         NetworkStats.Bucket bucket = new NetworkStats.Bucket();
180         stats.getNextBucket(bucket);
181         return bucket;
182     }
183 
184     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
185     public ArrayList<Long> getCycles() {
186         return mCycles;
187     }
188 
189     public static abstract class Builder<T extends NetworkCycleDataLoader> {
190         private final Context mContext;
191         private NetworkTemplate mNetworkTemplate;
192         private ArrayList<Long> mCycles;
193 
194         public Builder (Context context) {
195             mContext = context;
196         }
197 
198         public Builder<T> setNetworkTemplate(NetworkTemplate template) {
199             mNetworkTemplate = template;
200             return this;
201         }
202 
203         /**
204          * Sets the network cycles to be used to query the usage data.
205          * @param cycles the time slots for the network cycle to be used to query the network usage.
206          * @return the builder
207          */
208         public Builder<T> setCycles(ArrayList<Long> cycles) {
209             mCycles = cycles;
210             return this;
211         }
212 
213         public abstract T build();
214     }
215 
216 }
217