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.content.Context;
     28 import android.net.ConnectivityManager;
     29 import android.net.INetworkStatsService;
     30 import android.net.INetworkStatsSession;
     31 import android.net.NetworkPolicy;
     32 import android.net.NetworkPolicyManager;
     33 import android.net.NetworkStatsHistory;
     34 import android.net.NetworkTemplate;
     35 import android.os.RemoteException;
     36 import android.os.ServiceManager;
     37 import android.telephony.SubscriptionManager;
     38 import android.telephony.TelephonyManager;
     39 import android.text.format.DateUtils;
     40 import android.util.Log;
     41 import android.util.Pair;
     42 
     43 import com.android.internal.R;
     44 
     45 import java.time.ZonedDateTime;
     46 import java.util.Date;
     47 import java.util.Locale;
     48 
     49 public class DataUsageController {
     50 
     51     private static final String TAG = "DataUsageController";
     52     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     53     private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
     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 INetworkStatsService mStatsService;
     62     private final NetworkPolicyManager mPolicyManager;
     63 
     64     private INetworkStatsSession mSession;
     65     private Callback mCallback;
     66     private NetworkNameProvider mNetworkController;
     67 
     68     public DataUsageController(Context context) {
     69         mContext = context;
     70         mTelephonyManager = TelephonyManager.from(context);
     71         mConnectivityManager = ConnectivityManager.from(context);
     72         mStatsService = INetworkStatsService.Stub.asInterface(
     73                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
     74         mPolicyManager = NetworkPolicyManager.from(mContext);
     75     }
     76 
     77     public void setNetworkController(NetworkNameProvider networkController) {
     78         mNetworkController = networkController;
     79     }
     80 
     81     /**
     82      * Returns the default warning level in bytes.
     83      */
     84     public long getDefaultWarningLevel() {
     85         return MB_IN_BYTES
     86                 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
     87     }
     88 
     89     private INetworkStatsSession getSession() {
     90         if (mSession == null) {
     91             try {
     92                 mSession = mStatsService.openSession();
     93             } catch (RemoteException e) {
     94                 Log.w(TAG, "Failed to open stats session", e);
     95             } catch (RuntimeException e) {
     96                 Log.w(TAG, "Failed to open stats session", e);
     97             }
     98         }
     99         return mSession;
    100     }
    101 
    102     public void setCallback(Callback callback) {
    103         mCallback = callback;
    104     }
    105 
    106     private DataUsageInfo warn(String msg) {
    107         Log.w(TAG, "Failed to get data usage, " + msg);
    108         return null;
    109     }
    110 
    111     public DataUsageInfo getDataUsageInfo() {
    112         final String subscriberId = getActiveSubscriberId(mContext);
    113         if (subscriberId == null) {
    114             return warn("no subscriber id");
    115         }
    116         NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
    117         template = NetworkTemplate.normalize(template, mTelephonyManager.getMergedSubscriberIds());
    118 
    119         return getDataUsageInfo(template);
    120     }
    121 
    122     public DataUsageInfo getWifiDataUsageInfo() {
    123         NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
    124         return getDataUsageInfo(template);
    125     }
    126 
    127     public DataUsageInfo getDataUsageInfo(NetworkTemplate template) {
    128         final INetworkStatsSession session = getSession();
    129         if (session == null) {
    130             return warn("no stats session");
    131         }
    132         final NetworkPolicy policy = findNetworkPolicy(template);
    133         try {
    134             final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
    135             final long now = System.currentTimeMillis();
    136             final long start, end;
    137             if (policy != null) {
    138                 final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
    139                         .cycleIterator(policy).next();
    140                 start = cycle.first.toInstant().toEpochMilli();
    141                 end = cycle.second.toInstant().toEpochMilli();
    142             } else {
    143                 // period = last 4 wks
    144                 end = now;
    145                 start = now - DateUtils.WEEK_IN_MILLIS * 4;
    146             }
    147             final long callStart = System.currentTimeMillis();
    148             final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
    149             final long callEnd = System.currentTimeMillis();
    150             if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s",
    151                     new Date(start), new Date(end), new Date(now), callEnd - callStart,
    152                     historyEntryToString(entry)));
    153             if (entry == null) {
    154                 return warn("no entry data");
    155             }
    156             final long totalBytes = entry.rxBytes + entry.txBytes;
    157             final DataUsageInfo usage = new DataUsageInfo();
    158             usage.startDate = start;
    159             usage.usageLevel = totalBytes;
    160             usage.period = formatDateRange(start, end);
    161             usage.cycleStart = start;
    162             usage.cycleEnd = end;
    163 
    164             if (policy != null) {
    165                 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
    166                 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
    167             } else {
    168                 usage.warningLevel = getDefaultWarningLevel();
    169             }
    170             if (usage != null && mNetworkController != null) {
    171                 usage.carrier = mNetworkController.getMobileDataNetworkName();
    172             }
    173             return usage;
    174         } catch (RemoteException e) {
    175             return warn("remote call failed");
    176         }
    177     }
    178 
    179     private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
    180         if (mPolicyManager == null || template == null) return null;
    181         final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
    182         if (policies == null) return null;
    183         final int N = policies.length;
    184         for (int i = 0; i < N; i++) {
    185             final NetworkPolicy policy = policies[i];
    186             if (policy != null && template.equals(policy.template)) {
    187                 return policy;
    188             }
    189         }
    190         return null;
    191     }
    192 
    193     private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
    194         return entry == null ? null : new StringBuilder("Entry[")
    195                 .append("bucketDuration=").append(entry.bucketDuration)
    196                 .append(",bucketStart=").append(entry.bucketStart)
    197                 .append(",activeTime=").append(entry.activeTime)
    198                 .append(",rxBytes=").append(entry.rxBytes)
    199                 .append(",rxPackets=").append(entry.rxPackets)
    200                 .append(",txBytes=").append(entry.txBytes)
    201                 .append(",txPackets=").append(entry.txPackets)
    202                 .append(",operations=").append(entry.operations)
    203                 .append(']').toString();
    204     }
    205 
    206     public void setMobileDataEnabled(boolean enabled) {
    207         Log.d(TAG, "setMobileDataEnabled: enabled=" + enabled);
    208         mTelephonyManager.setDataEnabled(enabled);
    209         if (mCallback != null) {
    210             mCallback.onMobileDataEnabled(enabled);
    211         }
    212     }
    213 
    214     public boolean isMobileDataSupported() {
    215         // require both supported network and ready SIM
    216         return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
    217                 && mTelephonyManager.getSimState() == SIM_STATE_READY;
    218     }
    219 
    220     public boolean isMobileDataEnabled() {
    221         return mTelephonyManager.getDataEnabled();
    222     }
    223 
    224     private static String getActiveSubscriberId(Context context) {
    225         final TelephonyManager tele = TelephonyManager.from(context);
    226         final String actualSubscriberId = tele.getSubscriberId(
    227                 SubscriptionManager.getDefaultDataSubscriptionId());
    228         return actualSubscriberId;
    229     }
    230 
    231     private String formatDateRange(long start, long end) {
    232         final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
    233         synchronized (PERIOD_BUILDER) {
    234             PERIOD_BUILDER.setLength(0);
    235             return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
    236                     .toString();
    237         }
    238     }
    239 
    240     public interface NetworkNameProvider {
    241         String getMobileDataNetworkName();
    242     }
    243 
    244     public static class DataUsageInfo {
    245         public String carrier;
    246         public String period;
    247         public long startDate;
    248         public long limitLevel;
    249         public long warningLevel;
    250         public long usageLevel;
    251         public long cycleStart;
    252         public long cycleEnd;
    253     }
    254 
    255     public interface Callback {
    256         void onMobileDataEnabled(boolean enabled);
    257     }
    258 }
    259