1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.settings.datausage; 16 17 import static android.content.pm.PackageManager.FEATURE_ETHERNET; 18 import static android.content.pm.PackageManager.FEATURE_USB_HOST; 19 import static android.content.pm.PackageManager.FEATURE_WIFI; 20 import static android.telephony.TelephonyManager.SIM_STATE_READY; 21 22 import android.app.usage.NetworkStats.Bucket; 23 import android.app.usage.NetworkStatsManager; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.net.ConnectivityManager; 28 import android.net.NetworkTemplate; 29 import android.os.RemoteException; 30 import android.os.SystemProperties; 31 import android.provider.Settings; 32 import android.telephony.SubscriptionInfo; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.text.BidiFormatter; 36 import android.text.format.Formatter; 37 import android.text.format.Formatter.BytesResult; 38 import android.util.Log; 39 40 import com.android.settings.datausage.lib.DataUsageLib; 41 import com.android.settings.network.ProxySubscriptionManager; 42 43 import java.util.List; 44 import java.util.Optional; 45 46 /** 47 * Utility methods for data usage classes. 48 */ 49 public final class DataUsageUtils extends com.android.settingslib.net.DataUsageUtils { 50 static final boolean TEST_RADIOS = false; 51 static final String TEST_RADIOS_PROP = "test.radios"; 52 private static final boolean LOGD = false; 53 private static final String ETHERNET = "ethernet"; 54 private static final String TAG = "DataUsageUtils"; 55 DataUsageUtils()56 private DataUsageUtils() { 57 } 58 59 /** 60 * Format byte value to readable string using IEC units. 61 */ formatDataUsage(Context context, long byteValue)62 public static CharSequence formatDataUsage(Context context, long byteValue) { 63 final BytesResult res = Formatter.formatBytes(context.getResources(), byteValue, 64 Formatter.FLAG_IEC_UNITS); 65 return BidiFormatter.getInstance().unicodeWrap(context.getString( 66 com.android.internal.R.string.fileSizeSuffix, res.value, res.units)); 67 } 68 69 /** 70 * Test if device has an ethernet network connection. 71 */ hasEthernet(Context context)72 public static boolean hasEthernet(Context context) { 73 if (DataUsageUtils.TEST_RADIOS) { 74 return SystemProperties.get(DataUsageUtils.TEST_RADIOS_PROP).contains(ETHERNET); 75 } 76 77 // See ConnectivityService#deviceSupportsEthernet. 78 final PackageManager pm = context.getPackageManager(); 79 if (!pm.hasSystemFeature(FEATURE_ETHERNET) && !pm.hasSystemFeature(FEATURE_USB_HOST)) { 80 return false; 81 } 82 83 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); 84 final NetworkStatsManager networkStatsManager = 85 context.getSystemService(NetworkStatsManager.class); 86 boolean hasEthernetUsage = false; 87 try { 88 final Bucket bucket = networkStatsManager.querySummaryForUser( 89 ConnectivityManager.TYPE_ETHERNET, telephonyManager.getSubscriberId(), 90 0L /* startTime */, System.currentTimeMillis() /* endTime */); 91 if (bucket != null) { 92 hasEthernetUsage = bucket.getRxBytes() > 0 || bucket.getTxBytes() > 0; 93 } 94 } catch (RemoteException e) { 95 Log.e(TAG, "Exception querying network detail.", e); 96 } 97 return hasEthernetUsage; 98 } 99 100 /** 101 * Returns whether device has mobile data. 102 * TODO: This is the opposite to Utils.isWifiOnly(), it should be refactored into 1 method. 103 */ hasMobileData(Context context)104 public static boolean hasMobileData(Context context) { 105 final TelephonyManager tele = context.getSystemService(TelephonyManager.class); 106 return tele.isDataCapable(); 107 } 108 109 /** 110 * Test if device has a mobile data radio with SIM in ready state. 111 */ hasReadyMobileRadio(Context context)112 public static boolean hasReadyMobileRadio(Context context) { 113 if (DataUsageUtils.TEST_RADIOS) { 114 return SystemProperties.get(DataUsageUtils.TEST_RADIOS_PROP).contains("mobile"); 115 } 116 final List<SubscriptionInfo> subInfoList = 117 ProxySubscriptionManager.getInstance(context) 118 .getActiveSubscriptionsInfo(); 119 // No activated Subscriptions 120 if (subInfoList == null) { 121 if (LOGD) { 122 Log.d(TAG, "hasReadyMobileRadio: subInfoList=null"); 123 } 124 return false; 125 } 126 final TelephonyManager tele = context.getSystemService(TelephonyManager.class); 127 // require both supported network and ready SIM 128 boolean isReady = true; 129 for (SubscriptionInfo subInfo : subInfoList) { 130 isReady = isReady & tele.getSimState(subInfo.getSimSlotIndex()) == SIM_STATE_READY; 131 if (LOGD) { 132 Log.d(TAG, "hasReadyMobileRadio: subInfo=" + subInfo); 133 } 134 } 135 136 final boolean isDataCapable = tele.isDataCapable(); 137 final boolean retVal = isDataCapable && isReady; 138 if (LOGD) { 139 Log.d(TAG, "hasReadyMobileRadio:" 140 + " telephonManager.isDataCapable()=" 141 + isDataCapable 142 + " isReady=" + isReady); 143 } 144 return retVal; 145 } 146 147 /** 148 * Whether device has a Wi-Fi data radio. 149 */ hasWifiRadio(Context context)150 public static boolean hasWifiRadio(Context context) { 151 if (TEST_RADIOS) { 152 return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi"); 153 } 154 155 final PackageManager packageManager = context.getPackageManager(); 156 return packageManager != null && packageManager.hasSystemFeature(FEATURE_WIFI); 157 } 158 159 /** 160 * Returns the default subscription if available else returns 161 * SubscriptionManager#INVALID_SUBSCRIPTION_ID 162 */ getDefaultSubscriptionId(Context context)163 public static int getDefaultSubscriptionId(Context context) { 164 // default data subscription is first choice 165 final int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId(); 166 if (SubscriptionManager.isValidSubscriptionId(dataSubId)) { 167 return dataSubId; 168 } 169 170 final ProxySubscriptionManager proxySubscriptionMgr = 171 ProxySubscriptionManager.getInstance(context); 172 173 // any active subscription is second choice 174 List<SubscriptionInfo> subList = proxySubscriptionMgr.getActiveSubscriptionsInfo(); 175 if ((subList == null) || (subList.size() <= 0)) { 176 // any subscription is third choice 177 subList = proxySubscriptionMgr.getAccessibleSubscriptionsInfo(); 178 } 179 if ((subList == null) || (subList.size() <= 0)) { 180 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 181 } 182 return subList.get(0).getSubscriptionId(); 183 } 184 185 /** 186 * Returns the default network template based on the availability of mobile data, Wifi. Returns 187 * ethernet template if both mobile data and Wifi are not available. 188 */ getDefaultTemplate(Context context, int defaultSubId)189 public static NetworkTemplate getDefaultTemplate(Context context, int defaultSubId) { 190 if (SubscriptionManager.isValidSubscriptionId(defaultSubId) && hasMobileData(context)) { 191 return DataUsageLib.getMobileTemplate(context, defaultSubId); 192 } else if (hasWifiRadio(context)) { 193 return new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build(); 194 } else { 195 return new NetworkTemplate.Builder(NetworkTemplate.MATCH_ETHERNET).build(); 196 } 197 } 198 199 /** 200 * Returns a mobile NetworkTemplate if EXTRA_SUB_ID of the Intent is available and the subId 201 * is valid & hasMobileData. Otherwise, returns empty data. 202 */ getMobileNetworkTemplateFromSubId(Context context, Intent intent)203 public static Optional<NetworkTemplate> getMobileNetworkTemplateFromSubId(Context context, 204 Intent intent) { 205 if (intent == null || !intent.hasExtra(Settings.EXTRA_SUB_ID)) { 206 return Optional.empty(); 207 } 208 209 int subId = intent.getIntExtra(Settings.EXTRA_SUB_ID, 210 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 211 if (SubscriptionManager.isValidSubscriptionId(subId) && hasMobileData(context)) { 212 return Optional.of(DataUsageLib.getMobileTemplate(context, subId)); 213 } 214 215 return Optional.empty(); 216 } 217 } 218