• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DataUsageSummaryPreferenceController(Activity activity, Lifecycle lifecycle, DataUsageSummary dataUsageSummary)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
DataUsageSummaryPreferenceController( DataUsageController dataUsageController, DataUsageInfoController dataInfoController, NetworkTemplate defaultTemplate, NetworkPolicyEditor policyEditor, int dataUsageTemplate, boolean hasMobileData, SubscriptionManager subscriptionManager, Activity activity, Lifecycle lifecycle, EntityHeaderController entityHeaderController, DataUsageSummary dataUsageSummary)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
onStart()159     public void onStart() {
160         RecyclerView view = mDataUsageSummary.getListView();
161         mEntityHeaderController.setRecyclerView(view, mLifecycle);
162         mEntityHeaderController.styleActionBar(mActivity);
163     }
164 
165     @VisibleForTesting
setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse)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
setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent)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
getAvailabilityStatus()182     public int getAvailabilityStatus() {
183         return DataUsageUtils.hasSim(mActivity)
184                 || DataUsageUtils.hasWifiRadio(mContext) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
185     }
186 
187     @Override
updateState(Preference preference)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)
refreshDataplanInfo(DataUsageController.DataUsageInfo info)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 
getPrimaryPlan(SubscriptionManager subManager, int primaryId)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 
saneSize(long value)302     private static boolean saneSize(long value) {
303         return value >= 0L && value < PETA;
304     }
305 
unlimited(long size)306     public static boolean unlimited(long size) {
307         return size == SubscriptionPlan.BYTES_UNLIMITED;
308     }
309 }
310