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