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