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 static android.net.NetworkStatsHistory.FIELD_RX_BYTES; 20 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; 21 import static android.net.TrafficStats.MB_IN_BYTES; 22 import static android.telephony.TelephonyManager.SIM_STATE_READY; 23 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; 24 import static android.text.format.DateUtils.FORMAT_SHOW_DATE; 25 26 import android.app.usage.NetworkStats.Bucket; 27 import android.app.usage.NetworkStatsManager; 28 import android.content.Context; 29 import android.net.ConnectivityManager; 30 import android.net.INetworkStatsService; 31 import android.net.INetworkStatsSession; 32 import android.net.NetworkPolicy; 33 import android.net.NetworkPolicyManager; 34 import android.net.NetworkTemplate; 35 import android.os.RemoteException; 36 import android.os.ServiceManager; 37 import android.telephony.SubscriptionManager; 38 import android.telephony.TelephonyManager; 39 import android.text.format.DateUtils; 40 import android.util.Log; 41 import android.util.Range; 42 43 import com.android.internal.R; 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.util.ArrayUtils; 46 47 import java.time.ZonedDateTime; 48 import java.util.Iterator; 49 import java.util.Locale; 50 51 public class DataUsageController { 52 53 private static final String TAG = "DataUsageController"; 54 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 55 private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES; 56 private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50); 57 private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter( 58 PERIOD_BUILDER, Locale.getDefault()); 59 60 private final Context mContext; 61 private final INetworkStatsService mStatsService; 62 private final NetworkPolicyManager mPolicyManager; 63 private final NetworkStatsManager mNetworkStatsManager; 64 65 private INetworkStatsSession mSession; 66 private Callback mCallback; 67 private NetworkNameProvider mNetworkController; 68 private int mSubscriptionId; 69 DataUsageController(Context context)70 public DataUsageController(Context context) { 71 mContext = context; 72 mStatsService = INetworkStatsService.Stub.asInterface( 73 ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); 74 mPolicyManager = NetworkPolicyManager.from(mContext); 75 mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class); 76 mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 77 } 78 setNetworkController(NetworkNameProvider networkController)79 public void setNetworkController(NetworkNameProvider networkController) { 80 mNetworkController = networkController; 81 } 82 83 /** 84 * By default this class will just get data usage information for the default data subscription, 85 * but this method can be called to require it to use an explicit subscription id which may be 86 * different from the default one (this is useful for the case of multi-SIM devices). 87 */ setSubscriptionId(int subscriptionId)88 public void setSubscriptionId(int subscriptionId) { 89 mSubscriptionId = subscriptionId; 90 } 91 92 /** 93 * Returns the default warning level in bytes. 94 */ getDefaultWarningLevel()95 public long getDefaultWarningLevel() { 96 return MB_IN_BYTES 97 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb); 98 } 99 setCallback(Callback callback)100 public void setCallback(Callback callback) { 101 mCallback = callback; 102 } 103 warn(String msg)104 private DataUsageInfo warn(String msg) { 105 Log.w(TAG, "Failed to get data usage, " + msg); 106 return null; 107 } 108 getDataUsageInfo()109 public DataUsageInfo getDataUsageInfo() { 110 NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId); 111 112 return getDataUsageInfo(template); 113 } 114 getWifiDataUsageInfo()115 public DataUsageInfo getWifiDataUsageInfo() { 116 NetworkTemplate template = NetworkTemplate.buildTemplateWifi( 117 NetworkTemplate.WIFI_NETWORKID_ALL, null); 118 return getDataUsageInfo(template); 119 } 120 getDataUsageInfo(NetworkTemplate template)121 public DataUsageInfo getDataUsageInfo(NetworkTemplate template) { 122 final NetworkPolicy policy = findNetworkPolicy(template); 123 final long now = System.currentTimeMillis(); 124 final long start, end; 125 final Iterator<Range<ZonedDateTime>> it = (policy != null) ? policy.cycleIterator() : null; 126 if (it != null && it.hasNext()) { 127 final Range<ZonedDateTime> cycle = it.next(); 128 start = cycle.getLower().toInstant().toEpochMilli(); 129 end = cycle.getUpper().toInstant().toEpochMilli(); 130 } else { 131 // period = last 4 wks 132 end = now; 133 start = now - DateUtils.WEEK_IN_MILLIS * 4; 134 } 135 final long totalBytes = getUsageLevel(template, start, end); 136 if (totalBytes < 0L) { 137 return warn("no entry data"); 138 } 139 final DataUsageInfo usage = new DataUsageInfo(); 140 usage.startDate = start; 141 usage.usageLevel = totalBytes; 142 usage.period = formatDateRange(start, end); 143 usage.cycleStart = start; 144 usage.cycleEnd = end; 145 146 if (policy != null) { 147 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0; 148 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0; 149 } else { 150 usage.warningLevel = getDefaultWarningLevel(); 151 } 152 if (usage != null && mNetworkController != null) { 153 usage.carrier = mNetworkController.getMobileDataNetworkName(); 154 } 155 return usage; 156 } 157 158 /** 159 * Get the total usage level recorded in the network history 160 * @param template the network template to retrieve the network history 161 * @return the total usage level recorded in the network history or -1L if there is error 162 * retrieving the data. 163 */ getHistoricalUsageLevel(NetworkTemplate template)164 public long getHistoricalUsageLevel(NetworkTemplate template) { 165 return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */); 166 } 167 getUsageLevel(NetworkTemplate template, long start, long end)168 private long getUsageLevel(NetworkTemplate template, long start, long end) { 169 try { 170 final Bucket bucket = mNetworkStatsManager.querySummaryForDevice(template, start, end); 171 if (bucket != null) { 172 return bucket.getRxBytes() + bucket.getTxBytes(); 173 } 174 Log.w(TAG, "Failed to get data usage, no entry data"); 175 } catch (RemoteException e) { 176 Log.w(TAG, "Failed to get data usage, remote call failed"); 177 } 178 return -1L; 179 } 180 findNetworkPolicy(NetworkTemplate template)181 private NetworkPolicy findNetworkPolicy(NetworkTemplate template) { 182 if (mPolicyManager == null || template == null) return null; 183 final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies(); 184 if (policies == null) return null; 185 final int N = policies.length; 186 for (int i = 0; i < N; i++) { 187 final NetworkPolicy policy = policies[i]; 188 if (policy != null && template.equals(policy.template)) { 189 return policy; 190 } 191 } 192 return null; 193 } 194 statsBucketToString(Bucket bucket)195 private static String statsBucketToString(Bucket bucket) { 196 return bucket == null ? null : new StringBuilder("Entry[") 197 .append("bucketDuration=").append(bucket.getEndTimeStamp() - bucket.getStartTimeStamp()) 198 .append(",bucketStart=").append(bucket.getStartTimeStamp()) 199 .append(",rxBytes=").append(bucket.getRxBytes()) 200 .append(",rxPackets=").append(bucket.getRxPackets()) 201 .append(",txBytes=").append(bucket.getTxBytes()) 202 .append(",txPackets=").append(bucket.getTxPackets()) 203 .append(']').toString(); 204 } 205 206 @VisibleForTesting getTelephonyManager()207 public TelephonyManager getTelephonyManager() { 208 int subscriptionId = mSubscriptionId; 209 210 // If mSubscriptionId is invalid, get default data sub. 211 if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { 212 subscriptionId = SubscriptionManager.getDefaultDataSubscriptionId(); 213 } 214 215 // If data sub is also invalid, get any active sub. 216 if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { 217 int[] activeSubIds = SubscriptionManager.from(mContext).getActiveSubscriptionIdList(); 218 if (!ArrayUtils.isEmpty(activeSubIds)) { 219 subscriptionId = activeSubIds[0]; 220 } 221 } 222 223 return mContext.getSystemService( 224 TelephonyManager.class).createForSubscriptionId(subscriptionId); 225 } 226 setMobileDataEnabled(boolean enabled)227 public void setMobileDataEnabled(boolean enabled) { 228 Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled); 229 getTelephonyManager().setDataEnabled(enabled); 230 if (mCallback != null) { 231 mCallback.onMobileDataEnabled(enabled); 232 } 233 } 234 isMobileDataSupported()235 public boolean isMobileDataSupported() { 236 // require both supported network and ready SIM 237 return getTelephonyManager().isDataCapable() 238 && getTelephonyManager().getSimState() == SIM_STATE_READY; 239 } 240 isMobileDataEnabled()241 public boolean isMobileDataEnabled() { 242 return getTelephonyManager().isDataEnabled(); 243 } 244 getNetworkType(NetworkTemplate networkTemplate)245 static int getNetworkType(NetworkTemplate networkTemplate) { 246 if (networkTemplate == null) { 247 return ConnectivityManager.TYPE_NONE; 248 } 249 final int matchRule = networkTemplate.getMatchRule(); 250 switch (matchRule) { 251 case NetworkTemplate.MATCH_MOBILE: 252 case NetworkTemplate.MATCH_MOBILE_WILDCARD: 253 return ConnectivityManager.TYPE_MOBILE; 254 case NetworkTemplate.MATCH_WIFI: 255 case NetworkTemplate.MATCH_WIFI_WILDCARD: 256 return ConnectivityManager.TYPE_WIFI; 257 case NetworkTemplate.MATCH_ETHERNET: 258 return ConnectivityManager.TYPE_ETHERNET; 259 default: 260 return ConnectivityManager.TYPE_MOBILE; 261 } 262 } 263 getActiveSubscriberId()264 private String getActiveSubscriberId() { 265 final String actualSubscriberId = getTelephonyManager().getSubscriberId(); 266 return actualSubscriberId; 267 } 268 formatDateRange(long start, long end)269 private String formatDateRange(long start, long end) { 270 final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH; 271 synchronized (PERIOD_BUILDER) { 272 PERIOD_BUILDER.setLength(0); 273 return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null) 274 .toString(); 275 } 276 } 277 278 public interface NetworkNameProvider { getMobileDataNetworkName()279 String getMobileDataNetworkName(); 280 } 281 282 public static class DataUsageInfo { 283 public String carrier; 284 public String period; 285 public long startDate; 286 public long limitLevel; 287 public long warningLevel; 288 public long usageLevel; 289 public long cycleStart; 290 public long cycleEnd; 291 } 292 293 public interface Callback { onMobileDataEnabled(boolean enabled)294 void onMobileDataEnabled(boolean enabled); 295 } 296 } 297