• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.settings.datausage;
16 
17 import android.app.Activity;
18 import android.app.settings.SettingsEnums;
19 import android.content.Context;
20 import android.net.NetworkTemplate;
21 import android.os.Bundle;
22 import android.os.UserManager;
23 import android.telephony.SubscriptionInfo;
24 import android.telephony.SubscriptionManager;
25 import android.text.BidiFormatter;
26 import android.text.Spannable;
27 import android.text.SpannableString;
28 import android.text.TextUtils;
29 import android.text.format.Formatter;
30 import android.text.style.RelativeSizeSpan;
31 import android.util.EventLog;
32 import android.util.Log;
33 
34 import androidx.annotation.VisibleForTesting;
35 import androidx.preference.Preference;
36 import androidx.preference.PreferenceScreen;
37 
38 import com.android.settings.R;
39 import com.android.settings.datausage.lib.DataUsageLib;
40 import com.android.settings.network.ProxySubscriptionManager;
41 import com.android.settings.network.SubscriptionUtil;
42 import com.android.settings.network.telephony.MobileNetworkUtils;
43 import com.android.settingslib.NetworkPolicyEditor;
44 import com.android.settingslib.core.AbstractPreferenceController;
45 
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 /**
50  * Settings preference fragment that displays data usage summary.
51  */
52 public class DataUsageSummary extends DataUsageBaseFragment implements DataUsageEditController {
53 
54     private static final String TAG = "DataUsageSummary";
55 
56     static final boolean LOGD = false;
57 
58     public static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
59 
60     private static final String KEY_STATUS_HEADER = "status_header";
61 
62     // Mobile data keys
63     public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category";
64     public static final String KEY_MOBILE_DATA_USAGE_TOGGLE = "data_usage_enable";
65     public static final String KEY_MOBILE_DATA_USAGE = "cellular_data_usage";
66     public static final String KEY_MOBILE_BILLING_CYCLE = "billing_preference";
67 
68     // Wifi keys
69     public static final String KEY_WIFI_USAGE_TITLE = "wifi_category";
70     public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage";
71 
72     private DataUsageSummaryPreference mSummaryPreference;
73     private DataUsageSummaryPreferenceController mSummaryController;
74     private NetworkTemplate mDefaultTemplate;
75     private ProxySubscriptionManager mProxySubscriptionMgr;
76 
77     @Override
getHelpResource()78     public int getHelpResource() {
79         return R.string.help_url_data_usage;
80     }
81 
isSimHardwareVisible(Context context)82     public boolean isSimHardwareVisible(Context context) {
83         return SubscriptionUtil.isSimHardwareVisible(context);
84     }
85 
86     @Override
onCreate(Bundle icicle)87     public void onCreate(Bundle icicle) {
88         super.onCreate(icicle);
89         Context context = getContext();
90         if (isGuestUser(context)) {
91             Log.e(TAG, "This setting isn't available due to user restriction.");
92             EventLog.writeEvent(0x534e4554, "262243574", -1 /* UID */, "Guest user");
93             finish();
94             return;
95         }
96 
97         if (!isSimHardwareVisible(context) ||
98             MobileNetworkUtils.isMobileNetworkUserRestricted(context)) {
99             finish();
100             return;
101         }
102         enableProxySubscriptionManager(context);
103 
104         boolean hasMobileData = DataUsageUtils.hasMobileData(context);
105 
106         final int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
107         if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
108             hasMobileData = false;
109         }
110         mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
111         mSummaryPreference = findPreference(KEY_STATUS_HEADER);
112 
113         if (!hasMobileData || !isAdmin()) {
114             removePreference(KEY_RESTRICT_BACKGROUND);
115         }
116         boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context);
117         if (hasMobileData) {
118             addMobileSection(defaultSubId);
119             if (hasActiveSubscription() && hasWifiRadio) {
120                 // If the device has active SIM, the data usage section shows usage for mobile,
121                 // and the WiFi section is added if there is a WiFi radio - legacy behavior.
122                 addWifiSection();
123             }
124             // Do not add the WiFi section if either there is no WiFi radio (obviously) or if no
125             // SIM is installed. In the latter case the data usage section will show WiFi usage and
126             // there should be no explicit WiFi section added.
127         } else if (hasWifiRadio) {
128             addWifiSection();
129         }
130         if (DataUsageUtils.hasEthernet(context)) {
131             addEthernetSection();
132         }
133         setHasOptionsMenu(true);
134     }
135 
136     @Override
onPreferenceTreeClick(Preference preference)137     public boolean onPreferenceTreeClick(Preference preference) {
138         if (preference == findPreference(KEY_STATUS_HEADER)) {
139             BillingCycleSettings.BytesEditorFragment.show(this, false);
140             return false;
141         }
142         return super.onPreferenceTreeClick(preference);
143     }
144 
145     @Override
getPreferenceScreenResId()146     protected int getPreferenceScreenResId() {
147         return R.xml.data_usage;
148     }
149 
150     @Override
getLogTag()151     protected String getLogTag() {
152         return TAG;
153     }
154 
155     @Override
createPreferenceControllers(Context context)156     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
157         final Activity activity = getActivity();
158         final ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
159         if (!isSimHardwareVisible(context) ||
160             MobileNetworkUtils.isMobileNetworkUserRestricted(context)) {
161             return controllers;
162         }
163         mSummaryController =
164                 new DataUsageSummaryPreferenceController(activity, getSettingsLifecycle(), this,
165                         DataUsageUtils.getDefaultSubscriptionId(activity));
166         controllers.add(mSummaryController);
167         getSettingsLifecycle().addObserver(mSummaryController);
168         return controllers;
169     }
170 
171     @VisibleForTesting
addMobileSection(int subId)172     void addMobileSection(int subId) {
173         addMobileSection(subId, null);
174     }
175 
176     @VisibleForTesting
enableProxySubscriptionManager(Context context)177     void enableProxySubscriptionManager(Context context) {
178         // Enable ProxySubscriptionMgr with Lifecycle support for all controllers
179         // live within this fragment
180         mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(context);
181         mProxySubscriptionMgr.setLifecycle(getLifecycle());
182     }
183 
184     @VisibleForTesting
hasActiveSubscription()185     boolean hasActiveSubscription() {
186         final List<SubscriptionInfo> subInfoList =
187                 mProxySubscriptionMgr.getActiveSubscriptionsInfo();
188         return ((subInfoList != null) && (subInfoList.size() > 0));
189     }
190 
addMobileSection(int subId, SubscriptionInfo subInfo)191     private void addMobileSection(int subId, SubscriptionInfo subInfo) {
192         TemplatePreferenceCategory category = (TemplatePreferenceCategory)
193                 inflatePreferences(R.xml.data_usage_cellular);
194         category.setTemplate(DataUsageLib.getMobileTemplate(getContext(), subId),
195                 subId, services);
196         category.pushTemplates(services);
197         final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
198                 subInfo, getContext());
199         if (subInfo != null && !TextUtils.isEmpty(displayName)) {
200             Preference title  = category.findPreference(KEY_MOBILE_USAGE_TITLE);
201             title.setTitle(displayName);
202         }
203     }
204 
205     @VisibleForTesting
addWifiSection()206     void addWifiSection() {
207         TemplatePreferenceCategory category = (TemplatePreferenceCategory)
208                 inflatePreferences(R.xml.data_usage_wifi);
209         category.setTemplate(new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build(),
210                 0, services);
211     }
212 
addEthernetSection()213     private void addEthernetSection() {
214         TemplatePreferenceCategory category = (TemplatePreferenceCategory)
215                 inflatePreferences(R.xml.data_usage_ethernet);
216         category.setTemplate(new NetworkTemplate.Builder(NetworkTemplate.MATCH_ETHERNET).build(),
217                 0, services);
218     }
219 
inflatePreferences(int resId)220     private Preference inflatePreferences(int resId) {
221         PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
222                 getPrefContext(), resId, null);
223         Preference pref = rootPreferences.getPreference(0);
224         rootPreferences.removeAll();
225 
226         PreferenceScreen screen = getPreferenceScreen();
227         pref.setOrder(screen.getPreferenceCount());
228         screen.addPreference(pref);
229 
230         return pref;
231     }
232 
233     @Override
onResume()234     public void onResume() {
235         super.onResume();
236         updateState();
237     }
238 
239     @VisibleForTesting
formatUsage(Context context, String template, long usageLevel)240     static CharSequence formatUsage(Context context, String template, long usageLevel) {
241         final float LARGER_SIZE = 1.25f * 1.25f;  // (1/0.8)^2
242         final float SMALLER_SIZE = 1.0f / LARGER_SIZE;  // 0.8^2
243         return formatUsage(context, template, usageLevel, LARGER_SIZE, SMALLER_SIZE);
244     }
245 
formatUsage(Context context, String template, long usageLevel, float larger, float smaller)246     static CharSequence formatUsage(Context context, String template, long usageLevel,
247                                     float larger, float smaller) {
248         final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
249 
250         final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
251                 usageLevel, Formatter.FLAG_CALCULATE_ROUNDED | Formatter.FLAG_IEC_UNITS);
252         final SpannableString enlargedValue = new SpannableString(usedResult.value);
253         enlargedValue.setSpan(new RelativeSizeSpan(larger), 0, enlargedValue.length(), FLAGS);
254 
255         final SpannableString amountTemplate = new SpannableString(
256                 context.getString(com.android.internal.R.string.fileSizeSuffix)
257                 .replace("%1$s", "^1").replace("%2$s", "^2"));
258         final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate,
259                 enlargedValue, usedResult.units);
260 
261         final SpannableString fullTemplate = new SpannableString(template);
262         fullTemplate.setSpan(new RelativeSizeSpan(smaller), 0, fullTemplate.length(), FLAGS);
263         return TextUtils.expandTemplate(fullTemplate,
264                 BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
265     }
266 
updateState()267     private void updateState() {
268         PreferenceScreen screen = getPreferenceScreen();
269         for (int i = 1; i < screen.getPreferenceCount(); i++) {
270           Preference currentPreference = screen.getPreference(i);
271           if (currentPreference instanceof TemplatePreferenceCategory) {
272             ((TemplatePreferenceCategory) currentPreference).pushTemplates(services);
273           }
274         }
275     }
276 
277     @Override
getMetricsCategory()278     public int getMetricsCategory() {
279         return SettingsEnums.DATA_USAGE_SUMMARY;
280     }
281 
282     @Override
getNetworkPolicyEditor()283     public NetworkPolicyEditor getNetworkPolicyEditor() {
284         return services.mPolicyEditor;
285     }
286 
287     @Override
getNetworkTemplate()288     public NetworkTemplate getNetworkTemplate() {
289         return mDefaultTemplate;
290     }
291 
292     @Override
updateDataUsage()293     public void updateDataUsage() {
294         updateState();
295         mSummaryController.updateState(mSummaryPreference);
296     }
297 
isGuestUser(Context context)298     private static boolean isGuestUser(Context context) {
299         if (context == null) return false;
300         final UserManager userManager = context.getSystemService(UserManager.class);
301         if (userManager == null) return false;
302         return userManager.isGuestUser();
303     }
304 }
305