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