Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 282dab7d authored by Rajeev Kumar's avatar Rajeev Kumar Committed by Android (Google) Code Review
Browse files

Merge "Data plan usage summary fragment."

parents 74c03cd8 e19cd482
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
        android:title="@string/data_usage_summary_title">

    <PreferenceCategory
            android:key="data_plan_usage"
            android:title="@string/usage">

    </PreferenceCategory>
</PreferenceScreen>
+30 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:title="@string/data_usage_summary_title">

    <com.android.settings.datausage.TemplatePreferenceCategory
            android:key="data_usage_mobile_category"
            android:title="@string/data_usage_tab_mobile">

        <com.android.settings.datausage.CellDataPreference
                android:key="data_usage_enable"
                android:title="@string/data_usage_enable_mobile" />
    </com.android.settings.datausage.TemplatePreferenceCategory>

</PreferenceScreen>
+399 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.android.settings.datausage;

import static android.net.NetworkPolicy.LIMIT_DISABLED;

import android.annotation.IdRes;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settingslib.NetworkPolicyEditor;
import com.android.settingslib.net.DataUsageController;
import java.util.ArrayList;
import java.util.List;

public class DataPlanUsageSummary extends DataUsageBase implements Indexable {

    public static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
    public static final String KEY_NETWORK_RESTRICTIONS = "network_restrictions";
    private static final String KEY_DATA_PLAN_USAGE = "data_plan_usage";

    private static final String KEY_STATUS_HEADER = "status_header";
    private static final String KEY_LIMIT_SUMMARY = "plan_summary";
    private static final String KEY_MOBILE_USAGE_TITLE = "data_usage_mobile_category";
    private static final String KEY_WIFI_USAGE_TITLE = "wifi_category";

    private DataUsageController mDataUsageController;
    private DataUsageInfoController mDataInfoController;
    private List<DataPlanSummaryPreference> mDataPlanSummaryPreferenceList;
    private Preference mLimitPreference;
    private NetworkTemplate mDefaultTemplate;
    private NetworkRestrictionsPreference mNetworkRestrictionPreference;
    private WifiManager mWifiManager;
    private NetworkPolicyEditor mPolicyEditor;

