1 /* 2 * Copyright (C) 2015 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.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; 34 35 import java.util.Date; 36 import java.util.Locale; 37 38 import static android.net.ConnectivityManager.TYPE_MOBILE; 39 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; 40 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; 41 import static android.telephony.TelephonyManager.SIM_STATE_READY; 42 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; 43 import static android.text.format.DateUtils.FORMAT_SHOW_DATE; 44 45 public class DataUsageController { 46 private static final String TAG = "DataUsageController"; 47 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 48 49 public static final long DEFAULT_WARNING_LEVEL = 2L * 1024 * 1024 * 1024; 50 private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES; 51 private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50); 52 private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter( 53 PERIOD_BUILDER, Locale.getDefault()); 54 55 private final Context mContext; 56 private final TelephonyManager mTelephonyManager; 57 private final ConnectivityManager mConnectivityManager; 58 private final INetworkStatsService mStatsService; 59 private final NetworkPolicyManager mPolicyManager; 60 61 private INetworkStatsSession mSession; 62 private Callback mCallback; 63 private NetworkNameProvider mNetworkController; 64 DataUsageController(Context context)65 public DataUsageController(Context context) { 66 mContext = context; 67 mTelephonyManager = TelephonyManager.from(context); 68 mConnectivityManager = ConnectivityManager.from(context); 69 mStatsService = INetworkStatsService.Stub.asInterface( 70 ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); 71 mPolicyManager = NetworkPolicyManager.from(mContext); 72 } 73 setNetworkController(NetworkNameProvider networkController)74 public void setNetworkController(NetworkNameProvider networkController) { 75 mNetworkController = networkController; 76 } 77 getSession()78 private INetworkStatsSession getSession() { 79 if (mSession == null) { 80 try { 81 mSession = mStatsService.openSession(); 82 } catch (RemoteException e) { 83 Log.w(TAG, "Failed to open stats session", e); 84 } catch (RuntimeException e) { 85 Log.w(TAG, "Failed to open stats session", e); 86 } 87 } 88 return mSession; 89 } 90 setCallback(Callback callback)91 public void setCallback(Callback callback) { 92 mCallback = callback; 93 } 94 warn(String msg)95 private DataUsageInfo warn(String msg) { 96 Log.w(TAG, "Failed to get data usage, " + msg); 97 return null; 98 } 99 addMonth(Time t, int months)100 private static Time addMonth(Time t, int months) { 101 final Time rt = new Time(t); 102 rt.set(t.monthDay, t.month + months, t.year); 103 rt.normalize(false); 104 return rt; 105 } 106 getDataUsageInfo()107 public DataUsageInfo getDataUsageInfo() { 108 final String subscriberId = getActiveSubscriberId(mContext); 109 if (subscriberId == null) { 110 return warn("no subscriber id"); 111 } 112 NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId); 113 template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds()); 114 115 return getDataUsageInfo(template); 116 } 117 getWifiDataUsageInfo()118 public DataUsageInfo getWifiDataUsageInfo() { 119 NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard(); 120 return getDataUsageInfo(template); 121 } 122 getDataUsageInfo(NetworkTemplate template)123 public DataUsageInfo getDataUsageInfo(NetworkTemplate template) { 124 final INetworkStatsSession session = getSession(); 125 if (session == null) { 126 return warn("no stats session"); 127 } 128 final NetworkPolicy policy = findNetworkPolicy(template); 129 try { 130 final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS); 131 final long now = System.currentTimeMillis(); 132 final long start, end; 133 if (policy != null && policy.cycleDay > 0) { 134 // period = determined from cycleDay 135 if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz=" 136 + policy.cycleTimezone); 137 final Time nowTime = new Time(policy.cycleTimezone); 138 nowTime.setToNow(); 139 final Time policyTime = new Time(nowTime); 140 policyTime.set(policy.cycleDay, policyTime.month, policyTime.year); 141 policyTime.normalize(false); 142 if (nowTime.after(policyTime)) { 143 start = policyTime.toMillis(false); 144 end = addMonth(policyTime, 1).toMillis(false); 145 } else { 146 start = addMonth(policyTime, -1).toMillis(false); 147 end = policyTime.toMillis(false); 148 } 149 } else { 150 // period = last 4 wks 151 end = now; 152 start = now - DateUtils.WEEK_IN_MILLIS * 4; 153 } 154 final long callStart = System.currentTimeMillis(); 155 final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null); 156 final long callEnd = System.currentTimeMillis(); 157 if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s", 158 new Date(start), new Date(end), new Date(now), callEnd - callStart, 159 historyEntryToString(entry))); 160 if (entry == null) { 161 return warn("no entry data"); 162 } 163 final long totalBytes = entry.rxBytes + entry.txBytes; 164 final DataUsageInfo usage = new DataUsageInfo(); 165 usage.startDate = start; 166 usage.usageLevel = totalBytes; 167 usage.period = formatDateRange(start, end); 168 if (policy != null) { 169 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0; 170 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0; 171 } else { 172 usage.warningLevel = DEFAULT_WARNING_LEVEL; 173 } 174 if (usage != null && mNetworkController != null) { 175 usage.carrier = mNetworkController.getMobileDataNetworkName(); 176 } 177 return usage; 178 } catch (RemoteException e) { 179 return warn("remote call failed"); 180 } 181 } 182 findNetworkPolicy(NetworkTemplate template)183 private NetworkPolicy findNetworkPolicy(NetworkTemplate template) { 184 if (mPolicyManager == null || template == null) return null; 185 final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies(); 186 if (policies == null) return null; 187 final int N = policies.length; 188 for (int i = 0; i < N; i++) { 189 final NetworkPolicy policy = policies[i]; 190 if (policy != null && template.equals(policy.template)) { 191 return policy; 192 } 193 } 194 return null; 195 } 196 historyEntryToString(NetworkStatsHistory.Entry entry)197 private static String historyEntryToString(NetworkStatsHistory.Entry entry) { 198 return entry == null ? null : new StringBuilder("Entry[") 199 .append("bucketDuration=").append(entry.bucketDuration) 200 .append(",bucketStart=").append(entry.bucketStart) 201 .append(",activeTime=").append(entry.activeTime) 202 .append(",rxBytes=").append(entry.rxBytes) 203 .append(",rxPackets=").append(entry.rxPackets) 204 .append(",txBytes=").append(entry.txBytes) 205 .append(",txPackets=").append(entry.txPackets) 206 .append(",operations=").append(entry.operations) 207 .append(']').toString(); 208 } 209 setMobileDataEnabled(boolean enabled)210 public void setMobileDataEnabled(boolean enabled) { 211 Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled); 212 mTelephonyManager.setDataEnabled(enabled); 213 if (mCallback != null) { 214 mCallback.onMobileDataEnabled(enabled); 215 } 216 } 217 isMobileDataSupported()218 public boolean isMobileDataSupported() { 219 // require both supported network and ready SIM 220 return mConnectivityManager.isNetworkSupported(TYPE_MOBILE) 221 && mTelephonyManager.getSimState() == SIM_STATE_READY; 222 } 223 isMobileDataEnabled()224 public boolean isMobileDataEnabled() { 225 return mTelephonyManager.getDataEnabled(); 226 } 227 getActiveSubscriberId(Context context)228 private static String getActiveSubscriberId(Context context) { 229 final TelephonyManager tele = TelephonyManager.from(context); 230 final String actualSubscriberId = tele.getSubscriberId( 231 SubscriptionManager.getDefaultDataSubscriptionId()); 232 return actualSubscriberId; 233 } 234 formatDateRange(long start, long end)235 private String formatDateRange(long start, long end) { 236 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH; 237 synchronized (PERIOD_BUILDER) { 238 PERIOD_BUILDER.setLength(0); 239 return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null) 240 .toString(); 241 } 242 } 243 244 public interface NetworkNameProvider { getMobileDataNetworkName()245 String getMobileDataNetworkName(); 246 } 247 248 public static class DataUsageInfo { 249 public String carrier; 250 public String period; 251 public long startDate; 252 public long limitLevel; 253 public long warningLevel; 254 public long usageLevel; 255 } 256 257 public interface Callback { onMobileDataEnabled(boolean enabled)258 void onMobileDataEnabled(boolean enabled); 259 } 260 } 261