2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settingslib.net;
19 import android.content.Context;
20 import android.net.ConnectivityManager;
21 import android.net.INetworkStatsService;
22 import android.net.INetworkStatsSession;
23 import android.net.NetworkPolicy;
24 import android.net.NetworkPolicyManager;
25 import android.net.NetworkStatsHistory;
26 import android.net.NetworkTemplate;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.telephony.SubscriptionManager;
30 import android.telephony.TelephonyManager;
31 import android.text.format.DateUtils;
32 import android.text.format.Time;
33 import android.util.Log;
35 import com.android.settingslib.R;
37 import java.util.Date;
38 import java.util.Locale;
40 import static android.net.ConnectivityManager.TYPE_MOBILE;
41 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
42 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
43 import static android.telephony.TelephonyManager.SIM_STATE_READY;
44 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
45 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
46 import static android.net.TrafficStats.MB_IN_BYTES;
48 public class DataUsageController {
50 private static final String TAG = "DataUsageController";
51 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
52 private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
53 private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
54 private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
55 PERIOD_BUILDER, Locale.getDefault());
57 private final Context mContext;
58 private final TelephonyManager mTelephonyManager;
59 private final ConnectivityManager mConnectivityManager;
60 private final INetworkStatsService mStatsService;
61 private final NetworkPolicyManager mPolicyManager;
63 private INetworkStatsSession mSession;
64 private Callback mCallback;
65 private NetworkNameProvider mNetworkController;
67 public DataUsageController(Context context) {
69 mTelephonyManager = TelephonyManager.from(context);
70 mConnectivityManager = ConnectivityManager.from(context);
71 mStatsService = INetworkStatsService.Stub.asInterface(
72 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
73 mPolicyManager = NetworkPolicyManager.from(mContext);
76 public void setNetworkController(NetworkNameProvider networkController) {
77 mNetworkController = networkController;
81 * Returns the default warning level in bytes.
83 public long getDefaultWarningLevel() {
85 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
88 private INetworkStatsSession getSession() {
89 if (mSession == null) {
91 mSession = mStatsService.openSession();
92 } catch (RemoteException e) {
93 Log.w(TAG, "Failed to open stats session", e);
94 } catch (RuntimeException e) {
95 Log.w(TAG, "Failed to open stats session", e);
101 public void setCallback(Callback callback) {
102 mCallback = callback;
105 private DataUsageInfo warn(String msg) {
106 Log.w(TAG, "Failed to get data usage, " + msg);
110 private static Time addMonth(Time t, int months) {
111 final Time rt = new Time(t);
112 rt.set(t.monthDay, t.month + months, t.year);
117 public DataUsageInfo getDataUsageInfo() {
118 final String subscriberId = getActiveSubscriberId(mContext);
119 if (subscriberId == null) {
120 return warn("no subscriber id");
122 NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
123 template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds());
125 return getDataUsageInfo(template);
128 public DataUsageInfo getWifiDataUsageInfo() {
129 NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
130 return getDataUsageInfo(template);
133 public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
134 final INetworkStatsSession session = getSession();
135 if (session == null) {
136 return warn("no stats session");
138 final NetworkPolicy policy = findNetworkPolicy(template);
140 final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
141 final long now = System.currentTimeMillis();
142 final long start, end;
143 if (policy != null && policy.cycleDay > 0) {
144 // period = determined from cycleDay
145 if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz="
146 + policy.cycleTimezone);
147 final Time nowTime = new Time(policy.cycleTimezone);
149 final Time policyTime = new Time(nowTime);
150 policyTime.set(policy.cycleDay, policyTime.month, policyTime.year);
151 policyTime.normalize(false);
152 if (nowTime.after(policyTime)) {
153 start = policyTime.toMillis(false);
154 end = addMonth(policyTime, 1).toMillis(false);
156 start = addMonth(policyTime, -1).toMillis(false);
157 end = policyTime.toMillis(false);
160 // period = last 4 wks
162 start = now - DateUtils.WEEK_IN_MILLIS * 4;
164 final long callStart = System.currentTimeMillis();
165 final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
166 final long callEnd = System.currentTimeMillis();
167 if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s",
168 new Date(start), new Date(end), new Date(now), callEnd - callStart,
169 historyEntryToString(entry)));
171 return warn("no entry data");
173 final long totalBytes = entry.rxBytes + entry.txBytes;
174 final DataUsageInfo usage = new DataUsageInfo();
175 usage.startDate = start;
176 usage.usageLevel = totalBytes;
177 usage.period = formatDateRange(start, end);
178 if (policy != null) {
179 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
180 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
182 usage.warningLevel = getDefaultWarningLevel();
184 if (usage != null && mNetworkController != null) {
185 usage.carrier = mNetworkController.getMobileDataNetworkName();
188 } catch (RemoteException e) {
189 return warn("remote call failed");
193 private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
194 if (mPolicyManager == null || template == null) return null;
195 final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
196 if (policies == null) return null;
197 final int N = policies.length;
198 for (int i = 0; i < N; i++) {
199 final NetworkPolicy policy = policies[i];
200 if (policy != null && template.equals(policy.template)) {
207 private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
208 return entry == null ? null : new StringBuilder("Entry[")
209 .append("bucketDuration=").append(entry.bucketDuration)
210 .append(",bucketStart=").append(entry.bucketStart)
211 .append(",activeTime=").append(entry.activeTime)
212 .append(",rxBytes=").append(entry.rxBytes)
213 .append(",rxPackets=").append(entry.rxPackets)
214 .append(",txBytes=").append(entry.txBytes)
215 .append(",txPackets=").append(entry.txPackets)
216 .append(",operations=").append(entry.operations)
217 .append(']').toString();
220 public void setMobileDataEnabled(boolean enabled) {
221 Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled);
222 mTelephonyManager.setDataEnabled(enabled);
223 if (mCallback != null) {
224 mCallback.onMobileDataEnabled(enabled);
228 public boolean isMobileDataSupported() {
229 // require both supported network and ready SIM
230 return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
231 && mTelephonyManager.getSimState() == SIM_STATE_READY;
234 public boolean isMobileDataEnabled() {
235 return mTelephonyManager.getDataEnabled();
238 private static String getActiveSubscriberId(Context context) {
239 final TelephonyManager tele = TelephonyManager.from(context);
240 final String actualSubscriberId = tele.getSubscriberId(
241 SubscriptionManager.getDefaultDataSubscriptionId());
242 return actualSubscriberId;
245 private String formatDateRange(long start, long end) {
246 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
247 synchronized (PERIOD_BUILDER) {
248 PERIOD_BUILDER.setLength(0);
249 return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
254 public interface NetworkNameProvider {
255 String getMobileDataNetworkName();
258 public static class DataUsageInfo {
259 public String carrier;
260 public String period;
261 public long startDate;
262 public long limitLevel;
263 public long warningLevel;
264 public long usageLevel;
267 public interface Callback {
268 void onMobileDataEnabled(boolean enabled);