    @Override
    protected int getHelpResource() {
        return R.string.help_url_data_usage;
    }

    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);

        final Context context = getContext();
        NetworkPolicyManager policyManager = NetworkPolicyManager.from(context);
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        mPolicyEditor = new NetworkPolicyEditor(policyManager);
        mDataUsageController = new DataUsageController(context);
        mDataInfoController = new DataUsageInfoController();

        int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
        boolean hasMobileData = DataUsageUtils.hasMobileData(context);
        if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            hasMobileData = false;
        }
        mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);

        if (!hasMobileData || !isAdmin()) {
            removePreference(KEY_RESTRICT_BACKGROUND);
        }

        if (hasMobileData) {
            addDataPlanSection(defaultSubId);
        }

        if (DataUsageUtils.hasWifiRadio(context)) {
            addWifiSection();
        }

        if (hasEthernet(context)) {
            addEthernetSection();
        }
        setHasOptionsMenu(true);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        if (UserManager.get(getContext()).isAdminUser()) {
            inflater.inflate(R.menu.data_usage, menu);
        }
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.data_usage_menu_cellular_networks: {
                final Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
                startActivity(intent);
                return true;
            }
        }
        return false;
    }

    private void addDataPlanSection(int defaultSubId) {
        Context context = getPrefContext();
        addPreferencesFromResource(R.xml.data_plan_usage);
        PreferenceScreen screen = getPreferenceScreen();
        screen.setTitle(context.getString(R.string.data_usage_summary_title));

        PreferenceCategory preferenceCategory =
                (PreferenceCategory) findPreference(KEY_DATA_PLAN_USAGE);
        screen.addPreference(preferenceCategory);

        Preference dataPlansSyncTimePreference = new Preference(context);
        dataPlansSyncTimePreference.setLayoutResource(R.layout.data_plans_sync_time_preference);
        dataPlansSyncTimePreference.setTitle(MockDataPlanUsage.SYNC_TIME);
        preferenceCategory.addPreference(dataPlansSyncTimePreference);

        mDataPlanSummaryPreferenceList = new ArrayList<>(MockDataPlanUsage.DATA_PLAN_USAGES.length);
        for (int i = 0; i < MockDataPlanUsage.DATA_PLAN_USAGES.length; i++) {
            DataPlanSummaryPreference dataPlanSummaryPreference =
                    new DataPlanSummaryPreference(context);
            dataPlanSummaryPreference.setKey(KEY_STATUS_HEADER + (i + 1));
            mDataPlanSummaryPreferenceList.add(dataPlanSummaryPreference);
            preferenceCategory.addPreference(dataPlanSummaryPreference);
        }

        Preference preference = new Preference(context);
        preference.setLayoutResource(R.layout.manage_data_plans_preference);
        preferenceCategory.addPreference(preference);
        setPreferenceScreen(screen);

        mLimitPreference = findPreference(KEY_LIMIT_SUMMARY);
        List<SubscriptionInfo> subscriptions =
                services.mSubscriptionManager.getActiveSubscriptionInfoList();

        if (subscriptions == null || subscriptions.isEmpty()) {
            addMobileSection(defaultSubId);
        }

        for (int i = 0, subscriptionsSize = subscriptions != null ? subscriptions.size() : 0;
                i < subscriptionsSize; i++) {
            SubscriptionInfo subInfo = subscriptions.get(i);
            if (subscriptionsSize > 1) {
                addMobileSection(subInfo.getSubscriptionId(), subInfo);
            } else {
                addMobileSection(subInfo.getSubscriptionId());
            }
        }
    }

    private void addMobileSection(int subId) {
        addMobileSection(subId, null);
    }

    private void addMobileSection(int subId, SubscriptionInfo subInfo) {
        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
                inflatePreferences(R.xml.data_plan_usage_cell_data_preference_screen);
        category.setTemplate(getNetworkTemplate(subId), subId, services);
        category.pushTemplates(services);
        if (subInfo != null && !TextUtils.isEmpty(subInfo.getDisplayName())) {
            Preference title = category.findPreference(KEY_MOBILE_USAGE_TITLE);
            title.setTitle(subInfo.getDisplayName());
        }
    }

    private void addWifiSection() {
        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
                inflatePreferences(R.xml.data_usage_wifi);
        category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0 /* subId */, services);
        mNetworkRestrictionPreference =
                (NetworkRestrictionsPreference) category.findPreference(KEY_NETWORK_RESTRICTIONS);
    }

    private void addEthernetSection() {
        TemplatePreferenceCategory category = (TemplatePreferenceCategory)
                inflatePreferences(R.xml.data_usage_ethernet);
        category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0 /* subId */, services);
    }

    private Preference inflatePreferences(@IdRes int resId) {
        PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
                getPrefContext(), resId, null);
        Preference pref = rootPreferences.getPreference(0);
        rootPreferences.removeAll();

        PreferenceScreen screen = getPreferenceScreen();
        pref.setOrder(screen.getPreferenceCount());
        screen.addPreference(pref);

        return pref;
    }

    private NetworkTemplate getNetworkTemplate(int subscriptionId) {
        NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
                services.mTelephonyManager.getSubscriberId(subscriptionId));
        return NetworkTemplate.normalize(mobileAll,
                services.mTelephonyManager.getMergedSubscriberIds());
    }

    @Override
    public void onResume() {
        super.onResume();
        updateState();
    }

    private void updateState() {
        DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
                mDefaultTemplate);

        Context context = getContext();
        mDataInfoController.updateDataLimit(info,
                services.mPolicyEditor.getPolicy(mDefaultTemplate));

        // TODO(b/63391323): Get rid of MockDataPlanUsage once we integrate with data plan APIs
        if (mDataPlanSummaryPreferenceList != null && !mDataPlanSummaryPreferenceList.isEmpty()) {
            MockDataPlanUsage[] dataPlanUsages = MockDataPlanUsage.getDataPlanUsage();
            for (int i = 0; i < dataPlanUsages.length; i++) {
                DataPlanSummaryPreference dataPlanSummaryPreference =
                        mDataPlanSummaryPreferenceList.get(i);
                MockDataPlanUsage dataPlanUsage = dataPlanUsages[i];
                dataPlanSummaryPreference.setTitle(dataPlanUsage.mUsage);
                dataPlanSummaryPreference.setUsageTextColor(dataPlanUsage.mUsageTextColor);
                dataPlanSummaryPreference.setName(dataPlanUsage.mName);
                dataPlanSummaryPreference.setPercentageUsage(dataPlanUsage.mPercentageUsage);
                dataPlanSummaryPreference
                        .setMeterBackgroundColor(dataPlanUsage.mMeterBackgroundColor);
                dataPlanSummaryPreference.setMeterConsumedColor(dataPlanUsage.mMeterConsumedColor);
                dataPlanSummaryPreference.setDescription(dataPlanUsage.mDescription);
            }
        }

        if (mLimitPreference != null && (info.warningLevel > 0 || info.limitLevel > 0)) {
            String warning = Formatter.formatFileSize(context, info.warningLevel);
            String limit = Formatter.formatFileSize(context, info.limitLevel);
            mLimitPreference.setSummary(getString(info.limitLevel <= 0 ? R.string.cell_warning_only
                    : R.string.cell_warning_and_limit, warning, limit));
        } else if (mLimitPreference != null) {
            mLimitPreference.setSummary(null);
        }

        updateNetworkRestrictionSummary(mNetworkRestrictionPreference);

        PreferenceScreen screen = getPreferenceScreen();
        for (int i = 1, preferenceCount = screen.getPreferenceCount(); i < preferenceCount; i++) {
            ((TemplatePreferenceCategory) screen.getPreference(i)).pushTemplates(services);
        }
    }

    @Override
    public int getMetricsCategory() {
        return MetricsEvent.DATA_USAGE_SUMMARY;
    }

    @VisibleForTesting
    void updateNetworkRestrictionSummary(NetworkRestrictionsPreference preference) {
        if (preference == null) {
            return;
        }
        mPolicyEditor.read();
        int count = 0;
        List<WifiConfiguration> configuredNetworks = mWifiManager.getConfiguredNetworks();
        for (int i = 0, configuredNetworksSize = configuredNetworks.size();
                i < configuredNetworksSize; i++) {
            WifiConfiguration config = configuredNetworks.get(i);
            if (isMetered(config)) {
                count++;
            }
        }
        preference.setSummary(getResources().getQuantityString(
                R.plurals.network_restrictions_summary, count, count));
    }

    @VisibleForTesting
    boolean isMetered(WifiConfiguration config) {
        if (config.SSID == null) {
            return false;
        }
        final String networkId = config.isPasspoint() ? config.providerFriendlyName : config.SSID;
        final NetworkPolicy policy =
                mPolicyEditor.getPolicyMaybeUnquoted(NetworkTemplate.buildTemplateWifi(networkId));
        if (policy == null) {
            return false;
        }
        if (policy.limitBytes != LIMIT_DISABLED) {
            return true;
        }
        return policy.metered;
    }

    private static class SummaryProvider
            implements SummaryLoader.SummaryProvider {

        private final Activity mActivity;
        private final SummaryLoader mSummaryLoader;
        private final DataUsageController mDataController;

        public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
            mActivity = activity;
            mSummaryLoader = summaryLoader;
            mDataController = new DataUsageController(activity);
        }

        @Override
        public void setListening(boolean listening) {
            if (listening) {
                DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
                String used;
                if (info == null) {
                    used = Formatter.formatFileSize(mActivity, 0);
                } else if (info.limitLevel <= 0) {
                    used = Formatter.formatFileSize(mActivity, info.usageLevel);
                } else {
                    used = Utils.formatPercentage(info.usageLevel, info.limitLevel);
                }
                mSummaryLoader.setSummary(this,
                        mActivity.getString(R.string.data_usage_summary_format, used));
            }
        }
    }

    public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
            = SummaryProvider::new;

    /**
     * For search
     */
    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
            new BaseSearchIndexProvider() {

                @Override
                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
                        boolean enabled) {
                    List<SearchIndexableResource> resources = new ArrayList<>();
                    SearchIndexableResource resource = new SearchIndexableResource(context);
                    resource.xmlResId = R.xml.data_plan_usage;
                    resources.add(resource);

                    if (DataUsageUtils.hasMobileData(context)) {
                        resource = new SearchIndexableResource(context);
                        resource.xmlResId = R.xml.data_plan_usage_cell_data_preference_screen;
                        resources.add(resource);
                    }
                    if (DataUsageUtils.hasWifiRadio(context)) {
                        resource = new SearchIndexableResource(context);
                        resource.xmlResId = R.xml.data_usage_wifi;
                        resources.add(resource);
                    }
                    return resources;
                }

                @Override
                public List<String> getNonIndexableKeys(Context context) {
                    List<String> keys = super.getNonIndexableKeys(context);

                    if (DataUsageUtils.hasMobileData(context)) {
                        keys.add(KEY_RESTRICT_BACKGROUND);
                    }
                    if (DataUsageUtils.hasWifiRadio(context)) {
                        keys.add(KEY_NETWORK_RESTRICTIONS);
                    }
                    keys.add(KEY_WIFI_USAGE_TITLE);

                    return keys;
                }
            };
}
+39 −1
Original line number Diff line number Diff line
@@ -14,14 +14,21 @@

