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