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