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