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