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.content.pm.PackageManager;
23 import android.net.INetworkPolicyManager;
24 import android.net.NetworkPolicyManager;
25 import android.net.NetworkTemplate;
26 import android.os.ServiceManager;
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 androidx.annotation.VisibleForTesting;
35 import androidx.preference.Preference;
36 import androidx.preference.PreferenceFragmentCompat;
37 import androidx.recyclerview.widget.RecyclerView;
38 
39 import com.android.internal.util.CollectionUtils;
40 import com.android.settings.R;
41 import com.android.settings.core.BasePreferenceController;
42 import com.android.settings.core.PreferenceControllerMixin;
43 import com.android.settings.network.ProxySubscriptionManager;
44 import com.android.settings.widget.EntityHeaderController;
45 import com.android.settingslib.NetworkPolicyEditor;
46 import com.android.settingslib.core.lifecycle.Lifecycle;
47 import com.android.settingslib.core.lifecycle.LifecycleObserver;
48 import com.android.settingslib.core.lifecycle.events.OnStart;
49 import com.android.settingslib.net.DataUsageController;
50 
51 import java.util.List;
52 
53 /**
54  * This is the controller for a data usage header that retrieves carrier data from the new
55  * subscriptions framework API if available. The controller reads subscription information from the
56  * framework and falls back to legacy usage data if none are available.
57  */
58 public class DataUsageSummaryPreferenceController extends BasePreferenceController
59         implements PreferenceControllerMixin, LifecycleObserver, OnStart {
60 
61     private static final String TAG = "DataUsageController";
62     private static final String KEY = "status_header";
63     private static final long PETA = 1000000000000000L;
64     private static final float RELATIVE_SIZE_LARGE = 1.25f * 1.25f;  // (1/0.8)^2
65     private static final float RELATIVE_SIZE_SMALL = 1.0f / RELATIVE_SIZE_LARGE;  // 0.8^2
66 
67     private final Activity mActivity;
68     private final EntityHeaderController mEntityHeaderController;
69     private final Lifecycle mLifecycle;
70     private final PreferenceFragmentCompat mFragment;
71     protected final DataUsageController mDataUsageController;
72     protected final DataUsageInfoController mDataInfoController;
73     private final NetworkTemplate mDefaultTemplate;
74     protected final NetworkPolicyEditor mPolicyEditor;
75     private final int mDataUsageTemplate;
76     private final boolean mHasMobileData;
77     private final SubscriptionManager mSubscriptionManager;
78 
79     /** Name of the carrier, or null if not available */
80     private CharSequence mCarrierName;
81 
82     /** The number of registered plans, [0,N] */
83     private int mDataplanCount;
84 
85     /** The time of the last update in milliseconds since the epoch, or -1 if unknown */
86     private long mSnapshotTime;
87 
88     /**
89      * The size of the first registered plan if one exists or the size of the warning if it is set.
90      * -1 if no information is available.
91      */
92     private long mDataplanSize;
93     /** The "size" of the data usage bar, i.e. the amount of data its rhs end represents */
94     private long mDataBarSize;
95     /** The number of bytes used since the start of the cycle. */
96     private long mDataplanUse;
97     /** The starting time of the billing cycle in ms since the epoch */
98     private long mCycleStart;
99     /** The ending time of the billing cycle in ms since the epoch */
100     private long mCycleEnd;
101     /** The subscription that we should show usage for. */
102     private int mSubscriptionId;
103 
104     private Intent mManageSubscriptionIntent;
105 
DataUsageSummaryPreferenceController(Activity activity, Lifecycle lifecycle, PreferenceFragmentCompat fragment, int subscriptionId)106     public DataUsageSummaryPreferenceController(Activity activity,
107             Lifecycle lifecycle, PreferenceFragmentCompat fragment, int subscriptionId) {
108         super(activity, KEY);
109 
110         mActivity = activity;
111         mEntityHeaderController = EntityHeaderController.newInstance(activity,
112                 fragment, null);
113         mLifecycle = lifecycle;
114         mFragment = fragment;
115         mSubscriptionId = subscriptionId;
116 
117         mDefaultTemplate = DataUsageUtils.getDefaultTemplate(activity, mSubscriptionId);
118         NetworkPolicyManager policyManager = activity.getSystemService(NetworkPolicyManager.class);
119         mPolicyEditor = new NetworkPolicyEditor(policyManager);
120 
121         mHasMobileData = SubscriptionManager.isValidSubscriptionId(mSubscriptionId)
122                 && DataUsageUtils.hasMobileData(activity);
123 
124         mDataUsageController = new DataUsageController(activity);
125         mDataUsageController.setSubscriptionId(mSubscriptionId);
126         mDataInfoController = new DataUsageInfoController();
127 
128         if (mHasMobileData) {
129             mDataUsageTemplate = R.string.cell_data_template;
130         } else if (DataUsageUtils.hasWifiRadio(activity)) {
131             mDataUsageTemplate = R.string.wifi_data_template;
132         } else {
133             mDataUsageTemplate = R.string.ethernet_data_template;
134         }
135 
136         mSubscriptionManager = (SubscriptionManager)
137                 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
138     }
139 
140     @VisibleForTesting
DataUsageSummaryPreferenceController( DataUsageController dataUsageController, DataUsageInfoController dataInfoController, NetworkTemplate defaultTemplate, NetworkPolicyEditor policyEditor, int dataUsageTemplate, boolean hasMobileData, SubscriptionManager subscriptionManager, Activity activity, Lifecycle lifecycle, EntityHeaderController entityHeaderController, PreferenceFragmentCompat fragment, int subscriptionId)141     DataUsageSummaryPreferenceController(
142             DataUsageController dataUsageController,
143             DataUsageInfoController dataInfoController,
144             NetworkTemplate defaultTemplate,
145             NetworkPolicyEditor policyEditor,
146             int dataUsageTemplate,
147             boolean hasMobileData,
148             SubscriptionManager subscriptionManager,
149             Activity activity,
150             Lifecycle lifecycle,
151             EntityHeaderController entityHeaderController,
152             PreferenceFragmentCompat fragment,
153             int subscriptionId) {
154         super(activity, KEY);
155         mDataUsageController = dataUsageController;
156         mDataInfoController = dataInfoController;
157         mDefaultTemplate = defaultTemplate;
158         mPolicyEditor = policyEditor;
159         mDataUsageTemplate = dataUsageTemplate;
160         mHasMobileData = hasMobileData;
161         mSubscriptionManager = subscriptionManager;
162         mActivity = activity;
163         mLifecycle = lifecycle;
164         mEntityHeaderController = entityHeaderController;
165         mFragment = fragment;
166         mSubscriptionId = subscriptionId;
167     }
168 
169     @Override
onStart()170     public void onStart() {
171         RecyclerView view = mFragment.getListView();
172         mEntityHeaderController.setRecyclerView(view, mLifecycle);
173         mEntityHeaderController.styleActionBar(mActivity);
174     }
175 
176     @VisibleForTesting
setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse)177     void setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse) {
178         mDataplanCount = dataPlanCount;
179         mDataplanSize = dataPlanSize;
180         mDataBarSize = dataPlanSize;
181         mDataplanUse = dataPlanUse;
182     }
183 
184     @VisibleForTesting
setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent)185     void setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent) {
186         mCarrierName = carrierName;
187         mSnapshotTime = snapshotTime;
188         mCycleEnd = cycleEnd;
189         mManageSubscriptionIntent = intent;
190     }
191 
192     @Override
getAvailabilityStatus()193     public int getAvailabilityStatus() {
194         return DataUsageUtils.hasSim(mActivity)
195                 || DataUsageUtils.hasWifiRadio(mContext) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
196     }
197 
198     @Override
updateState(Preference preference)199     public void updateState(Preference preference) {
200         DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference;
201 
202         final DataUsageController.DataUsageInfo info;
203         if (DataUsageUtils.hasSim(mActivity)) {
204             info = mDataUsageController.getDataUsageInfo(mDefaultTemplate);
205             mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate));
206             summaryPreference.setWifiMode(/* isWifiMode */ false,
207                     /* usagePeriod */ null, /* isSingleWifi */ false);
208         } else {
209             info = mDataUsageController.getDataUsageInfo(
210                     NetworkTemplate.buildTemplateWifiWildcard());
211             summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */
212                     info.period, /* isSingleWifi */ false);
213             summaryPreference.setLimitInfo(null);
214             summaryPreference.setUsageNumbers(info.usageLevel,
215                     /* dataPlanSize */ -1L,
216                     /* hasMobileData */ true);
217             summaryPreference.setChartEnabled(false);
218             summaryPreference.setUsageInfo(info.cycleEnd,
219                     /* snapshotTime */ -1L,
220                     /* carrierName */ null,
221                     /* numPlans */ 0,
222                     /* launchIntent */ null);
223             return;
224         }
225 
226         if (mSubscriptionManager != null) {
227             refreshDataplanInfo(info);
228         }
229 
230         if (info.warningLevel > 0 && info.limitLevel > 0) {
231             summaryPreference.setLimitInfo(TextUtils.expandTemplate(
232                     mContext.getText(R.string.cell_data_warning_and_limit),
233                     DataUsageUtils.formatDataUsage(mContext, info.warningLevel),
234                     DataUsageUtils.formatDataUsage(mContext, info.limitLevel)));
235         } else if (info.warningLevel > 0) {
236             summaryPreference.setLimitInfo(TextUtils.expandTemplate(
237                     mContext.getText(R.string.cell_data_warning),
238                     DataUsageUtils.formatDataUsage(mContext, info.warningLevel)));
239         } else if (info.limitLevel > 0) {
240             summaryPreference.setLimitInfo(TextUtils.expandTemplate(
241                     mContext.getText(R.string.cell_data_limit),
242                     DataUsageUtils.formatDataUsage(mContext, info.limitLevel)));
243         } else {
244             summaryPreference.setLimitInfo(null);
245         }
246 
247         summaryPreference.setUsageNumbers(mDataplanUse, mDataplanSize, mHasMobileData);
248 
249         if (mDataBarSize <= 0) {
250             summaryPreference.setChartEnabled(false);
251         } else {
252             summaryPreference.setChartEnabled(true);
253             summaryPreference.setLabels(DataUsageUtils.formatDataUsage(mContext, 0 /* sizeBytes */),
254                     DataUsageUtils.formatDataUsage(mContext, mDataBarSize));
255             summaryPreference.setProgress(mDataplanUse / (float) mDataBarSize);
256         }
257         summaryPreference.setUsageInfo(mCycleEnd, mSnapshotTime, mCarrierName,
258                 mDataplanCount, mManageSubscriptionIntent);
259     }
260 
261     // TODO(b/70950124) add test for this method once the robolectric shadow run script is
262     // completed (b/3526807)
refreshDataplanInfo(DataUsageController.DataUsageInfo info)263     private void refreshDataplanInfo(DataUsageController.DataUsageInfo info) {
264         // reset data before overwriting
265         mCarrierName = null;
266         mDataplanCount = 0;
267         mDataplanSize = -1L;
268         mDataBarSize = mDataInfoController.getSummaryLimit(info);
269         mDataplanUse = info.usageLevel;
270         mCycleStart = info.cycleStart;
271         mCycleEnd = info.cycleEnd;
272         mSnapshotTime = -1L;
273 
274         final ProxySubscriptionManager proxySubsciptionMgr =
275                 ProxySubscriptionManager.getInstance(mContext);
276         final SubscriptionInfo subInfo = proxySubsciptionMgr
277                 .getAccessibleSubscriptionInfo(mSubscriptionId);
278         if (subInfo != null && mHasMobileData) {
279             mCarrierName = subInfo.getCarrierName();
280             List<SubscriptionPlan> plans = mSubscriptionManager.getSubscriptionPlans(
281                     mSubscriptionId);
282             final SubscriptionPlan primaryPlan = getPrimaryPlan(mSubscriptionManager,
283                     mSubscriptionId);
284 
285             if (primaryPlan != null) {
286                 mDataplanCount = plans.size();
287                 mDataplanSize = primaryPlan.getDataLimitBytes();
288                 if (unlimited(mDataplanSize)) {
289                     mDataplanSize = -1L;
290                 }
291                 mDataBarSize = mDataplanSize;
292                 mDataplanUse = primaryPlan.getDataUsageBytes();
293 
294                 RecurrenceRule rule = primaryPlan.getCycleRule();
295                 if (rule != null && rule.start != null && rule.end != null) {
296                     mCycleStart = rule.start.toEpochSecond() * 1000L;
297                     mCycleEnd = rule.end.toEpochSecond() * 1000L;
298                 }
299                 mSnapshotTime = primaryPlan.getDataUsageTime();
300             }
301         }
302         mManageSubscriptionIntent = createManageSubscriptionIntent(mSubscriptionId);
303         Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + mSubscriptionId
304                 + ", intent " + mManageSubscriptionIntent);
305     }
306 
307     /**
308      * Create an {@link Intent} that can be launched towards the carrier app
309      * that is currently defining the billing relationship plan through
310      * {@link INetworkPolicyManager#setSubscriptionPlans(int, SubscriptionPlan [], String)}.
311      *
312      * @return ready to launch Intent targeted towards the carrier app, or
313      *         {@code null} if no carrier app is defined, or if the defined
314      *         carrier app provides no management activity.
315      */
createManageSubscriptionIntent(int subId)316     private Intent createManageSubscriptionIntent(int subId) {
317         final INetworkPolicyManager iNetPolicyManager = INetworkPolicyManager.Stub.asInterface(
318                 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
319         String owner = "";
320         try {
321             owner = iNetPolicyManager.getSubscriptionPlansOwner(subId);
322         } catch (Exception ex) {
323             Log.w(TAG, "Fail to get subscription plan owner for subId " + subId, ex);
324         }
325 
326         if (TextUtils.isEmpty(owner)) {
327             return null;
328         }
329 
330         final List<SubscriptionPlan> plans = mSubscriptionManager.getSubscriptionPlans(subId);
331         if (plans.isEmpty()) {
332             return null;
333         }
334 
335         final Intent intent = new Intent(SubscriptionManager.ACTION_MANAGE_SUBSCRIPTION_PLANS);
336         intent.setPackage(owner);
337         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
338 
339         if (mActivity.getPackageManager().queryIntentActivities(intent,
340                 PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
341             return null;
342         }
343 
344         return intent;
345     }
346 
getPrimaryPlan(SubscriptionManager subManager, int primaryId)347     public static SubscriptionPlan getPrimaryPlan(SubscriptionManager subManager, int primaryId) {
348         List<SubscriptionPlan> plans = subManager.getSubscriptionPlans(primaryId);
349         if (CollectionUtils.isEmpty(plans)) {
350             return null;
351         }
352         // First plan in the list is the primary plan
353         SubscriptionPlan plan = plans.get(0);
354         return plan.getDataLimitBytes() > 0
355                 && saneSize(plan.getDataUsageBytes())
356                 && plan.getCycleRule() != null ? plan : null;
357     }
358 
saneSize(long value)359     private static boolean saneSize(long value) {
360         return value >= 0L && value < PETA;
361     }
362 
unlimited(long size)363     public static boolean unlimited(long size) {
364         return size == SubscriptionPlan.BYTES_UNLIMITED;
365     }
366 }
367