Add "Mobile data" and "Data usage" into Mobile network setting UI.
Bug: 62201918
Test: Manual
Change-Id: I8108c4910d57fa33df371b086b99a7ea2fa83bc3
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 195fc7a..07733d0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -172,6 +172,8 @@
presses home. -->
<uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<application android:name="PhoneApp"
android:persistent="true"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f6d0bd0..0957b02 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -497,6 +497,14 @@
<string name="mobile_data_settings_title">Mobile Data</string>
<!-- Mobile network settings screen, title of Mobile Data switch preference -->
<string name="mobile_data_settings_summary">Access data using mobile network</string>
+ <!-- Message to show when user trying to turn off mobile data, in single sim mode [CHAR LIMIT=100]-->
+ <string name="data_usage_disable_mobile">Turn off mobile data?</string>
+ <!-- When a SIM preference hasn't been selected yet, this string is displayed as the pref summary until the user chooses a SIM subscription from the preference list [CHAR LIMIT=50] -->
+ <string name="sim_selection_required_pref">Selection required</string>
+ <!-- Title asking user if they wish to change the default sim for cellular data. [CHAR LIMIT=30] -->
+ <string name="sim_change_data_title">Change data SIM?</string>
+ <!-- Message confirming the user wishes to change the default data SIM from one to another. [CHAR LIMIT=NONE] -->
+ <string name="sim_change_data_message">Use <xliff:g id="new_sim">%1$s</xliff:g> instead of <xliff:g id="old_sim">%2$s</xliff:g> for mobile data?</string>
<!-- Mobile network settings screen, title of Wi-Fi calling setting -->
<string name="wifi_calling_settings_title">Wi-Fi calling</string>
<!-- Mobile network settings screen, title of Video calling setting -->
diff --git a/res/xml/network_setting_fragment.xml b/res/xml/network_setting_fragment.xml
index 42a8e47..69508f0 100644
--- a/res/xml/network_setting_fragment.xml
+++ b/res/xml/network_setting_fragment.xml
@@ -21,6 +21,11 @@
android:title="@string/cdma_lte_data_service">
</PreferenceScreen>
+ <com.android.phone.MobileDataPreference
+ android:key="mobile_data_enable"
+ android:title="@string/mobile_data_settings_title"
+ android:summary="@string/mobile_data_settings_summary"/>
+
<com.android.phone.RestrictedSwitchPreference
android:key="button_roaming_key"
android:title="@string/roaming"
@@ -28,6 +33,10 @@
android:summaryOn="@string/roaming_enable"
android:summaryOff="@string/roaming_disable"/>
+ <com.android.phone.DataUsagePreference
+ android:key="data_usage_summary"
+ android:title="@string/data_usage_title" />
+
<ListPreference
android:key="preferred_network_mode_key"
android:title="@string/preferred_network_mode_title"
diff --git a/src/com/android/phone/DataUsagePreference.java b/src/com/android/phone/DataUsagePreference.java
new file mode 100644
index 0000000..174ff6a
--- /dev/null
+++ b/src/com/android/phone/DataUsagePreference.java
@@ -0,0 +1,82 @@
+/*
+ * 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.phone;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+
+import com.android.settingslib.net.DataUsageController;
+
+/**
+ * The preference that shows mobile data usage summary and
+ * leads to mobile data usage list page.
+ */
+public class DataUsagePreference extends Preference {
+
+ private NetworkTemplate mTemplate;
+ private int mSubId;
+
+ public DataUsagePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * After creating this preference, this functions needs to be called to
+ * initialize which subID it connects to.
+ */
+ public void initialize(int subId) {
+ Activity activity = (Activity) getContext();
+
+ mSubId = subId;
+ mTemplate = getNetworkTemplate(activity, subId);
+
+ DataUsageController controller = new DataUsageController(activity);
+
+ DataUsageController.DataUsageInfo usageInfo = controller.getDataUsageInfo(mTemplate);
+ setSummary(activity.getString(R.string.data_usage_template,
+ Formatter.formatFileSize(activity, usageInfo.usageLevel), usageInfo.period));
+ setIntent(getIntent());
+ }
+
+ @Override
+ public Intent getIntent() {
+ Bundle args = new Bundle();
+ Intent intent = new Intent(Settings.ACTION_MOBILE_DATA_USAGE);
+
+ intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mTemplate);
+ intent.putExtra(Settings.EXTRA_SUB_ID, mSubId);
+
+ return intent;
+ }
+
+ private NetworkTemplate getNetworkTemplate(Activity activity, int subId) {
+ TelephonyManager tm = (TelephonyManager) activity
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
+ tm.getSubscriberId(subId));
+ return NetworkTemplate.normalize(mobileAll,
+ tm.getMergedSubscriberIds());
+ }
+}
diff --git a/src/com/android/phone/MobileDataPreference.java b/src/com/android/phone/MobileDataPreference.java
new file mode 100644
index 0000000..e1d0528
--- /dev/null
+++ b/src/com/android/phone/MobileDataPreference.java
@@ -0,0 +1,304 @@
+/*
+ * 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.phone;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.preference.DialogPreference;
+import android.preference.PreferenceScreen;
+import android.provider.Settings.Global;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Checkable;
+
+import java.util.List;
+
+/**
+ * Customized Preference to enable / disable mobile data.
+ * Basically copy of with com.android.settings.CellDataPreference.
+ */
+public class MobileDataPreference extends DialogPreference {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "MobileDataPreference";
+
+ public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ public boolean mChecked;
+ public boolean mMultiSimDialog;
+ private TelephonyManager mTelephonyManager;
+ private SubscriptionManager mSubscriptionManager;
+
+ public MobileDataPreference(Context context, AttributeSet attrs) {
+ super(context, attrs, com.android.internal.R.attr.switchPreferenceStyle);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable s) {
+ CellDataState state = (CellDataState) s;
+ super.onRestoreInstanceState(state.getSuperState());
+ mTelephonyManager = TelephonyManager.from(getContext());
+ mChecked = state.mChecked;
+ mMultiSimDialog = state.mMultiSimDialog;
+ if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ mSubId = state.mSubId;
+ setKey(getKey() + mSubId);
+ }
+ notifyChanged();
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ CellDataState state = new CellDataState(super.onSaveInstanceState());
+ state.mChecked = mChecked;
+ state.mMultiSimDialog = mMultiSimDialog;
+ state.mSubId = mSubId;
+ return state;
+ }
+
+ @Override
+ protected void onAttachedToActivity() {
+ super.onAttachedToActivity();
+ mListener.setListener(true, mSubId, getContext());
+ }
+
+ @Override
+ protected void onPrepareForRemoval() {
+ mListener.setListener(false, mSubId, getContext());
+ super.onPrepareForRemoval();
+ }
+
+ /**
+ * Initialize this preference with subId.
+ */
+ public void initialize(int subId) {
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ throw new IllegalArgumentException("MobileDataPreference needs a SubscriptionInfo");
+ }
+ mSubscriptionManager = SubscriptionManager.from(getContext());
+ mTelephonyManager = TelephonyManager.from(getContext());
+ if (mSubId != subId) {
+ mSubId = subId;
+ setKey(getKey() + subId);
+ }
+ updateChecked();
+ }
+
+ private void updateChecked() {
+ setChecked(mTelephonyManager.getDataEnabled(mSubId));
+ }
+
+ @Override
+ public void performClick(PreferenceScreen preferenceScreen) {
+ final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
+ mSubId);
+ final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
+ boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
+ if (mChecked) {
+ // If the device is single SIM or is enabling data on the active data SIM then forgo
+ // the pop-up.
+ if (isMultiSim || (nextSir != null && currentSir != null
+ && currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
+ setMobileDataEnabled(false);
+ if (nextSir != null && currentSir != null
+ && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
+ disableDataForOtherSubscriptions(mSubId);
+ }
+ return;
+ }
+ // disabling data; show confirmation dialog which eventually
+ // calls setMobileDataEnabled() once user confirms.
+ mMultiSimDialog = false;
+ super.performClick(preferenceScreen);
+ } else {
+ // If we are showing the Sim Card tile then we are a Multi-Sim device.
+ if (isMultiSim) {
+ mMultiSimDialog = true;
+ if (nextSir != null && currentSir != null
+ && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
+ setMobileDataEnabled(true);
+ disableDataForOtherSubscriptions(mSubId);
+ return;
+ }
+ super.performClick(preferenceScreen);
+ } else {
+ setMobileDataEnabled(true);
+ }
+ }
+ }
+
+ private void setMobileDataEnabled(boolean enabled) {
+ if (DBG) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")");
+ mTelephonyManager.setDataEnabled(mSubId, enabled);
+ setChecked(enabled);
+ }
+
+ private void setChecked(boolean checked) {
+ if (mChecked == checked) return;
+ mChecked = checked;
+ notifyChanged();
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+ View checkableView = view.findViewById(com.android.internal.R.id.switch_widget);
+ checkableView.setClickable(false);
+ ((Checkable) checkableView).setChecked(mChecked);
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ if (mMultiSimDialog) {
+ showMultiSimDialog(builder);
+ } else {
+ showDisableDialog(builder);
+ }
+ }
+
+ private void showDisableDialog(AlertDialog.Builder builder) {
+ builder.setTitle(null)
+ .setMessage(R.string.data_usage_disable_mobile)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, null);
+ }
+
+ private void showMultiSimDialog(AlertDialog.Builder builder) {
+ final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
+ final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
+
+ final String previousName = (nextSir == null)
+ ? getContext().getResources().getString(R.string.sim_selection_required_pref)
+ : nextSir.getDisplayName().toString();
+
+ builder.setTitle(R.string.sim_change_data_title);
+ builder.setMessage(getContext().getString(R.string.sim_change_data_message,
+ String.valueOf(currentSir != null ? currentSir.getDisplayName() : null),
+ previousName));
+
+ builder.setPositiveButton(R.string.ok, this);
+ builder.setNegativeButton(R.string.cancel, null);
+ }
+
+ private void disableDataForOtherSubscriptions(int subId) {
+ List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
+ if (subInfoList != null) {
+ for (SubscriptionInfo subInfo : subInfoList) {
+ if (subInfo.getSubscriptionId() != subId) {
+ mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which != DialogInterface.BUTTON_POSITIVE) {
+ return;
+ }
+ if (mMultiSimDialog) {
+ mSubscriptionManager.setDefaultDataSubId(mSubId);
+ setMobileDataEnabled(true);
+ disableDataForOtherSubscriptions(mSubId);
+ } else {
+ // TODO: extend to modify policy enabled flag.
+ setMobileDataEnabled(false);
+ }
+ }
+
+ private final DataStateListener mListener = new DataStateListener() {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateChecked();
+ }
+ };
+
+ /**
+ * Listener that listens mobile data state change.
+ */
+ public abstract static class DataStateListener extends ContentObserver {
+ public DataStateListener() {
+ super(new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Set / Unset data state listening, specifying subId.
+ */
+ public void setListener(boolean listening, int subId, Context context) {
+ if (listening) {
+ Uri uri = Global.getUriFor(Global.MOBILE_DATA);
+ if (TelephonyManager.getDefault().getSimCount() != 1) {
+ uri = Global.getUriFor(Global.MOBILE_DATA + subId);
+ }
+ context.getContentResolver().registerContentObserver(uri, false, this);
+ } else {
+ context.getContentResolver().unregisterContentObserver(this);
+ }
+ }
+ }
+
+ /**
+ * Class that represents state of mobile data state.
+ * Used by onSaveInstanceState and onRestoreInstanceState.
+ */
+ public static class CellDataState extends BaseSavedState {
+ public int mSubId;
+ public boolean mChecked;
+ public boolean mMultiSimDialog;
+
+ public CellDataState(Parcelable base) {
+ super(base);
+ }
+
+ public CellDataState(Parcel source) {
+ super(source);
+ mChecked = source.readByte() != 0;
+ mMultiSimDialog = source.readByte() != 0;
+ mSubId = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeByte((byte) (mChecked ? 1 : 0));
+ dest.writeByte((byte) (mMultiSimDialog ? 1 : 0));
+ dest.writeInt(mSubId);
+ }
+
+ public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
+ @Override
+ public CellDataState createFromParcel(Parcel source) {
+ return new CellDataState(source);
+ }
+
+ @Override
+ public CellDataState[] newArray(int size) {
+ return new CellDataState[size];
+ }
+ };
+ }
+}
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index b5f49eb..44506b7 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -65,6 +65,8 @@
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
@@ -72,6 +74,7 @@
import com.android.phone.settings.PhoneAccountSettingsFragment;
import com.android.settingslib.RestrictedLockUtils;
+
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -152,6 +155,8 @@
private static final String BUTTON_WIFI_CALLING_KEY = "wifi_calling_key";
private static final String BUTTON_VIDEO_CALLING_KEY = "video_calling_key";
private static final String CATEGORY_CALLING_KEY = "calling";
+ private static final String BUTTON_MOBILE_DATA_ENABLE_KEY = "mobile_data_enable";
+ private static final String BUTTON_DATA_USAGE_KEY = "data_usage_summary";
private final BroadcastReceiver mPhoneChangeReceiver = new PhoneChangeReceiver();
@@ -176,6 +181,8 @@
private Preference mWiFiCallingPref;
private SwitchPreference mVideoCallingPref;
private NetworkSelectListPreference mButtonNetworkSelect;
+ private MobileDataPreference mMobileDataPref;
+ private DataUsagePreference mDataUsagePref;
private static final String iface = "rmnet0"; //TODO: this will go away
private List<SubscriptionInfo> mActiveSubInfos;
@@ -284,6 +291,8 @@
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
+ sendMetricsEventPreferenceClicked(preferenceScreen, preference);
+
/** TODO: Refactor and get rid of the if's using subclasses */
final int phoneSubId = mPhone.getSubId();
if (preference.getKey().equals(BUTTON_4G_LTE_KEY)) {
@@ -342,7 +351,8 @@
Intent intent = new Intent(EuiccManager.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS);
startActivity(intent);
return true;
- } else if (preference == mWiFiCallingPref || preference == mVideoCallingPref) {
+ } else if (preference == mWiFiCallingPref || preference == mVideoCallingPref
+ || preference == mMobileDataPref || preference == mDataUsagePref) {
return false;
} else {
// if the button is anything but the simple toggle preference,
@@ -551,6 +561,8 @@
mCallingCategory = (PreferenceCategory) findPreference(CATEGORY_CALLING_KEY);
mWiFiCallingPref = findPreference(BUTTON_WIFI_CALLING_KEY);
mVideoCallingPref = (SwitchPreference) findPreference(BUTTON_VIDEO_CALLING_KEY);
+ mMobileDataPref = (MobileDataPreference) findPreference(BUTTON_MOBILE_DATA_ENABLE_KEY);
+ mDataUsagePref = (DataUsagePreference) findPreference(BUTTON_DATA_USAGE_KEY);
try {
Context con = activity.createPackageContext("com.android.systemui", 0);
@@ -702,6 +714,12 @@
prefSet.addPreference(mButtonPreferredNetworkMode);
prefSet.addPreference(mButtonEnabledNetworks);
prefSet.addPreference(mButton4glte);
+
+ mMobileDataPref.initialize(phoneSubId);
+ prefSet.addPreference(mMobileDataPref);
+ mDataUsagePref.initialize(phoneSubId);
+ prefSet.addPreference(mDataUsagePref);
+
if (showEuiccSettings()) {
prefSet.addPreference(mEuiccSettingsPref);
if (TextUtils.isEmpty(mTelephonyManager.getLine1Number())) {
@@ -949,6 +967,8 @@
mButton4glte.setTitle(enhanced4glteModeTitleId);
mButton4glte.setEnabled(hasActiveSubscriptions && canChange4glte);
mLteDataServicePref.setEnabled(hasActiveSubscriptions);
+ mMobileDataPref.setEnabled(hasActiveSubscriptions);
+ mDataUsagePref.setEnabled(hasActiveSubscriptions);
Preference ps;
PreferenceScreen root = getPreferenceScreen();
ps = findPreference(BUTTON_CELL_BROADCAST_SETTINGS);
@@ -1692,6 +1712,19 @@
}
return false;
}
+
+ private void sendMetricsEventPreferenceClicked(
+ PreferenceScreen preferenceScreen, Preference preference) {
+ if (preference == mMobileDataPref) {
+ MetricsLogger.action(getContext(),
+ MetricsEvent.ACTION_MOBILE_NETWORK_MOBILE_DATA_TOGGLE,
+ ((MobileDataPreference) preference).mChecked);
+ } else if (preference == mDataUsagePref) {
+ MetricsLogger.action(getContext(),
+ MetricsEvent.ACTION_MOBILE_NETWORK_DATA_USAGE);
+ }
+ // TODO: add Metrics constants for other preferences and send events here accordingly.
+ }
}
}