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