Home | History | Annotate | Download | only in net
      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.app.usage.NetworkStats.Bucket;
     28 import android.app.usage.NetworkStatsManager;
     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.NetworkTemplate;
     36 import android.os.RemoteException;
     37 import android.os.ServiceManager;
     38 import android.telephony.SubscriptionManager;
     39 import android.telephony.TelephonyManager;
     40 import android.text.format.DateUtils;
     41 import android.util.Log;
     42 import android.util.Range;
     43 
     44 import com.android.internal.R;
     45 import com.android.internal.annotations.VisibleForTesting;
     46 import com.android.internal.util.ArrayUtils;
     47 
     48 import java.time.ZonedDateTime;
     49 import java.util.Iterator;
     50 import java.util.Locale;
     51 
     52 public class DataUsageController {
     53 
     54     private static final String TAG = "DataUsageController";
     55     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     56     private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
     57     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
     58     private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
     59             PERIOD_BUILDER, Locale.getDefault());
     60 
     61     private final Context mContext;
     62     private final ConnectivityManager mConnectivityManager;
     63     private final INetworkStatsService mStatsService;
     64     private final NetworkPolicyManager mPolicyManager;
     65     private final NetworkStatsManager mNetworkStatsManager;
     66 
     67     private INetworkStatsSession mSession;
     68     private Callback mCallback;
     69     private NetworkNameProvider mNetworkController;
     70     private int mSubscriptionId;
     71 
     72     public DataUsageController(Context context) {
     73         mContext = context;
     74         mConnectivityManager = ConnectivityManager.from(context);
     75         mStatsService = INetworkStatsService.Stub.asInterface(
     76                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
     77         mPolicyManager = NetworkPolicyManager.from(mContext);
     78         mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
     79         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     80     }
     81 
     82     public void setNetworkController(NetworkNameProvider networkController) {
     83         mNetworkController = networkController;
     84     }
     85 
     86     /**
     87      * By default this class will just get data usage information for the default data subscription,
     88      * but this method can be called to require it to use an explicit subscription id which may be
     89      * different from the default one (this is useful for the case of multi-SIM devices).
     90      */
     91     public void setSubscriptionId(int subscriptionId) {
     92         mSubscriptionId = subscriptionId;
     93     }
     94 
     95     /**
     96      * Returns the default warning level in bytes.
     97      */
     98     public long getDefaultWarningLevel() {
     99         return MB_IN_BYTES
    100                 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
    101     }
    102 
    103     public void setCallback(Callback callback) {
    104         mCallback = callback;
    105     }
    106 
    107     private DataUsageInfo warn(String msg) {
    108         Log.w(TAG, "Failed to get data usage, " + msg);
    109         return null;
    110     }
    111 
    112     public DataUsageInfo getDataUsageInfo() {
    113         NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId);
    114 
    115         return getDataUsageInfo(template);
    116     }
    117 
    118     public DataUsageInfo getWifiDataUsageInfo() {
    119         NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
    120         return getDataUsageInfo(template);
    121     }
    122 
    123     public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
    124         final NetworkPolicy policy = findNetworkPolicy(template);
    125         final long now = System.currentTimeMillis();
    126         final long start, end;
    127         final Iterator<Range<ZonedDateTime>> it = (policy != null) ? policy.cycleIterator() : null;
    128         if (it != null && it.hasNext()) {
    129             final Range<ZonedDateTime> cycle = it.next();
    130             start = cycle.getLower().toInstant().toEpochMilli();
    131             end = cycle.getUpper().toInstant().toEpochMilli();
    132         } else {
    133             // period = last 4 wks
    134             end = now;
    135             start = now - DateUtils.WEEK_IN_MILLIS * 4;
    136         }
    137         final long totalBytes = getUsageLevel(template, start, end);
    138         if (totalBytes < 0L) {
    139             return warn("no entry data");
    140         }
    141         final DataUsageInfo usage = new DataUsageInfo();
    142         usage.startDate = start;
    143         usage.usageLevel = totalBytes;
    144         usage.period = formatDateRange(start, end);
    145         usage.cycleStart = start;
    146         usage.cycleEnd = end;
    147 
    148         if (policy != null) {
    149             usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
    150             usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
    151         } else {
    152             usage.warningLevel = getDefaultWarningLevel();
    153         }
    154         if (usage != null && mNetworkController != null) {
    155             usage.carrier = mNetworkController.getMobileDataNetworkName();
    156         }
    157         return usage;
    158     }
    159 
    160     /**
    161      * Get the total usage level recorded in the network history
    162      * @param template the network template to retrieve the network history
    163      * @return the total usage level recorded in the network history or -1L if there is error
    164      * retrieving the data.
    165      */
    166     public long getHistoricalUsageLevel(NetworkTemplate template) {
    167         return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
    168     }
    169 
    170     private long getUsageLevel(NetworkTemplate template, long start, long end) {
    171         try {
    172             final Bucket bucket = mNetworkStatsManager.querySummaryForDevice(template, start, end);
    173             if (bucket != null) {
    174                 return bucket.getRxBytes() + bucket.getTxBytes();
    175             }
    176             Log.w(TAG, "Failed to get data usage, no entry data");
    177         } catch (RemoteException e) {
    178             Log.w(TAG, "Failed to get data usage, remote call failed");
    179         }
    180         return -1L;
    181     }
    182 
    183     private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
    184         if (mPolicyManager == null || template == null) return null;
    185         final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
    186         if (policies == null) return null;
    187         final int N = policies.length;
    188         for (int i = 0; i < N; i++) {
    189             final NetworkPolicy policy = policies[i];
    190             if (policy != null && template.equals(policy.template)) {
    191                 return policy;
    192             }
    193         }
    194         return null;
    195     }
    196 
    197     private static String statsBucketToString(Bucket bucket) {
    198         return bucket == null ? null : new StringBuilder("Entry[")
    199             .append("bucketDuration=").append(bucket.getEndTimeStamp() - bucket.getStartTimeStamp())
    200             .append(",bucketStart=").append(bucket.getStartTimeStamp())
    201             .append(",rxBytes=").append(bucket.getRxBytes())
    202             .append(",rxPackets=").append(bucket.getRxPackets())
    203             .append(",txBytes=").append(bucket.getTxBytes())
    204             .append(",txPackets=").append(bucket.getTxPackets())
    205             .append(']').toString();
    206     }
    207 
    208     @VisibleForTesting
    209     public TelephonyManager getTelephonyManager() {
    210         int subscriptionId = mSubscriptionId;
    211 
    212         // If mSubscriptionId is invalid, get default data sub.
    213         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
    214             subscriptionId = SubscriptionManager.getDefaultDataSubscriptionId();
    215         }
    216 
    217         // If data sub is also invalid, get any active sub.
    218         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
    219             int[] activeSubIds = SubscriptionManager.from(mContext).getActiveSubscriptionIdList();
    220             if (!ArrayUtils.isEmpty(activeSubIds)) {
    221                 subscriptionId = activeSubIds[0];
    222             }
    223         }
    224 
    225         return TelephonyManager.from(mContext).createForSubscriptionId(subscriptionId);
    226     }
    227 
    228     public void setMobileDataEnabled(boolean enabled) {
    229         Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled);
    230         getTelephonyManager().setDataEnabled(enabled);
    231         if (mCallback != null) {
    232             mCallback.onMobileDataEnabled(enabled);
    233         }
    234     }
    235 
    236     public boolean isMobileDataSupported() {
    237         // require both supported network and ready SIM
    238         return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
    239                 && getTelephonyManager().getSimState() == SIM_STATE_READY;
    240     }
    241 
    242     public boolean isMobileDataEnabled() {
    243         return getTelephonyManager().isDataEnabled();
    244     }
    245 
    246     static int getNetworkType(NetworkTemplate networkTemplate) {
    247         if (networkTemplate == null) {
    248             return ConnectivityManager.TYPE_NONE;
    249         }
    250         final int matchRule = networkTemplate.getMatchRule();
    251         switch (matchRule) {
    252             case NetworkTemplate.MATCH_MOBILE:
    253             case NetworkTemplate.MATCH_MOBILE_WILDCARD:
    254                 return ConnectivityManager.TYPE_MOBILE;
    255             case NetworkTemplate.MATCH_WIFI:
    256             case NetworkTemplate.MATCH_WIFI_WILDCARD:
    257                 return  ConnectivityManager.TYPE_WIFI;
    258             case NetworkTemplate.MATCH_ETHERNET:
    259                 return  ConnectivityManager.TYPE_ETHERNET;
    260             default:
    261                 return ConnectivityManager.TYPE_MOBILE;
    262         }
    263     }
    264 
    265     private String getActiveSubscriberId() {
    266         final String actualSubscriberId = getTelephonyManager().getSubscriberId();
    267         return actualSubscriberId;
    268     }
    269 
    270     private String formatDateRange(long start, long end) {
    271         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
    272         synchronized (PERIOD_BUILDER) {
    273             PERIOD_BUILDER.setLength(0);
    274             return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
    275                     .toString();
    276         }
    277     }
    278 
    279     public interface NetworkNameProvider {
    280         String getMobileDataNetworkName();
    281     }
    282 
    283     public static class DataUsageInfo {
    284         public String carrier;
    285         public String period;
    286         public long startDate;
    287         public long limitLevel;
    288         public long warningLevel;
    289         public long usageLevel;
    290         public long cycleStart;
    291         public long cycleEnd;
    292     }
    293 
    294     public interface Callback {
    295         void onMobileDataEnabled(boolean enabled);
    296     }
    297 }
    298