package com.android.settings.datausage;

import static android.net.ConnectivityManager.TYPE_ETHERNET;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -31,6 +38,7 @@ import com.android.settingslib.NetworkPolicyEditor;

public abstract class DataUsageBase extends SettingsPreferenceFragment {
    private static final String TAG = "DataUsageBase";
    private static final String ETHERNET = "ethernet";

    protected final TemplatePreference.NetworkServices services =
            new TemplatePreference.NetworkServices();
@@ -83,8 +91,38 @@ public abstract class DataUsageBase extends SettingsPreferenceFragment {
        try {
            return services.mNetworkService.isBandwidthControlEnabled();
        } catch (RemoteException e) {
            Log.w(TAG, "problem talking with INetworkManagementService: " + e);
            Log.w(TAG, "problem talking with INetworkManagementService: ", e);
            return false;
        }
    }

    /**
     * Test if device has an ethernet network connection.
     */
    public boolean hasEthernet(Context context) {
        if (DataUsageUtils.TEST_RADIOS) {
            return SystemProperties.get(DataUsageUtils.TEST_RADIOS_PROP).contains(ETHERNET);
        }

        final ConnectivityManager conn = ConnectivityManager.from(context);
        final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);

        final long ethernetBytes;
        try {
            INetworkStatsSession statsSession = services.mStatsService.openSession();
            if (statsSession != null) {
                ethernetBytes = statsSession.getSummaryForNetwork(
                        NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
                        .getTotalBytes();
                TrafficStats.closeQuietly(statsSession);
            } else {
                ethernetBytes = 0;
            }
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }

        // only show ethernet when both hardware present and traffic has occurred
        return hasEthernet && ethernetBytes > 0;
    }
}
+6 −30
Original line number Diff line number Diff line
@@ -58,6 +58,12 @@ import com.android.settingslib.net.DataUsageController;
import java.util.ArrayList;
import java.util.List;

