1 /* 2 * Copyright (C) 2018 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.googlecode.android_scripting.facade; 18 19 import static android.net.ConnectivityManager.TYPE_MOBILE; 20 import static android.net.NetworkStats.UID_ALL; 21 import static android.telephony.TelephonyManager.SIM_STATE_READY; 22 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; 23 import static android.text.format.DateUtils.FORMAT_SHOW_DATE; 24 import static android.text.format.DateUtils.FORMAT_SHOW_TIME; 25 import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; 26 27 import android.app.usage.NetworkStats; 28 import android.app.usage.NetworkStatsManager; 29 import android.content.Context; 30 import android.net.ConnectivityManager; 31 import android.net.NetworkPolicy; 32 import android.net.NetworkPolicyManager; 33 import android.net.NetworkStatsHistory; 34 import android.net.NetworkTemplate; 35 import android.telephony.TelephonyManager; 36 import android.text.format.DateUtils; 37 import android.util.Log; 38 import android.util.Pair; 39 40 import java.time.ZonedDateTime; 41 import java.util.Locale; 42 43 /** 44 * DataUsageController. 45 */ 46 public class DataUsageController { 47 48 private static final String TAG = "DataUsageController"; 49 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 50 private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50); 51 private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter( 52 PERIOD_BUILDER, Locale.getDefault()); 53 54 private final Context mContext; 55 private final TelephonyManager mTelephonyManager; 56 private final ConnectivityManager mConnectivityManager; 57 private final NetworkPolicyManager mPolicyManager; 58 59 private Callback mCallback; 60 private NetworkNameProvider mNetworkController; 61 DataUsageController(Context context)62 public DataUsageController(Context context) { 63 mContext = context; 64 mTelephonyManager = TelephonyManager.from(context); 65 mConnectivityManager = ConnectivityManager.from(context); 66 mPolicyManager = NetworkPolicyManager.from(mContext); 67 } 68 setNetworkController(NetworkNameProvider networkController)69 public void setNetworkController(NetworkNameProvider networkController) { 70 mNetworkController = networkController; 71 } 72 setCallback(Callback callback)73 public void setCallback(Callback callback) { 74 mCallback = callback; 75 } 76 warn(String msg)77 private DataUsageInfo warn(String msg) { 78 Log.w(TAG, "Failed to get data usage, " + msg); 79 return null; 80 } 81 82 /** 83 * Get mobile data usage info. 84 * @return DataUsageInfo: The Mobile data usage information. 85 */ getMobileDataUsageInfoForSubscriber(String subscriberId)86 public DataUsageInfo getMobileDataUsageInfoForSubscriber(String subscriberId) { 87 if (subscriberId == null) { 88 return warn("no subscriber id"); 89 } 90 NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId); 91 template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds()); 92 93 return getDataUsageInfo(template); 94 } 95 96 /** 97 * Get mobile data usage info. 98 * @return DataUsageInfo: The Mobile data usage information. 99 */ getMobileDataUsageInfoForUid(Integer uId, String subscriberId)100 public DataUsageInfo getMobileDataUsageInfoForUid(Integer uId, String subscriberId) { 101 if (subscriberId == null) { 102 return warn("no subscriber id"); 103 } 104 NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId); 105 template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds()); 106 107 return getDataUsageInfo(template, uId); 108 } 109 110 /** 111 * Get wifi data usage info. 112 * @return DataUsageInfo: The Wifi data usage information. 113 */ getWifiDataUsageInfo()114 public DataUsageInfo getWifiDataUsageInfo() { 115 NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard(); 116 return getDataUsageInfo(template); 117 } 118 119 /** 120 * Get data usage info for a given template. 121 * @param template A given template. 122 * @return DataUsageInfo: The data usage information. 123 */ getDataUsageInfo(NetworkTemplate template)124 public DataUsageInfo getDataUsageInfo(NetworkTemplate template) { 125 return getDataUsageInfo(template, UID_ALL); 126 } 127 getTotalBytesForUid(NetworkStats stats, int uid)128 private static long getTotalBytesForUid(NetworkStats stats, int uid) { 129 if (!stats.hasNextBucket()) { 130 return 0; 131 } 132 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 133 long rx = 0; 134 long tx = 0; 135 while (stats.hasNextBucket() && stats.getNextBucket(bucket)) { 136 if (uid != bucket.getUid()) continue; 137 rx += bucket.getRxBytes(); 138 tx += bucket.getTxBytes(); 139 } 140 return rx + tx; 141 } 142 143 /** 144 * Get data usage info for a given template. 145 * @param template A given template. 146 * @param uid UID of app, uid = -1 {@link NetworkStats#UID_ALL} value indicates summarised 147 * data usage without UID details. 148 * @return DataUsageInfo: The data usage information. 149 */ getDataUsageInfo(NetworkTemplate template, int uid)150 public DataUsageInfo getDataUsageInfo(NetworkTemplate template, int uid) { 151 final NetworkPolicy policy = findNetworkPolicy(template); 152 final long totalBytes; 153 final long now = System.currentTimeMillis(); 154 final long start, end; 155 final NetworkStatsManager mNetworkStatsManager = 156 mContext.getSystemService(NetworkStatsManager.class); 157 if (policy != null) { 158 final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager 159 .cycleIterator(policy).next(); 160 start = cycle.first.toInstant().toEpochMilli(); 161 end = cycle.second.toInstant().toEpochMilli(); 162 } else { 163 // period = last 4 wks 164 end = now; 165 start = now - DateUtils.WEEK_IN_MILLIS * 4; 166 } 167 168 if (uid == UID_ALL) { 169 final NetworkStats.Bucket ret = mNetworkStatsManager 170 .querySummaryForDevice(template, start, end); 171 totalBytes = ret.getRxBytes() + ret.getTxBytes(); 172 } else { 173 final NetworkStats ret = mNetworkStatsManager.querySummary(template, start, end); 174 totalBytes = getTotalBytesForUid(ret, uid); 175 } 176 177 final DataUsageInfo usage = new DataUsageInfo(); 178 usage.subscriberId = template.getSubscriberId(); 179 usage.startEpochMilli = start; 180 usage.endEpochMilli = end; 181 usage.usageLevel = totalBytes; 182 usage.period = formatDateRange(start, end); 183 usage.cycleStart = DateUtils.formatDateTime(mContext, start, 184 FORMAT_SHOW_DATE + FORMAT_SHOW_YEAR + FORMAT_SHOW_TIME); 185 usage.cycleEnd = DateUtils.formatDateTime(mContext, end, 186 FORMAT_SHOW_DATE + FORMAT_SHOW_YEAR + FORMAT_SHOW_TIME); 187 188 if (policy != null) { 189 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : -1; 190 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : -1; 191 } 192 if (uid != UID_ALL) { 193 usage.uId = uid; 194 } 195 return usage; 196 } 197 findNetworkPolicy(NetworkTemplate template)198 private NetworkPolicy findNetworkPolicy(NetworkTemplate template) { 199 if (mPolicyManager == null || template == null) return null; 200 final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies(); 201 if (policies == null) return null; 202 final int mLength = policies.length; 203 for (int i = 0; i < mLength; i++) { 204 final NetworkPolicy policy = policies[i]; 205 if (policy != null && template.equals(policy.template)) { 206 return policy; 207 } 208 } 209 return null; 210 } 211 historyEntryToString(NetworkStatsHistory.Entry entry)212 private static String historyEntryToString(NetworkStatsHistory.Entry entry) { 213 return entry == null ? null : new StringBuilder("Entry[") 214 .append("bucketDuration=").append(entry.bucketDuration) 215 .append(",bucketStart=").append(entry.bucketStart) 216 .append(",activeTime=").append(entry.activeTime) 217 .append(",rxBytes=").append(entry.rxBytes) 218 .append(",rxPackets=").append(entry.rxPackets) 219 .append(",txBytes=").append(entry.txBytes) 220 .append(",txPackets=").append(entry.txPackets) 221 .append(",operations=").append(entry.operations) 222 .append(']').toString(); 223 } 224 225 /** 226 * Enable or disable mobile data. 227 * @param enabled Enable data: True: Disable data: False. 228 */ setMobileDataEnabled(boolean enabled)229 public void setMobileDataEnabled(boolean enabled) { 230 Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled); 231 mTelephonyManager.setDataEnabled(enabled); 232 if (mCallback != null) { 233 mCallback.onMobileDataEnabled(enabled); 234 } 235 } 236 237 /** 238 * Check if mobile data is supported. 239 * @return True supported, False: Not supported. 240 */ isMobileDataSupported()241 public boolean isMobileDataSupported() { 242 // require both supported network and ready SIM 243 return mConnectivityManager.isNetworkSupported(TYPE_MOBILE) 244 && mTelephonyManager.getSimState() == SIM_STATE_READY; 245 } 246 247 /** 248 * Check if mobile data is enabled. 249 * @return True: enabled, False: Not enabled. 250 */ isMobileDataEnabled()251 public boolean isMobileDataEnabled() { 252 return mTelephonyManager.getDataEnabled(); 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 subscriberId; 270 public String period; 271 public Integer uId; 272 public long startEpochMilli; 273 public long endEpochMilli; 274 public long limitLevel; 275 public long warningLevel; 276 public long usageLevel; 277 public String cycleStart; 278 public String cycleEnd; 279 } 280 281 public interface Callback { onMobileDataEnabled(boolean enabled)282 void onMobileDataEnabled(boolean enabled); 283 } 284 } 285