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