/**
 * Settings preference fragment that displays data usage summary.
 *
 * This class in deprecated use {@link DataPlanUsageSummary}.
 */
@Deprecated
public class DataUsageSummary extends DataUsageBase implements Indexable, DataUsageEditController {

    static final boolean LOGD = false;
@@ -311,36 +317,6 @@ public class DataUsageSummary extends DataUsageBase implements Indexable, DataUs
        updateState();
    }

    /**
     * Test if device has an ethernet network connection.
     */
    public boolean hasEthernet(Context context) {
        if (DataUsageUtils.TEST_RADIOS) {
            return SystemProperties.get(DataUsageUtils.TEST_RADIOS_PROP).contains("ethernet");
        }

        final ConnectivityManager conn = ConnectivityManager.from(context);
        final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);

        final long ethernetBytes;
        try {
            INetworkStatsSession statsSession = services.mStatsService.openSession();
            if (statsSession != null) {
                ethernetBytes = statsSession.getSummaryForNetwork(
                        NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
                        .getTotalBytes();
                TrafficStats.closeQuietly(statsSession);
            } else {
                ethernetBytes = 0;
            }
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }

        // only show ethernet when both hardware present and traffic has occurred
        return hasEthernet && ethernetBytes > 0;
    }

    @VisibleForTesting
    void updateNetworkRestrictionSummary(NetworkRestrictionsPreference preference) {
        if (preference == null) {
Loading