Home | History | Annotate | Download | only in facade
      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 
     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 
     78     public void setNetworkController(NetworkNameProvider networkController) {
     79         mNetworkController = networkController;
     80     }
     81 
     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 
     95     public void setCallback(Callback callback) {
     96         mCallback = callback;
     97     }
     98 
     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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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      */
    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      */
    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      */
    262     public boolean isMobileDataEnabled() {
    263         return mTelephonyManager.getDataEnabled();
    264     }
    265 
    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 {
    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 {
    293         void onMobileDataEnabled(boolean enabled);
    294     }
    295 }
    296