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