1/*
2 * Copyright (C) 2017 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
15package com.android.settings.datausage;
16
17import static android.net.NetworkPolicy.LIMIT_DISABLED;
18
19import android.annotation.IdRes;
20import android.app.Activity;
21import android.content.Context;
22import android.content.Intent;
23import android.net.NetworkPolicy;
24import android.net.NetworkPolicyManager;
25import android.net.NetworkTemplate;
26import android.net.wifi.WifiConfiguration;
27import android.net.wifi.WifiManager;
28import android.os.Bundle;
29import android.os.UserManager;
30import android.provider.Settings;
31import android.support.annotation.VisibleForTesting;
32import android.support.v7.preference.Preference;
33import android.support.v7.preference.PreferenceCategory;
34import android.support.v7.preference.PreferenceScreen;
35import android.telephony.SubscriptionInfo;
36import android.telephony.SubscriptionManager;
37import android.text.TextUtils;
38import android.text.format.Formatter;
39import android.view.Menu;
40import android.view.MenuInflater;
41import android.view.MenuItem;
42import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
43import com.android.settings.R;
44import com.android.settings.Utils;
45import com.android.settings.dashboard.SummaryLoader;
46import com.android.settingslib.NetworkPolicyEditor;
47import com.android.settingslib.net.DataUsageController;
48import java.util.ArrayList;
49import java.util.List;
50
51public class DataPlanUsageSummary extends DataUsageBase {
52
53    public static final String KEY_DATA_PLAN_USAGE = "data_plan_usage";
54
55    public static final String KEY_STATUS_HEADER = "status_header";
56    public static final String KEY_LIMIT_SUMMARY = "plan_summary";
57
58    // Mobile data keys
59    public static final String KEY_MOBILE_USAGE_TITLE = "data_usage_mobile_category";
60    public static final String KEY_MOBILE_DATA_USAGE_TOGGLE = "data_usage_enable";
61
62    // Wifi keys
63    public static final String KEY_WIFI_USAGE_TITLE = "wifi_category";
64    public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage";
65    public static final String KEY_NETWORK_RESTRICTIONS = "network_restrictions";
66
67    private DataUsageController mDataUsageController;
68    private DataUsageInfoController mDataInfoController;
69    private List<DataPlanSummaryPreference> mDataPlanSummaryPreferenceList;
70    private Preference mLimitPreference;
71    private NetworkTemplate mDefaultTemplate;
72    private NetworkRestrictionsPreference mNetworkRestrictionPreference;
73    private WifiManager mWifiManager;
74    private NetworkPolicyEditor mPolicyEditor;
75
76    @Override
77    protected int getHelpResource() {
78        return R.string.help_url_data_usage;
79    }
80
81    @Override
82    public void onCreate(Bundle bundle) {
83        super.onCreate(bundle);
84
85        final Context context = getContext();
86        NetworkPolicyManager policyManager = NetworkPolicyManager.from(context);
87        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
88        mPolicyEditor = new NetworkPolicyEditor(policyManager);
89        mDataUsageController = new DataUsageController(context);
90        mDataInfoController = new DataUsageInfoController();
91
92        int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
93        boolean hasMobileData = DataUsageUtils.hasMobileData(context);
94        if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
95            hasMobileData = false;
96        }
97        mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
98
99        if (hasMobileData) {
100            addDataPlanSection(defaultSubId);
101        }
102
103        if (DataUsageUtils.hasWifiRadio(context)) {
104            addWifiSection();
105        }
106
107        if (hasEthernet(context)) {
108            addEthernetSection();
109        }
110        setHasOptionsMenu(true);
111    }
112
113    @Override
114    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
115        if (UserManager.get(getContext()).isAdminUser()) {
116            inflater.inflate(R.menu.data_usage, menu);
117        }
118        super.onCreateOptionsMenu(menu, inflater);
119    }
120
121    @Override
122    public boolean onOptionsItemSelected(MenuItem item) {
123        switch (item.getItemId()) {
124            case R.id.data_usage_menu_cellular_networks: {
125                final Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
126                startActivity(intent);
127                return true;
128            }
129        }
130        return false;
131    }
132
133    private void addDataPlanSection(int defaultSubId) {
134        Context context = getPrefContext();
135        addPreferencesFromResource(R.xml.data_plan_usage);
136        PreferenceScreen screen = getPreferenceScreen();
137        screen.setTitle(context.getString(R.string.data_usage_summary_title));
138
139        PreferenceCategory preferenceCategory =
140                (PreferenceCategory) findPreference(KEY_DATA_PLAN_USAGE);
141        screen.addPreference(preferenceCategory);
142
143        Preference dataPlansSyncTimePreference = new Preference(context);
144        dataPlansSyncTimePreference.setLayoutResource(R.layout.data_plans_sync_time_preference);
145        dataPlansSyncTimePreference.setTitle(MockDataPlanUsage.SYNC_TIME);
146        preferenceCategory.addPreference(dataPlansSyncTimePreference);
147
148        mDataPlanSummaryPreferenceList = new ArrayList<>(MockDataPlanUsage.DATA_PLAN_USAGES.length);
149        for (int i = 0; i < MockDataPlanUsage.DATA_PLAN_USAGES.length; i++) {
150            DataPlanSummaryPreference dataPlanSummaryPreference =
151                    new DataPlanSummaryPreference(context);
152            dataPlanSummaryPreference.setKey(KEY_STATUS_HEADER + (i + 1));
153            mDataPlanSummaryPreferenceList.add(dataPlanSummaryPreference);
154            preferenceCategory.addPreference(dataPlanSummaryPreference);
155        }
156
157        Preference preference = new Preference(context);
158        preference.setLayoutResource(R.layout.manage_data_plans_preference);
159        preferenceCategory.addPreference(preference);
160        setPreferenceScreen(screen);
161
162        mLimitPreference = findPreference(KEY_LIMIT_SUMMARY);
163        List<SubscriptionInfo> subscriptions =
164                services.mSubscriptionManager.getActiveSubscriptionInfoList();
165
166        if (subscriptions == null || subscriptions.isEmpty()) {
167            addMobileSection(defaultSubId);
168        }
169
170        for (int i = 0, subscriptionsSize = subscriptions != null ? subscriptions.size() : 0;
171                i < subscriptionsSize; i++) {
172            SubscriptionInfo subInfo = subscriptions.get(i);
173            if (subscriptionsSize > 1) {
174                addMobileSection(subInfo.getSubscriptionId(), subInfo);
175            } else {
176                addMobileSection(subInfo.getSubscriptionId());
177            }
178        }
179    }
180
181    private void addMobileSection(int subId) {
182        addMobileSection(subId, null);
183    }
184
185    private void addMobileSection(int subId, SubscriptionInfo subInfo) {
186        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
187                inflatePreferences(R.xml.data_plan_usage_cell_data_preference_screen);
188        category.setTemplate(getNetworkTemplate(subId), subId, services);
189        category.pushTemplates(services);
190        if (subInfo != null && !TextUtils.isEmpty(subInfo.getDisplayName())) {
191            Preference title = category.findPreference(KEY_MOBILE_USAGE_TITLE);
192            title.setTitle(subInfo.getDisplayName());
193        }
194    }
195
196    private void addWifiSection() {
197        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
198                inflatePreferences(R.xml.data_usage_wifi);
199        category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0 /* subId */, services);
200        mNetworkRestrictionPreference =
201                (NetworkRestrictionsPreference) category.findPreference(KEY_NETWORK_RESTRICTIONS);
202    }
203
204    private void addEthernetSection() {
205        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
206                inflatePreferences(R.xml.data_usage_ethernet);
207        category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0 /* subId */, services);
208    }
209
210    private Preference inflatePreferences(@IdRes int resId) {
211        PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
212                getPrefContext(), resId, null);
213        Preference pref = rootPreferences.getPreference(0);
214        rootPreferences.removeAll();
215
216        PreferenceScreen screen = getPreferenceScreen();
217        pref.setOrder(screen.getPreferenceCount());
218        screen.addPreference(pref);
219
220        return pref;
221    }
222
223    private NetworkTemplate getNetworkTemplate(int subscriptionId) {
224        NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
225                services.mTelephonyManager.getSubscriberId(subscriptionId));
226        return NetworkTemplate.normalize(mobileAll,
227                services.mTelephonyManager.getMergedSubscriberIds());
228    }
229
230    @Override
231    public void onResume() {
232        super.onResume();
233        updateState();
234    }
235
236    private void updateState() {
237        DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
238                mDefaultTemplate);
239
240        Context context = getContext();
241        mDataInfoController.updateDataLimit(info,
242                services.mPolicyEditor.getPolicy(mDefaultTemplate));
243
244        // TODO(b/63391323): Get rid of MockDataPlanUsage once we integrate with data plan APIs
245        if (mDataPlanSummaryPreferenceList != null && !mDataPlanSummaryPreferenceList.isEmpty()) {
246            MockDataPlanUsage[] dataPlanUsages = MockDataPlanUsage.getDataPlanUsage();
247            for (int i = 0; i < dataPlanUsages.length; i++) {
248                DataPlanSummaryPreference dataPlanSummaryPreference =
249                        mDataPlanSummaryPreferenceList.get(i);
250                MockDataPlanUsage dataPlanUsage = dataPlanUsages[i];
251                dataPlanSummaryPreference.setTitle(dataPlanUsage.mUsage);
252                dataPlanSummaryPreference.setUsageTextColor(dataPlanUsage.mUsageTextColor);
253                dataPlanSummaryPreference.setName(dataPlanUsage.mName);
254                dataPlanSummaryPreference.setPercentageUsage(dataPlanUsage.mPercentageUsage);
255                dataPlanSummaryPreference
256                        .setMeterBackgroundColor(dataPlanUsage.mMeterBackgroundColor);
257                dataPlanSummaryPreference.setMeterConsumedColor(dataPlanUsage.mMeterConsumedColor);
258                dataPlanSummaryPreference.setDescription(dataPlanUsage.mDescription);
259            }
260        }
261
262        if (mLimitPreference != null && (info.warningLevel > 0 || info.limitLevel > 0)) {
263            String warning = Formatter.formatFileSize(context, info.warningLevel);
264            String limit = Formatter.formatFileSize(context, info.limitLevel);
265            mLimitPreference.setSummary(getString(info.limitLevel <= 0 ? R.string.cell_warning_only
266                    : R.string.cell_warning_and_limit, warning, limit));
267        } else if (mLimitPreference != null) {
268            mLimitPreference.setSummary(null);
269        }
270
271        updateNetworkRestrictionSummary(mNetworkRestrictionPreference);
272
273        PreferenceScreen screen = getPreferenceScreen();
274        for (int i = 1, preferenceCount = screen.getPreferenceCount(); i < preferenceCount; i++) {
275            ((TemplatePreferenceCategory) screen.getPreference(i)).pushTemplates(services);
276        }
277    }
278
279    @Override
280    public int getMetricsCategory() {
281        return MetricsEvent.DATA_USAGE_SUMMARY;
282    }
283
284    @VisibleForTesting
285    void updateNetworkRestrictionSummary(NetworkRestrictionsPreference preference) {
286        if (preference == null) {
287            return;
288        }
289        mPolicyEditor.read();
290        int count = 0;
291        List<WifiConfiguration> configuredNetworks = mWifiManager.getConfiguredNetworks();
292        for (int i = 0, configuredNetworksSize = configuredNetworks.size();
293                i < configuredNetworksSize; i++) {
294            WifiConfiguration config = configuredNetworks.get(i);
295            if (isMetered(config)) {
296                count++;
297            }
298        }
299        preference.setSummary(getResources().getQuantityString(
300                R.plurals.network_restrictions_summary, count, count));
301    }
302
303    @VisibleForTesting
304    boolean isMetered(WifiConfiguration config) {
305        if (config.SSID == null) {
306            return false;
307        }
308        final String networkId = config.isPasspoint() ? config.providerFriendlyName : config.SSID;
309        final NetworkPolicy policy =
310                mPolicyEditor.getPolicyMaybeUnquoted(NetworkTemplate.buildTemplateWifi(networkId));
311        if (policy == null) {
312            return false;
313        }
314        if (policy.limitBytes != LIMIT_DISABLED) {
315            return true;
316        }
317        return policy.metered;
318    }
319
320    private static class SummaryProvider
321            implements SummaryLoader.SummaryProvider {
322
323        private final Activity mActivity;
324        private final SummaryLoader mSummaryLoader;
325        private final DataUsageController mDataController;
326
327        public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
328            mActivity = activity;
329            mSummaryLoader = summaryLoader;
330            mDataController = new DataUsageController(activity);
331        }
332
333        @Override
334        public void setListening(boolean listening) {
335            if (listening) {
336                DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
337                String used;
338                if (info == null) {
339                    used = Formatter.formatFileSize(mActivity, 0);
340                } else if (info.limitLevel <= 0) {
341                    used = Formatter.formatFileSize(mActivity, info.usageLevel);
342                } else {
343                    used = Utils.formatPercentage(info.usageLevel, info.limitLevel);
344                }
345                mSummaryLoader.setSummary(this,
346                        mActivity.getString(R.string.data_usage_summary_format, used));
347            }
348        }
349    }
350
351    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
352            = SummaryProvider::new;
353}
354
355