xref: /aosp_15_r20/external/sl4a/Common/src/com/googlecode/android_scripting/facade/DataUsageController.java (revision 456ef56af69dcf0481dd36cc45216c4002d72fa3)
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.googlecode.android_scripting.facade;
18 
19 import static android.app.usage.NetworkStats.Bucket.METERED_YES;
20 import static android.net.ConnectivityManager.TYPE_MOBILE;
21 import static android.net.NetworkStats.UID_ALL;
22 import static android.net.NetworkTemplate.MATCH_MOBILE;
23 import static android.net.NetworkTemplate.MATCH_WIFI;
24 import static android.telephony.TelephonyManager.SIM_STATE_READY;
25 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
26 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
27 import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
28 import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
29 
30 import android.app.usage.NetworkStats;
31 import android.app.usage.NetworkStatsManager;
32 import android.content.Context;
33 import android.net.ConnectivityManager;
34 import android.net.NetworkPolicy;
35 import android.net.NetworkPolicyManager;
36 import android.net.NetworkStatsHistory;
37 import android.net.NetworkTemplate;
38 import android.telephony.TelephonyManager;
39 import android.text.format.DateUtils;
40 import android.util.Log;
41 import android.util.Pair;
42 
43 import java.time.ZonedDateTime;
44 import java.util.Locale;
45 import java.util.Set;
46 
47 /**
48  * DataUsageController.
49  */
50 public class DataUsageController {
51 
52     private static final String TAG = "DataUsageController";
53     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
54     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
55     private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
56             PERIOD_BUILDER, Locale.getDefault());
57 
58     private final Context mContext;
59     private final TelephonyManager mTelephonyManager;
60     private final ConnectivityManager mConnectivityManager;
61     private final NetworkPolicyManager mPolicyManager;
62 
63     private Callback mCallback;
64     private NetworkNameProvider mNetworkController;
65 
DataUsageController(Context context)66     public DataUsageController(Context context) {
67         mContext = context;
68         mTelephonyManager = TelephonyManager.from(context);
69         mConnectivityManager = ConnectivityManager.from(context);
70         mPolicyManager = NetworkPolicyManager.from(mContext);
71     }
72 
setNetworkController(NetworkNameProvider networkController)73     public void setNetworkController(NetworkNameProvider networkController) {
74         mNetworkController = networkController;
75     }
76 
setCallback(Callback callback)77     public void setCallback(Callback callback) {
78         mCallback = callback;
79     }
80 
warn(String msg)81     private DataUsageInfo warn(String msg) {
82         Log.w(TAG, "Failed to get data usage, " + msg);
83         return null;
84     }
85 
86     /**
87      * Get mobile data usage info.
88      * @return DataUsageInfo: The Mobile data usage information.
89      */
getMobileDataUsageInfoForSubscriber(String subscriberId)90     public DataUsageInfo getMobileDataUsageInfoForSubscriber(String subscriberId) {
91         if (subscriberId == null) {
92             return warn("no subscriber id");
93         }
94         NetworkTemplate template = new NetworkTemplate.Builder(MATCH_MOBILE)
95                 .setMeteredness(METERED_YES).setSubscriberIds(Set.of(subscriberId)).build();
96         template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds());
97 
98         return getDataUsageInfo(template);
99     }
100 
101     /**
102      * Get mobile data usage info.
103      * @return DataUsageInfo: The Mobile data usage information.
104      */
getMobileDataUsageInfoForUid(Integer uId, String subscriberId)105     public DataUsageInfo getMobileDataUsageInfoForUid(Integer uId, String subscriberId) {
106         if (subscriberId == null) {
107             return warn("no subscriber id");
108         }
109         NetworkTemplate template = new NetworkTemplate.Builder(MATCH_MOBILE)
110                 .setMeteredness(METERED_YES).setSubscriberIds(Set.of(subscriberId)).build();
111         template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds());
112 
113         return getDataUsageInfo(template, uId);
114     }
115 
116     /**
117      * Get wifi data usage info.
118      * @return DataUsageInfo: The Wifi data usage information.
119      */
getWifiDataUsageInfo()120     public DataUsageInfo getWifiDataUsageInfo() {
121         NetworkTemplate template = new NetworkTemplate.Builder(MATCH_WIFI).build();
122         return getDataUsageInfo(template);
123     }
124 
125     /**
126      * Get data usage info for a given template.
127      * @param template A given template.
128      * @return DataUsageInfo: The data usage information.
129      */
getDataUsageInfo(NetworkTemplate template)130     public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
131         return getDataUsageInfo(template, UID_ALL);
132     }
133 
getTotalBytesForUid(NetworkStats stats, int uid)134     private static long getTotalBytesForUid(NetworkStats stats, int uid) {
135         if (!stats.hasNextBucket()) {
136             return 0;
137         }
138         NetworkStats.Bucket bucket = new NetworkStats.Bucket();
139         long rx = 0;
140         long tx = 0;
141         while (stats.hasNextBucket() && stats.getNextBucket(bucket)) {
142             if (uid != bucket.getUid())  continue;
143             rx += bucket.getRxBytes();
144             tx += bucket.getTxBytes();
145         }
146         return rx + tx;
147     }
148 
149     /**
150      * Get data usage info for a given template.
151      * @param template A given template.
152      * @param uid UID of app, uid = -1 {@link NetworkStats#UID_ALL} value indicates summarised
153      *        data usage without UID details.
154      * @return DataUsageInfo: The data usage information.
155      */
getDataUsageInfo(NetworkTemplate template, int uid)156     public DataUsageInfo getDataUsageInfo(NetworkTemplate template, int uid) {
157         final NetworkPolicy policy = findNetworkPolicy(template);
158         final long totalBytes;
159         final long now = System.currentTimeMillis();
160         final long start, end;
161         final NetworkStatsManager mNetworkStatsManager =
162                     mContext.getSystemService(NetworkStatsManager.class);
163         if (policy != null) {
164             final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
165                     .cycleIterator(policy).next();
166             start = cycle.first.toInstant().toEpochMilli();
167             end = cycle.second.toInstant().toEpochMilli();
168         } else {
169             // period = last 4 wks
170             end = now;
171             start = now - DateUtils.WEEK_IN_MILLIS * 4;
172         }
173 
174         if (uid == UID_ALL) {
175             final NetworkStats.Bucket ret = mNetworkStatsManager
176                     .querySummaryForDevice(template, start, end);
177             totalBytes = ret.getRxBytes() + ret.getTxBytes();
178         } else {
179             final NetworkStats ret = mNetworkStatsManager.querySummary(template, start, end);
180             totalBytes = getTotalBytesForUid(ret, uid);
181         }
182 
183         final DataUsageInfo usage = new DataUsageInfo();
184         usage.subscriberId = template.getSubscriberId();
185         usage.startEpochMilli = start;
186         usage.endEpochMilli = end;
187         usage.usageLevel = totalBytes;
188         usage.period = formatDateRange(start, end);
189         usage.cycleStart = DateUtils.formatDateTime(mContext, start,
190                 FORMAT_SHOW_DATE + FORMAT_SHOW_YEAR + FORMAT_SHOW_TIME);
191         usage.cycleEnd = DateUtils.formatDateTime(mContext, end,
192                 FORMAT_SHOW_DATE + FORMAT_SHOW_YEAR + FORMAT_SHOW_TIME);
193 
194         if (policy != null) {
195             usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : -1;
196             usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : -1;
197         }
198         if (uid != UID_ALL) {
199             usage.uId = uid;
200         }
201         return usage;
202     }
203 
findNetworkPolicy(NetworkTemplate template)204     private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
205         if (mPolicyManager == null || template == null) return null;
206         final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
207         if (policies == null) return null;
208         final int mLength = policies.length;
209         for (int i = 0; i < mLength; i++) {
210             final NetworkPolicy policy = policies[i];
211             if (policy != null && template.equals(policy.template)) {
212                 return policy;
213             }
214         }
215         return null;
216     }
217 
historyEntryToString(NetworkStatsHistory.Entry entry)218     private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
219         return entry == null ? null : new StringBuilder("Entry[")
220                 .append("bucketDuration=").append(entry.bucketDuration)
221                 .append(",bucketStart=").append(entry.bucketStart)
222                 .append(",activeTime=").append(entry.activeTime)
223                 .append(",rxBytes=").append(entry.rxBytes)
224                 .append(",rxPackets=").append(entry.rxPackets)
225                 .append(",txBytes=").append(entry.txBytes)
226                 .append(",txPackets=").append(entry.txPackets)
227                 .append(",operations=").append(entry.operations)
228                 .append(']').toString();
229     }
230 
231     /**
232      * Enable or disable mobile data.
233      * @param enabled Enable data: True: Disable data: False.
234      */
setMobileDataEnabled(boolean enabled)235     public void setMobileDataEnabled(boolean enabled) {
236         Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled);
237         mTelephonyManager.setDataEnabled(enabled);
238         if (mCallback != null) {
239             mCallback.onMobileDataEnabled(enabled);
240         }
241     }
242 
243     /**
244      * Check if mobile data is supported.
245      * @return True supported, False: Not supported.
246      */
isMobileDataSupported()247     public boolean isMobileDataSupported() {
248         // require both supported network and ready SIM
249         return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
250                 && mTelephonyManager.getSimState() == SIM_STATE_READY;
251     }
252 
253     /**
254      * Check if mobile data is enabled.
255      * @return True: enabled, False: Not enabled.
256      */
isMobileDataEnabled()257     public boolean isMobileDataEnabled() {
258         return mTelephonyManager.getDataEnabled();
259     }
260 
formatDateRange(long start, long end)261     private String formatDateRange(long start, long end) {
262         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
263         synchronized (PERIOD_BUILDER) {
264             PERIOD_BUILDER.setLength(0);
265             return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
266                     .toString();
267         }
268     }
269 
270     public interface NetworkNameProvider {
getMobileDataNetworkName()271         String getMobileDataNetworkName();
272     }
273 
274     public static class DataUsageInfo {
275         public String subscriberId;
276         public String period;
277         public Integer uId;
278         public long startEpochMilli;
279         public long endEpochMilli;
280         public long limitLevel;
281         public long warningLevel;
282         public long usageLevel;
283         public String cycleStart;
284         public String cycleEnd;
285     }
286 
287     public interface Callback {
onMobileDataEnabled(boolean enabled)288         void onMobileDataEnabled(boolean enabled);
289     }
290 }
291