Home | History | Annotate | Download | only in datausage
      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.android.settings.datausage;
     18 
     19 import android.app.Activity;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.net.NetworkPolicyManager;
     23 import android.net.NetworkTemplate;
     24 import android.support.annotation.VisibleForTesting;
     25 import android.support.v7.preference.Preference;
     26 import android.support.v7.widget.RecyclerView;
     27 import android.telephony.SubscriptionInfo;
     28 import android.telephony.SubscriptionManager;
     29 import android.telephony.SubscriptionPlan;
     30 import android.text.TextUtils;
     31 import android.util.Log;
     32 import android.util.RecurrenceRule;
     33 
     34 import com.android.internal.util.CollectionUtils;
     35 import com.android.settings.R;
     36 import com.android.settings.core.BasePreferenceController;
     37 import com.android.settings.core.PreferenceControllerMixin;
     38 import com.android.settings.widget.EntityHeaderController;
     39 import com.android.settingslib.NetworkPolicyEditor;
     40 import com.android.settingslib.core.lifecycle.Lifecycle;
     41 import com.android.settingslib.core.lifecycle.LifecycleObserver;
     42 import com.android.settingslib.core.lifecycle.events.OnStart;
     43 import com.android.settingslib.net.DataUsageController;
     44 
     45 import java.util.List;
     46 
     47 /**
     48  * This is the controller for the top of the data usage screen that retrieves carrier data from the
     49  * new subscriptions framework API if available. The controller reads subscription information from
     50  * the framework and falls back to legacy usage data if none are available.
     51  */
     52 public class DataUsageSummaryPreferenceController extends BasePreferenceController
     53         implements PreferenceControllerMixin, LifecycleObserver, OnStart {
     54 
     55     private static final String TAG = "DataUsageController";
     56     private static final String KEY = "status_header";
     57     private static final long PETA = 1000000000000000L;
     58     private static final float RELATIVE_SIZE_LARGE = 1.25f * 1.25f;  // (1/0.8)^2
     59     private static final float RELATIVE_SIZE_SMALL = 1.0f / RELATIVE_SIZE_LARGE;  // 0.8^2
     60 
     61     private final Activity mActivity;
     62     private final EntityHeaderController mEntityHeaderController;
     63     private final Lifecycle mLifecycle;
     64     private final DataUsageSummary mDataUsageSummary;
     65     private final DataUsageController mDataUsageController;
     66     private final DataUsageInfoController mDataInfoController;
     67     private final NetworkTemplate mDefaultTemplate;
     68     private final NetworkPolicyEditor mPolicyEditor;
     69     private final int mDataUsageTemplate;
     70     private final boolean mHasMobileData;
     71     private final SubscriptionManager mSubscriptionManager;
     72 
     73     /** Name of the carrier, or null if not available */
     74     private CharSequence mCarrierName;
     75 
     76     /** The number of registered plans, [0,N] */
     77     private int mDataplanCount;
     78 
     79     /** The time of the last update in milliseconds since the epoch, or -1 if unknown */
     80     private long mSnapshotTime;
     81 
     82     /**
     83      * The size of the first registered plan if one exists or the size of the warning if it is set.
     84      * -1 if no information is available.
     85      */
     86     private long mDataplanSize;
     87     /** The "size" of the data usage bar, i.e. the amount of data its rhs end represents */
     88     private long mDataBarSize;
     89     /** The number of bytes used since the start of the cycle. */
     90     private long mDataplanUse;
     91     /** The starting time of the billing cycle in ms since the epoch */
     92     private long mCycleStart;
     93     /** The ending time of the billing cycle in ms since the epoch */
     94     private long mCycleEnd;
     95 
     96     private Intent mManageSubscriptionIntent;
     97 
     98     public DataUsageSummaryPreferenceController(Activity activity,
     99             Lifecycle lifecycle, DataUsageSummary dataUsageSummary) {
    100         super(activity, KEY);
    101 
    102         mActivity = activity;
    103         mEntityHeaderController = EntityHeaderController.newInstance(activity,
    104                 dataUsageSummary, null);
    105         mLifecycle = lifecycle;
    106         mDataUsageSummary = dataUsageSummary;
    107 
    108         final int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(activity);
    109         mDefaultTemplate = DataUsageUtils.getDefaultTemplate(activity, defaultSubId);
    110         NetworkPolicyManager policyManager = NetworkPolicyManager.from(activity);
    111         mPolicyEditor = new NetworkPolicyEditor(policyManager);
    112 
    113         mHasMobileData = DataUsageUtils.hasMobileData(activity)
    114                 && defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    115 
    116         mDataUsageController = new DataUsageController(activity);
    117         mDataInfoController = new DataUsageInfoController();
    118 
    119         if (mHasMobileData) {
    120             mDataUsageTemplate = R.string.cell_data_template;
    121         } else if (DataUsageUtils.hasWifiRadio(activity)) {
    122             mDataUsageTemplate = R.string.wifi_data_template;
    123         } else {
    124             mDataUsageTemplate = R.string.ethernet_data_template;
    125         }
    126 
    127         mSubscriptionManager = (SubscriptionManager)
    128                 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
    129     }
    130 
    131     @VisibleForTesting
    132     DataUsageSummaryPreferenceController(
    133             DataUsageController dataUsageController,
    134             DataUsageInfoController dataInfoController,
    135             NetworkTemplate defaultTemplate,
    136             NetworkPolicyEditor policyEditor,
    137             int dataUsageTemplate,
    138             boolean hasMobileData,
    139             SubscriptionManager subscriptionManager,
    140             Activity activity,
    141             Lifecycle lifecycle,
    142             EntityHeaderController entityHeaderController,
    143             DataUsageSummary dataUsageSummary) {
    144         super(activity, KEY);
    145         mDataUsageController = dataUsageController;
    146         mDataInfoController = dataInfoController;
    147         mDefaultTemplate = defaultTemplate;
    148         mPolicyEditor = policyEditor;
    149         mDataUsageTemplate = dataUsageTemplate;
    150         mHasMobileData = hasMobileData;
    151         mSubscriptionManager = subscriptionManager;
    152         mActivity = activity;
    153         mLifecycle = lifecycle;
    154         mEntityHeaderController = entityHeaderController;
    155         mDataUsageSummary = dataUsageSummary;
    156     }
    157 
    158     @Override
    159     public void onStart() {
    160         RecyclerView view = mDataUsageSummary.getListView();
    161         mEntityHeaderController.setRecyclerView(view, mLifecycle);
    162         mEntityHeaderController.styleActionBar(mActivity);
    163     }
    164 
    165     @VisibleForTesting
    166     void setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse) {
    167         mDataplanCount = dataPlanCount;
    168         mDataplanSize = dataPlanSize;
    169         mDataBarSize = dataPlanSize;
    170         mDataplanUse = dataPlanUse;
    171     }
    172 
    173     @VisibleForTesting
    174     void setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent) {
    175         mCarrierName = carrierName;
    176         mSnapshotTime = snapshotTime;
    177         mCycleEnd = cycleEnd;
    178         mManageSubscriptionIntent = intent;
    179     }
    180 
    181     @Override
    182     public int getAvailabilityStatus() {
    183         return DataUsageUtils.hasSim(mActivity)
    184                 || DataUsageUtils.hasWifiRadio(mContext) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
    185     }
    186 
    187     @Override
    188     public void updateState(Preference preference) {
    189         DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference;
    190 
    191         final DataUsageController.DataUsageInfo info;
    192         if (DataUsageUtils.hasSim(mActivity)) {
    193             info = mDataUsageController.getDataUsageInfo(mDefaultTemplate);
    194             mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate));
    195             summaryPreference.setWifiMode(/* isWifiMode */ false, /* usagePeriod */ null);
    196         } else {
    197             info = mDataUsageController.getDataUsageInfo(
    198                     NetworkTemplate.buildTemplateWifiWildcard());
    199             summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */ info.period);
    200             summaryPreference.setLimitInfo(null);
    201             summaryPreference.setUsageNumbers(info.usageLevel,
    202                     /* dataPlanSize */ -1L,
    203                     /* hasMobileData */ true);
    204             summaryPreference.setChartEnabled(false);
    205             summaryPreference.setUsageInfo(info.cycleEnd,
    206                     /* snapshotTime */ -1L,
    207                     /* carrierName */ null,
    208                     /* numPlans */ 0,
    209                     /* launchIntent */ null);
    210             return;
    211         }
    212 
    213         if (mSubscriptionManager != null) {
    214             refreshDataplanInfo(info);
    215         }
    216 
    217         if (info.warningLevel > 0 && info.limitLevel > 0) {
    218                 summaryPreference.setLimitInfo(TextUtils.expandTemplate(
    219                         mContext.getText(R.string.cell_data_warning_and_limit),
    220                         DataUsageUtils.formatDataUsage(mContext, info.warningLevel),
    221                         DataUsageUtils.formatDataUsage(mContext, info.limitLevel)).toString());
    222         } else if (info.warningLevel > 0) {
    223                 summaryPreference.setLimitInfo(TextUtils.expandTemplate(
    224                         mContext.getText(R.string.cell_data_warning),
    225                         DataUsageUtils.formatDataUsage(mContext, info.warningLevel)).toString());
    226         } else if (info.limitLevel > 0) {
    227             summaryPreference.setLimitInfo(TextUtils.expandTemplate(
    228                     mContext.getText(R.string.cell_data_limit),
    229                     DataUsageUtils.formatDataUsage(mContext, info.limitLevel)).toString());
    230         } else {
    231             summaryPreference.setLimitInfo(null);
    232         }
    233 
    234         summaryPreference.setUsageNumbers(mDataplanUse, mDataplanSize, mHasMobileData);
    235 
    236         if (mDataBarSize <= 0) {
    237             summaryPreference.setChartEnabled(false);
    238         } else {
    239             summaryPreference.setChartEnabled(true);
    240             summaryPreference.setLabels(DataUsageUtils.formatDataUsage(mContext, 0 /* sizeBytes */),
    241                     DataUsageUtils.formatDataUsage(mContext, mDataBarSize));
    242             summaryPreference.setProgress(mDataplanUse / (float) mDataBarSize);
    243         }
    244         summaryPreference.setUsageInfo(mCycleEnd, mSnapshotTime, mCarrierName,
    245                 mDataplanCount, mManageSubscriptionIntent);
    246     }
    247 
    248     // TODO(b/70950124) add test for this method once the robolectric shadow run script is
    249     // completed (b/3526807)
    250     private void refreshDataplanInfo(DataUsageController.DataUsageInfo info) {
    251         // reset data before overwriting
    252         mCarrierName = null;
    253         mDataplanCount = 0;
    254         mDataplanSize = -1L;
    255         mDataBarSize = mDataInfoController.getSummaryLimit(info);
    256         mDataplanUse = info.usageLevel;
    257         mCycleStart = info.cycleStart;
    258         mCycleEnd = info.cycleEnd;
    259         mSnapshotTime = -1L;
    260 
    261         final int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
    262         final SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo();
    263         if (subInfo != null && mHasMobileData) {
    264             mCarrierName = subInfo.getCarrierName();
    265             List<SubscriptionPlan> plans = mSubscriptionManager.getSubscriptionPlans(defaultSubId);
    266             final SubscriptionPlan primaryPlan = getPrimaryPlan(mSubscriptionManager, defaultSubId);
    267             if (primaryPlan != null) {
    268                 mDataplanCount = plans.size();
    269                 mDataplanSize = primaryPlan.getDataLimitBytes();
    270                 if (unlimited(mDataplanSize)) {
    271                     mDataplanSize = -1L;
    272                 }
    273                 mDataBarSize = mDataplanSize;
    274                 mDataplanUse = primaryPlan.getDataUsageBytes();
    275 
    276                 RecurrenceRule rule = primaryPlan.getCycleRule();
    277                 if (rule != null && rule.start != null && rule.end != null) {
    278                     mCycleStart = rule.start.toEpochSecond() * 1000L;
    279                     mCycleEnd = rule.end.toEpochSecond() * 1000L;
    280                 }
    281                 mSnapshotTime = primaryPlan.getDataUsageTime();
    282             }
    283         }
    284         mManageSubscriptionIntent =
    285                 mSubscriptionManager.createManageSubscriptionIntent(defaultSubId);
    286         Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + defaultSubId
    287                 + ", intent " + mManageSubscriptionIntent);
    288     }
    289 
    290     public static SubscriptionPlan getPrimaryPlan(SubscriptionManager subManager, int primaryId) {
    291         List<SubscriptionPlan> plans = subManager.getSubscriptionPlans(primaryId);
    292         if (CollectionUtils.isEmpty(plans)) {
    293             return null;
    294         }
    295         // First plan in the list is the primary plan
    296         SubscriptionPlan plan = plans.get(0);
    297         return plan.getDataLimitBytes() > 0
    298                 && saneSize(plan.getDataUsageBytes())
    299                 && plan.getCycleRule() != null ? plan : null;
    300     }
    301 
    302     private static boolean saneSize(long value) {
    303         return value >= 0L && value < PETA;
    304     }
    305 
    306     public static boolean unlimited(long size) {
    307         return size == SubscriptionPlan.BYTES_UNLIMITED;
    308     }
    309 }
    310