Merge "Data only device(without FEATURE_TELEPHON_CALLING) : Add try/catch to handle UnsupportedOperationException" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6ad4c05..854d409 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -140,6 +140,7 @@
<uses-permission android:name="android.permission.ACCESS_GPU_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
+ <uses-permission android:name="android.permission.RESTART_PHONE_PROCESS" />
<application
android:name=".SettingsApplication"
diff --git a/res/layout/data_usage_summary_preference.xml b/res/layout/data_usage_summary_preference.xml
index 24399ac..4cbd958 100644
--- a/res/layout/data_usage_summary_preference.xml
+++ b/res/layout/data_usage_summary_preference.xml
@@ -18,8 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="22dp"
- android:paddingBottom="32dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="16dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical"
@@ -99,6 +99,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="12dp"
+ android:minHeight="54dp"
android:orientation="vertical">
<TextView
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f287439..66e7321 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1216,7 +1216,7 @@
<!-- Summary for the Private Space page. [CHAR LIMIT=NONE] -->
<string name="private_space_summary">Keep private apps locked and hidden</string>
<!-- Description for the Private Space page. [CHAR LIMIT=NONE] -->
- <string name="private_space_description">Hide apps in a private folder that only you can access</string>
+ <string name="private_space_description">Keep private apps in a separate space that you can hide or lock</string>
<!-- Title for the Private Space one lock preference. [CHAR LIMIT=60] -->
<string name="private_space_lock_title">Private Space lock</string>
<!-- Description for the Private Space one lock preference page. [CHAR LIMIT=NONE] -->
@@ -12697,4 +12697,7 @@
<!-- Content description for setting password complete-->
<string name="accessibility_setup_password_complete">Password is now set up</string>
+
+ <!-- Authority of the content provider that support methods restartPhoneProcess and restartRild. Will be overlaid by OEM.-->
+ <string name="reset_telephony_stack_content_provider_authority" translatable="false"></string>
</resources>
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index cb4167b..364dd3d 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -33,7 +33,7 @@
android:icon="@drawable/ic_bt_audio_sharing"
settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingPreferenceController"/>
- <Preference
+ <com.android.settingslib.RestrictedPreference
android:fragment="com.android.settings.connecteddevice.NfcAndPaymentFragment"
android:key="nfc_and_payment_settings"
android:title="@string/nfc_quick_toggle_title"
@@ -61,7 +61,7 @@
android:fragment="com.android.settings.print.PrintSettingsFragment"
android:order="-3"/>
- <SwitchPreferenceCompat
+ <com.android.settingslib.RestrictedSwitchPreference
android:key="uwb_settings"
android:title="@string/uwb_settings_title"
android:order="100"
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index ebfa935..f05f65c 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -21,7 +21,6 @@
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.provider.Settings;
@@ -128,6 +127,8 @@
| ResetNetworkRequest.RESET_VPN_MANAGER;
if (Flags.resetMobileNetworkSettings()) {
resetOptions |= ResetNetworkRequest.RESET_IMS_STACK;
+ resetOptions |= ResetNetworkRequest.RESET_PHONE_PROCESS;
+ resetOptions |= ResetNetworkRequest.RESET_RILD;
}
ResetNetworkRequest request = new ResetNetworkRequest(resetOptions);
if (mSubscriptions != null && mSubscriptions.size() > 0) {
diff --git a/src/com/android/settings/ResetNetworkRequest.java b/src/com/android/settings/ResetNetworkRequest.java
index 71c12b1..4be8b32 100644
--- a/src/com/android/settings/ResetNetworkRequest.java
+++ b/src/com/android/settings/ResetNetworkRequest.java
@@ -51,6 +51,12 @@
/* Reset option - reset IMS stack */
public static final int RESET_IMS_STACK = 0x20;
+ /* Reset option - reset phone process */
+ public static final int RESET_PHONE_PROCESS = 0x40;
+
+ /* Reset option - reset RILD */
+ public static final int RESET_RILD = 0x80;
+
/**
* Subscription ID indicates NOT resetting any of the components below:
* - TelephonyAndNetworkPolicy
@@ -264,6 +270,12 @@
if ((mResetOptions & RESET_IMS_STACK) != 0) {
builder.resetIms(mSubscriptionIdToResetIms);
}
+ if ((mResetOptions & RESET_PHONE_PROCESS) != 0) {
+ builder.restartPhoneProcess();
+ }
+ if ((mResetOptions & RESET_RILD) != 0) {
+ builder.restartRild();
+ }
return builder;
}
}
diff --git a/src/com/android/settings/datausage/DataPlanInfo.kt b/src/com/android/settings/datausage/DataPlanInfo.kt
new file mode 100644
index 0000000..51eb592
--- /dev/null
+++ b/src/com/android/settings/datausage/DataPlanInfo.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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
+
+data class DataPlanInfo(
+
+ /** The number of registered plans, [0, N] */
+ val dataPlanCount: Int,
+
+ /**
+ * The size of the first registered plan if one exists or the size of the warning if it is set.
+ *
+ * Set to -1 if no plan information is available.
+ */
+ val dataPlanSize: Long,
+
+ /**
+ * The "size" of the data usage bar, i.e. the amount of data its rhs end represents.
+ *
+ * Set to -1 if not display a data usage bar.
+ */
+ val dataBarSize: Long,
+
+ /** The number of bytes used since the start of the cycle. */
+ val dataPlanUse: Long,
+
+ /**
+ * The ending time of the billing cycle in ms since the epoch.
+ *
+ * Set to `null` if no cycle information is available.
+ */
+ val cycleEnd: Long?,
+
+ /** The time of the last update in milliseconds since the epoch, or -1 if unknown. */
+ val snapshotTime: Long,
+)
diff --git a/src/com/android/settings/datausage/DataPlanRepository.kt b/src/com/android/settings/datausage/DataPlanRepository.kt
new file mode 100644
index 0000000..9d34200
--- /dev/null
+++ b/src/com/android/settings/datausage/DataPlanRepository.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 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 android.net.NetworkPolicy
+import android.telephony.SubscriptionPlan
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkCycleDataRepository.Companion.getCycles
+import com.android.settings.datausage.lib.NetworkStatsRepository
+
+interface DataPlanRepository {
+ fun getDataPlanInfo(policy: NetworkPolicy, plans: List<SubscriptionPlan>): DataPlanInfo
+}
+
+class DataPlanRepositoryImpl(
+ private val networkCycleDataRepository: INetworkCycleDataRepository,
+) : DataPlanRepository {
+ override fun getDataPlanInfo(
+ policy: NetworkPolicy,
+ plans: List<SubscriptionPlan>,
+ ): DataPlanInfo {
+ getPrimaryPlan(plans)?.let { primaryPlan ->
+ val dataPlanSize = when (primaryPlan.dataLimitBytes) {
+ SubscriptionPlan.BYTES_UNLIMITED -> SubscriptionPlan.BYTES_UNKNOWN
+ else -> primaryPlan.dataLimitBytes
+ }
+ return DataPlanInfo(
+ dataPlanCount = plans.size,
+ dataPlanSize = dataPlanSize,
+ dataBarSize = dataPlanSize,
+ dataPlanUse = primaryPlan.dataUsageBytes,
+ cycleEnd = primaryPlan.cycleRule.end?.toInstant()?.toEpochMilli(),
+ snapshotTime = primaryPlan.dataUsageTime,
+ )
+ }
+
+ val cycle = policy.getCycles().firstOrNull()
+ val dataUsage = networkCycleDataRepository.queryUsage(
+ cycle ?: NetworkStatsRepository.AllTimeRange
+ ).usage
+ return DataPlanInfo(
+ dataPlanCount = 0,
+ dataPlanSize = SubscriptionPlan.BYTES_UNKNOWN,
+ dataBarSize = maxOf(dataUsage, policy.limitBytes, policy.warningBytes),
+ dataPlanUse = dataUsage,
+ cycleEnd = cycle?.upper,
+ snapshotTime = SubscriptionPlan.TIME_UNKNOWN,
+ )
+ }
+
+ companion object {
+ private const val PETA = 1_000_000_000_000_000L
+
+ private fun getPrimaryPlan(plans: List<SubscriptionPlan>): SubscriptionPlan? =
+ plans.firstOrNull()?.takeIf { plan ->
+ plan.dataLimitBytes > 0 && validSize(plan.dataUsageBytes) && plan.cycleRule != null
+ }
+
+ private fun validSize(value: Long): Boolean = value in 0L until PETA
+ }
+}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreference.java b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
index f2fcddd..93d930c 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreference.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.graphics.Typeface;
import android.icu.text.MessageFormat;
-import android.net.NetworkTemplate;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -32,13 +31,14 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.Utils;
-import com.android.settingslib.net.DataUsageController;
import com.android.settingslib.utils.StringUtil;
import java.util.HashMap;
@@ -62,10 +62,9 @@
private CharSequence mEndLabel;
private int mNumPlans;
- /** The specified un-initialized value for cycle time */
- private static final long CYCLE_TIME_UNINITIAL_VALUE = 0;
/** The ending time of the billing cycle in milliseconds since epoch. */
- private long mCycleEndTimeMs;
+ @Nullable
+ private Long mCycleEndTimeMs;
/** The time of the last update in standard milliseconds since the epoch */
private long mSnapshotTimeMs;
/** Name of carrier, or null if not available */
@@ -74,7 +73,6 @@
/** Progress to display on ProgressBar */
private float mProgress;
- private boolean mHasMobileData;
/**
* The size of the first registered plan if one exists or the size of the warning if it is set.
@@ -102,7 +100,10 @@
notifyChanged();
}
- public void setUsageInfo(long cycleEnd, long snapshotTime, CharSequence carrierName,
+ /**
+ * Sets the usage info.
+ */
+ public void setUsageInfo(@Nullable Long cycleEnd, long snapshotTime, CharSequence carrierName,
int numPlans) {
mCycleEndTimeMs = cycleEnd;
mSnapshotTimeMs = snapshotTime;
@@ -124,15 +125,17 @@
notifyChanged();
}
- void setUsageNumbers(long used, long dataPlanSize, boolean hasMobileData) {
+ /**
+ * Sets the usage numbers.
+ */
+ public void setUsageNumbers(long used, long dataPlanSize) {
mDataplanUse = used;
mDataplanSize = dataPlanSize;
- mHasMobileData = hasMobileData;
notifyChanged();
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
ProgressBar bar = getProgressBar(holder);
@@ -178,7 +181,7 @@
final MeasurableLinearLayout layout = getLayout(holder);
- if (mHasMobileData && mNumPlans >= 0 && mDataplanSize > 0L) {
+ if (mDataplanSize > 0L) {
TextView usageRemainingField = getDataRemaining(holder);
long dataRemaining = mDataplanSize - mDataplanUse;
if (dataRemaining >= 0) {
@@ -204,7 +207,7 @@
TextView cycleTime = getCycleTime(holder);
// Takes zero as a special case which value is never set.
- if (mCycleEndTimeMs == CYCLE_TIME_UNINITIAL_VALUE) {
+ if (mCycleEndTimeMs == null) {
cycleTime.setVisibility(View.GONE);
return;
}
@@ -228,7 +231,7 @@
private void updateCarrierInfo(TextView carrierInfo) {
- if (mNumPlans > 0 && mSnapshotTimeMs >= 0L) {
+ if (mSnapshotTimeMs >= 0L) {
carrierInfo.setVisibility(View.VISIBLE);
long updateAgeMillis = calculateTruncatedUpdateAge();
@@ -294,13 +297,6 @@
}
@VisibleForTesting
- protected long getHistoricalUsageLevel() {
- final DataUsageController controller = new DataUsageController(getContext());
- return controller.getHistoricalUsageLevel(
- new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build());
- }
-
- @VisibleForTesting
protected TextView getUsageTitle(PreferenceViewHolder holder) {
return (TextView) holder.findViewById(R.id.usage_title);
}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
deleted file mode 100644
index 35f5931..0000000
--- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2018 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 android.app.Activity;
-import android.content.Context;
-import android.net.NetworkTemplate;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionPlan;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.RecurrenceRule;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-
-import com.android.internal.util.CollectionUtils;
-import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.datausage.lib.DataUsageLib;
-import com.android.settings.network.ProxySubscriptionManager;
-import com.android.settings.network.telephony.TelephonyBasePreferenceController;
-import com.android.settingslib.net.DataUsageController;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.util.List;
-import java.util.concurrent.Future;
-
-/**
- * This is the controller for a data usage header that retrieves carrier data from the new
- * subscriptions framework API if available. The controller reads subscription information from the
- * framework and falls back to legacy usage data if none are available.
- */
-public class DataUsageSummaryPreferenceController extends TelephonyBasePreferenceController
- implements PreferenceControllerMixin {
-
- private static final String TAG = "DataUsageController";
- private static final String KEY = "status_header";
- private static final long PETA = 1000000000000000L;
-
- protected DataUsageController mDataUsageController;
- protected DataUsageInfoController mDataInfoController;
- private NetworkTemplate mDefaultTemplate;
- private boolean mHasMobileData;
-
- /** Name of the carrier, or null if not available */
- private CharSequence mCarrierName;
-
- /** The number of registered plans, [0,N] */
- private int mDataplanCount;
-
- /** The time of the last update in milliseconds since the epoch, or -1 if unknown */
- private long mSnapshotTime;
-
- /**
- * The size of the first registered plan if one exists or the size of the warning if it is set.
- * -1 if no information is available.
- */
- private long mDataplanSize;
- /** The "size" of the data usage bar, i.e. the amount of data its rhs end represents */
- private long mDataBarSize;
- /** The number of bytes used since the start of the cycle. */
- private long mDataplanUse;
- /** The ending time of the billing cycle in ms since the epoch */
- private long mCycleEnd;
-
- private Future<Long> mHistoricalUsageLevel;
-
- public DataUsageSummaryPreferenceController(Activity activity, int subscriptionId) {
- super(activity, KEY);
-
- init(subscriptionId);
- }
-
- /**
- * Initialize based on subscription ID provided
- * @param subscriptionId is the target subscriptionId
- */
- public void init(int subscriptionId) {
- mSubId = subscriptionId;
- mHasMobileData = DataUsageUtils.hasMobileData(mContext);
- mDataUsageController = null;
- }
-
- protected void updateConfiguration(Context context,
- int subscriptionId, SubscriptionInfo subInfo) {
- mDataUsageController = createDataUsageController(context);
- mDataUsageController.setSubscriptionId(subscriptionId);
- mDataInfoController = new DataUsageInfoController();
-
- if (subInfo != null) {
- mDefaultTemplate = DataUsageLib.getMobileTemplate(context, subscriptionId);
- }
- }
-
- @VisibleForTesting
- DataUsageController createDataUsageController(Context context) {
- return new DataUsageController(context);
- }
-
- @VisibleForTesting
- DataUsageSummaryPreferenceController(
- DataUsageController dataUsageController,
- DataUsageInfoController dataInfoController,
- NetworkTemplate defaultTemplate,
- Activity activity,
- int subscriptionId) {
- super(activity, KEY);
- mDataUsageController = dataUsageController;
- mDataInfoController = dataInfoController;
- mDefaultTemplate = defaultTemplate;
- mHasMobileData = true;
- mSubId = subscriptionId;
- }
-
- @VisibleForTesting
- List<SubscriptionPlan> getSubscriptionPlans(int subscriptionId) {
- return ProxySubscriptionManager.getInstance(mContext).get()
- .getSubscriptionPlans(subscriptionId);
- }
-
- protected SubscriptionInfo getSubscriptionInfo(int subscriptionId) {
- if (!mHasMobileData) {
- return null;
- }
- return ProxySubscriptionManager.getInstance(mContext)
- .getAccessibleSubscriptionInfo(subscriptionId);
- }
-
- @Override
- public int getAvailabilityStatus(int subId) {
- return getSubscriptionInfo(subId) != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
- }
-
- @Override
- public void updateState(Preference preference) {
- DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference;
-
- final SubscriptionInfo subInfo = getSubscriptionInfo(mSubId);
- if (subInfo == null) {
- return;
- }
- if (mDataUsageController == null) {
- updateConfiguration(mContext, mSubId, subInfo);
- }
-
- mHistoricalUsageLevel = ThreadUtils.postOnBackgroundThread(() ->
- mDataUsageController.getHistoricalUsageLevel(mDefaultTemplate));
-
- final DataUsageController.DataUsageInfo info =
- mDataUsageController.getDataUsageInfo(mDefaultTemplate);
-
- long usageLevel = info.usageLevel;
-
- refreshDataplanInfo(info, subInfo);
-
- if (info.warningLevel > 0 && info.limitLevel > 0) {
- summaryPreference.setLimitInfo(TextUtils.expandTemplate(
- mContext.getText(R.string.cell_data_warning_and_limit),
- DataUsageUtils.formatDataUsage(mContext, info.warningLevel),
- DataUsageUtils.formatDataUsage(mContext, info.limitLevel)));
- } else if (info.warningLevel > 0) {
- summaryPreference.setLimitInfo(TextUtils.expandTemplate(
- mContext.getText(R.string.cell_data_warning),
- DataUsageUtils.formatDataUsage(mContext, info.warningLevel)));
- } else if (info.limitLevel > 0) {
- summaryPreference.setLimitInfo(TextUtils.expandTemplate(
- mContext.getText(R.string.cell_data_limit),
- DataUsageUtils.formatDataUsage(mContext, info.limitLevel)));
- } else {
- summaryPreference.setLimitInfo(null);
- }
-
- if ((mDataplanUse <= 0L) && (mSnapshotTime < 0)) {
- Log.d(TAG, "Display data usage from history");
- mDataplanUse = displayUsageLevel(usageLevel);
- mSnapshotTime = -1L;
- }
-
- summaryPreference.setUsageNumbers(mDataplanUse, mDataplanSize, mHasMobileData);
-
- if (mDataBarSize <= 0) {
- summaryPreference.setChartEnabled(false);
- } else {
- summaryPreference.setChartEnabled(true);
- summaryPreference.setLabels(DataUsageUtils.formatDataUsage(mContext, 0 /* sizeBytes */),
- DataUsageUtils.formatDataUsage(mContext, mDataBarSize));
- summaryPreference.setProgress(mDataplanUse / (float) mDataBarSize);
- }
- summaryPreference.setUsageInfo(mCycleEnd, mSnapshotTime, mCarrierName, mDataplanCount);
- }
-
- private long displayUsageLevel(long usageLevel) {
- if (usageLevel > 0) {
- return usageLevel;
- }
- try {
- usageLevel = mHistoricalUsageLevel.get();
- } catch (Exception ex) {
- }
- return usageLevel;
- }
-
- // TODO(b/70950124) add test for this method once the robolectric shadow run script is
- // completed (b/3526807)
- private void refreshDataplanInfo(DataUsageController.DataUsageInfo info,
- SubscriptionInfo subInfo) {
- // reset data before overwriting
- mCarrierName = null;
- mDataplanCount = 0;
- mDataplanSize = -1L;
- mDataBarSize = mDataInfoController.getSummaryLimit(info);
- mDataplanUse = info.usageLevel;
- mCycleEnd = info.cycleEnd;
- mSnapshotTime = -1L;
-
- if (subInfo != null && mHasMobileData) {
- mCarrierName = subInfo.getCarrierName();
- final List<SubscriptionPlan> plans = getSubscriptionPlans(mSubId);
- final SubscriptionPlan primaryPlan = getPrimaryPlan(plans);
-
- if (primaryPlan != null) {
- mDataplanCount = plans.size();
- mDataplanSize = primaryPlan.getDataLimitBytes();
- if (unlimited(mDataplanSize)) {
- mDataplanSize = -1L;
- }
- mDataBarSize = mDataplanSize;
- mDataplanUse = primaryPlan.getDataUsageBytes();
-
- RecurrenceRule rule = primaryPlan.getCycleRule();
- if (rule != null && rule.start != null && rule.end != null) {
- mCycleEnd = rule.end.toEpochSecond() * 1000L;
- }
- mSnapshotTime = primaryPlan.getDataUsageTime();
- }
- }
- Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + mSubId);
- }
-
- private static SubscriptionPlan getPrimaryPlan(List<SubscriptionPlan> plans) {
- if (CollectionUtils.isEmpty(plans)) {
- return null;
- }
- // First plan in the list is the primary plan
- SubscriptionPlan plan = plans.get(0);
- return plan.getDataLimitBytes() > 0
- && validSize(plan.getDataUsageBytes())
- && plan.getCycleRule() != null ? plan : null;
- }
-
- private static boolean validSize(long value) {
- return value >= 0L && value < PETA;
- }
-
- public static boolean unlimited(long size) {
- return size == SubscriptionPlan.BYTES_UNLIMITED;
- }
-}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.kt b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.kt
new file mode 100644
index 0000000..8b31f67
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2024 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 android.content.Context
+import android.net.NetworkPolicy
+import android.net.NetworkTemplate
+import android.text.TextUtils
+import android.util.Log
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.datausage.lib.DataUsageLib.getMobileTemplate
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkCycleDataRepository
+import com.android.settings.network.ProxySubscriptionManager
+import com.android.settings.network.telephony.TelephonyBasePreferenceController
+import kotlin.math.max
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * This is the controller for a data usage header that retrieves carrier data from the new
+ * subscriptions framework API if available. The controller reads subscription information from the
+ * framework and falls back to legacy usage data if none are available.
+ */
+open class DataUsageSummaryPreferenceController @JvmOverloads constructor(
+ context: Context,
+ subId: Int,
+ private val proxySubscriptionManager: ProxySubscriptionManager =
+ ProxySubscriptionManager.getInstance(context),
+ private val networkCycleDataRepositoryFactory: (
+ template: NetworkTemplate,
+ ) -> INetworkCycleDataRepository = { NetworkCycleDataRepository(context, it) },
+ private val dataPlanRepositoryFactory: (
+ networkCycleDataRepository: INetworkCycleDataRepository,
+ ) -> DataPlanRepository = { DataPlanRepositoryImpl(it) }
+) : TelephonyBasePreferenceController(context, KEY) {
+
+ init {
+ mSubId = subId
+ }
+
+ private val subInfo by lazy {
+ if (DataUsageUtils.hasMobileData(mContext)) {
+ proxySubscriptionManager.getAccessibleSubscriptionInfo(mSubId)
+ } else null
+ }
+ private val networkCycleDataRepository by lazy {
+ networkCycleDataRepositoryFactory(getMobileTemplate(mContext, mSubId))
+ }
+ private val policy by lazy { networkCycleDataRepository.getPolicy() }
+ private lateinit var preference: DataUsageSummaryPreference
+
+ override fun getAvailabilityStatus(subId: Int) =
+ if (subInfo != null && policy != null) AVAILABLE else CONDITIONALLY_UNAVAILABLE
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)!!
+ policy?.let {
+ preference.setLimitInfo(it.getLimitInfo())
+ val dataBarSize = max(it.limitBytes, it.warningBytes)
+ if (dataBarSize > NetworkPolicy.WARNING_DISABLED) {
+ setDataBarSize(dataBarSize)
+ }
+ }
+ }
+
+ override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ update()
+ }
+ }
+ }
+
+ private suspend fun update() {
+ val policy = policy ?: return
+ val dataPlanInfo = withContext(Dispatchers.Default) {
+ dataPlanRepositoryFactory(networkCycleDataRepository).getDataPlanInfo(
+ policy = policy,
+ plans = proxySubscriptionManager.get().getSubscriptionPlans(mSubId),
+ )
+ }
+ Log.d(TAG, "dataPlanInfo: $dataPlanInfo")
+ preference.setUsageNumbers(dataPlanInfo.dataPlanUse, dataPlanInfo.dataPlanSize)
+ if (dataPlanInfo.dataBarSize > 0) {
+ preference.setChartEnabled(true)
+ setDataBarSize(dataPlanInfo.dataBarSize)
+ preference.setProgress(dataPlanInfo.dataPlanUse / dataPlanInfo.dataBarSize.toFloat())
+ } else {
+ preference.setChartEnabled(false)
+ }
+
+ preference.setUsageInfo(
+ dataPlanInfo.cycleEnd,
+ dataPlanInfo.snapshotTime,
+ subInfo?.carrierName,
+ dataPlanInfo.dataPlanCount,
+ )
+ }
+
+ private fun setDataBarSize(dataBarSize: Long) {
+ preference.setLabels(
+ DataUsageUtils.formatDataUsage(mContext, /* byteValue = */ 0),
+ DataUsageUtils.formatDataUsage(mContext, dataBarSize)
+ )
+ }
+
+ private fun NetworkPolicy.getLimitInfo(): CharSequence? = when {
+ warningBytes > 0 && limitBytes > 0 -> {
+ TextUtils.expandTemplate(
+ mContext.getText(R.string.cell_data_warning_and_limit),
+ DataUsageUtils.formatDataUsage(mContext, warningBytes),
+ DataUsageUtils.formatDataUsage(mContext, limitBytes),
+ )
+ }
+
+ warningBytes > 0 -> {
+ TextUtils.expandTemplate(
+ mContext.getText(R.string.cell_data_warning),
+ DataUsageUtils.formatDataUsage(mContext, warningBytes),
+ )
+ }
+
+ limitBytes > 0 -> {
+ TextUtils.expandTemplate(
+ mContext.getText(R.string.cell_data_limit),
+ DataUsageUtils.formatDataUsage(mContext, limitBytes),
+ )
+ }
+
+ else -> null
+ }
+
+ companion object {
+ private const val TAG = "DataUsageSummaryPC"
+ private const val KEY = "status_header"
+ }
+}
diff --git a/src/com/android/settings/datausage/lib/NetworkCycleBucketRepository.kt b/src/com/android/settings/datausage/lib/NetworkCycleBucketRepository.kt
index 652919e0..7e3e183 100644
--- a/src/com/android/settings/datausage/lib/NetworkCycleBucketRepository.kt
+++ b/src/com/android/settings/datausage/lib/NetworkCycleBucketRepository.kt
@@ -21,6 +21,7 @@
import android.text.format.DateUtils
import android.util.Range
import com.android.settings.datausage.lib.NetworkCycleDataRepository.Companion.bucketRange
+import com.android.settings.datausage.lib.NetworkCycleDataRepository.Companion.getCycles
import com.android.settings.datausage.lib.NetworkCycleDataRepository.Companion.reverseBucketRange
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.Bucket
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.aggregate
@@ -37,12 +38,8 @@
fun loadCycles(): List<NetworkUsageData> =
getCycles().map { aggregateUsage(it) }.filter { it.usage > 0 }
- private fun getCycles(): List<Range<Long>> {
- val policy = networkCycleDataRepository.getPolicy() ?: return queryCyclesAsFourWeeks()
- return policy.cycleIterator().asSequence().map {
- Range(it.lower.toInstant().toEpochMilli(), it.upper.toInstant().toEpochMilli())
- }.toList()
- }
+ private fun getCycles(): List<Range<Long>> =
+ networkCycleDataRepository.getPolicy()?.getCycles() ?: queryCyclesAsFourWeeks()
private fun queryCyclesAsFourWeeks(): List<Range<Long>> {
val timeRange = buckets.aggregate()?.timeRange ?: return emptyList()
diff --git a/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt b/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
index cde64df..31052ef 100644
--- a/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
+++ b/src/com/android/settings/datausage/lib/NetworkCycleDataRepository.kt
@@ -27,6 +27,7 @@
interface INetworkCycleDataRepository {
fun getCycles(): List<Range<Long>>
fun getPolicy(): NetworkPolicy?
+ fun queryUsage(range: Range<Long>): NetworkUsageData
}
class NetworkCycleDataRepository(
@@ -40,12 +41,8 @@
fun loadFirstCycle(): NetworkUsageData? = getCycles().firstOrNull()?.let { queryUsage(it) }
- override fun getCycles(): List<Range<Long>> {
- val policy = getPolicy() ?: return queryCyclesAsFourWeeks()
- return policy.cycleIterator().asSequence().map {
- Range(it.lower.toInstant().toEpochMilli(), it.upper.toInstant().toEpochMilli())
- }.toList()
- }
+ override fun getCycles(): List<Range<Long>> =
+ getPolicy()?.getCycles() ?: queryCyclesAsFourWeeks()
private fun queryCyclesAsFourWeeks(): List<Range<Long>> {
val timeRange = networkStatsRepository.getTimeRange() ?: return emptyList()
@@ -63,13 +60,17 @@
}
- fun queryUsage(range: Range<Long>) = NetworkUsageData(
+ override fun queryUsage(range: Range<Long>) = NetworkUsageData(
startTime = range.lower,
endTime = range.upper,
usage = networkStatsRepository.querySummaryForDevice(range.lower, range.upper),
)
companion object {
+ fun NetworkPolicy.getCycles() = cycleIterator().asSequence().map {
+ Range(it.lower.toInstant().toEpochMilli(), it.upper.toInstant().toEpochMilli())
+ }.toList()
+
fun bucketRange(startTime: Long, endTime: Long, step: Long): List<Range<Long>> =
(startTime..endTime step step).zipWithNext(::Range)
diff --git a/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java b/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java
index 80b295c..abeb949 100644
--- a/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java
+++ b/src/com/android/settings/development/ForcePeakRefreshRatePreferenceController.java
@@ -28,6 +28,7 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
+import com.android.server.display.feature.flags.Flags;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
@@ -95,7 +96,9 @@
@VisibleForTesting
void forcePeakRefreshRate(boolean enable) {
- final float peakRefreshRate = enable ? Float.POSITIVE_INFINITY : NO_CONFIG;
+ final float valueIfEnabled = Flags.backUpSmoothDisplayAndForcePeakRefreshRate()
+ ? Float.POSITIVE_INFINITY : mPeakRefreshRate;
+ final float peakRefreshRate = enable ? valueIfEnabled : NO_CONFIG;
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.MIN_REFRESH_RATE, peakRefreshRate);
}
diff --git a/src/com/android/settings/display/PeakRefreshRatePreferenceController.java b/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
index dfe571a..17d763a 100644
--- a/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
+++ b/src/com/android/settings/display/PeakRefreshRatePreferenceController.java
@@ -30,6 +30,7 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import com.android.server.display.feature.flags.Flags;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -107,7 +108,9 @@
@Override
public boolean setChecked(boolean isChecked) {
- final float peakRefreshRate = isChecked ? Float.POSITIVE_INFINITY : DEFAULT_REFRESH_RATE;
+ final float valueIfChecked = Flags.backUpSmoothDisplayAndForcePeakRefreshRate()
+ ? Float.POSITIVE_INFINITY : mPeakRefreshRate;
+ final float peakRefreshRate = isChecked ? valueIfChecked : DEFAULT_REFRESH_RATE;
Log.d(TAG, "setChecked to : " + peakRefreshRate);
return Settings.System.putFloat(
diff --git a/src/com/android/settings/network/ResetNetworkOperationBuilder.java b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
index 61f57f9..ac07897 100644
--- a/src/com/android/settings/network/ResetNetworkOperationBuilder.java
+++ b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
@@ -33,6 +33,8 @@
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
import com.android.settings.ResetNetworkRequest;
import com.android.settings.network.apn.ApnSettings;
@@ -50,6 +52,13 @@
private static final boolean DRY_RUN = false;
+ // TelephonyContentProvider method to restart phone process
+ @VisibleForTesting
+ static final String METHOD_RESTART_PHONE_PROCESS = "restartPhoneProcess";
+ // TelephonyContentProvider method to restart RILD
+ @VisibleForTesting
+ static final String METHOD_RESTART_RILD = "restartRild";
+
private Context mContext;
private List<Runnable> mResetSequence = new ArrayList<Runnable>();
@@ -229,17 +238,57 @@
// Reset IMS for all slots
for (int slotIndex = 0; slotIndex < tm.getActiveModemCount(); slotIndex++) {
tm.resetIms(slotIndex);
+ Log.i(TAG, "IMS was reset for slot " + slotIndex);
}
} else {
// Reset IMS for the slot specified by the sucriptionId.
final int slotIndex = SubscriptionManager.getSlotIndex(subId);
tm.resetIms(slotIndex);
+ Log.i(TAG, "IMS was reset for slot " + slotIndex);
}
});
return this;
}
/**
+ * Append a step to restart phone process by the help of TelephonyContentProvider.
+ * It's a no-op if TelephonyContentProvider doesn't exist.
+ * @return this
+ */
+ public ResetNetworkOperationBuilder restartPhoneProcess() {
+ try {
+ mContext.getContentResolver().call(
+ getResetTelephonyContentProviderAuthority(),
+ METHOD_RESTART_PHONE_PROCESS,
+ /* arg= */ null,
+ /* extras= */ null);
+ Log.i(TAG, "Phone process was restarted.");
+ } catch (IllegalArgumentException iae) {
+ Log.w(TAG, "Fail to restart phone process: " + iae);
+ }
+ return this;
+ }
+
+ /**
+ * Append a step to restart RILD by the help of TelephonyContentProvider.
+ * It's a no-op if TelephonyContentProvider doesn't exist.
+ * @return this
+ */
+ public ResetNetworkOperationBuilder restartRild() {
+ try {
+ mContext.getContentResolver().call(
+ getResetTelephonyContentProviderAuthority(),
+ METHOD_RESTART_RILD,
+ /* arg= */ null,
+ /* extras= */ null);
+ Log.i(TAG, "RILD was restarted.");
+ } catch (IllegalArgumentException iae) {
+ Log.w(TAG, "Fail to restart RILD: " + iae);
+ }
+ return this;
+ }
+
+ /**
* Construct a Runnable containing all operations appended.
* @return Runnable
*/
@@ -262,4 +311,14 @@
};
mResetSequence.add(runnable);
}
+
+ /**
+ * @return the authority of the telephony content provider that support methods
+ * resetPhoneProcess and resetRild.
+ */
+ @VisibleForTesting
+ String getResetTelephonyContentProviderAuthority() {
+ return mContext.getResources().getString(
+ R.string.reset_telephony_stack_content_provider_authority);
+ }
}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 16b04aa..b4b40ef 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -172,7 +172,7 @@
});
return Arrays.asList(
- new DataUsageSummaryPreferenceController(getActivity(), mSubId),
+ new DataUsageSummaryPreferenceController(context, mSubId),
new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(),
this, mSubId),
new CallsDefaultSubscriptionController(context, KEY_CALLS_PREF,
@@ -229,11 +229,6 @@
}
- final DataUsageSummaryPreferenceController dataUsageSummaryPreferenceController =
- use(DataUsageSummaryPreferenceController.class);
- if (dataUsageSummaryPreferenceController != null) {
- dataUsageSummaryPreferenceController.init(mSubId);
- }
use(MobileNetworkSwitchController.class).init(mSubId);
use(CarrierSettingsVersionPreferenceController.class).init(mSubId);
use(BillingCyclePreferenceController.class).init(mSubId);
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
deleted file mode 100644
index a929873..0000000
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (C) 2018 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 com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.net.NetworkTemplate;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.SubscriptionPlan;
-import android.telephony.TelephonyManager;
-import android.util.RecurrenceRule;
-
-import androidx.fragment.app.FragmentActivity;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settingslib.net.DataUsageController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowEntityHeaderController.class)
-public class DataUsageSummaryPreferenceControllerTest {
-
- private static final long UPDATE_BACKOFF_MS = TimeUnit.MINUTES.toMillis(13);
- private static final long CYCLE_BACKOFF_MS = TimeUnit.DAYS.toMillis(6);
- private static final long CYCLE_LENGTH_MS = TimeUnit.DAYS.toMillis(30);
- private static final long USAGE1 = 373 * BillingCycleSettings.MIB_IN_BYTES;
- private static final long LIMIT1 = BillingCycleSettings.GIB_IN_BYTES;
- private static final String CARRIER_NAME = "z-mobile";
- private static final String PERIOD = "Feb";
-
- @Mock
- private DataUsageController mDataUsageController;
- @Mock
- private DataUsageSummaryPreference mSummaryPreference;
- @Mock
- private NetworkTemplate mNetworkTemplate;
- @Mock
- private SubscriptionInfo mSubscriptionInfo;
- @Mock
- private SubscriptionPlan mSubscriptionPlan;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private EntityHeaderController mHeaderController;
- @Mock
- private TelephonyManager mTelephonyManager;
- @Mock
- private PackageManager mPm;
-
- private DataUsageInfoController mDataInfoController;
-
- private FakeFeatureFactory mFactory;
- private FragmentActivity mActivity;
- private Context mContext;
- private DataUsageSummaryPreferenceController mController;
- private int mDefaultSubscriptionId;
- private List<SubscriptionPlan> mSubscriptionPlans;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application);
-
- doReturn("%1$s %2%s").when(mContext)
- .getString(com.android.internal.R.string.fileSizeSuffix);
-
- mDefaultSubscriptionId = 1234;
- mSubscriptionPlans = new ArrayList<SubscriptionPlan>();
-
- mFactory = FakeFeatureFactory.setupForTest();
- when(mFactory.metricsFeatureProvider.getMetricsCategory(any(Object.class)))
- .thenReturn(MetricsProto.MetricsEvent.SETTINGS_APP_NOTIF_CATEGORY);
- ShadowEntityHeaderController.setUseMock(mHeaderController);
-
- mDataInfoController = spy(new DataUsageInfoController());
- doReturn(-1L).when(mDataInfoController).getSummaryLimit(any());
-
- mActivity = spy(Robolectric.buildActivity(FragmentActivity.class).get());
- doReturn(mTelephonyManager).when(mActivity).getSystemService(TelephonyManager.class);
- doReturn(mTelephonyManager).when(mTelephonyManager)
- .createForSubscriptionId(mDefaultSubscriptionId);
- doReturn(mPm).when(mActivity).getPackageManager();
- doReturn(TelephonyManager.SIM_STATE_READY).when(mTelephonyManager).getSimState();
-
- mController = spy(new DataUsageSummaryPreferenceController(
- mDataUsageController,
- mDataInfoController,
- mNetworkTemplate,
- mActivity, mDefaultSubscriptionId));
- doReturn(null).when(mController).getSubscriptionInfo(
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- doReturn(null).when(mController).getSubscriptionPlans(
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-
- doReturn(CARRIER_NAME).when(mSubscriptionInfo).getCarrierName();
- doReturn(mSubscriptionInfo).when(mController).getSubscriptionInfo(mDefaultSubscriptionId);
- doReturn(mSubscriptionPlans).when(mController).getSubscriptionPlans(mDefaultSubscriptionId);
- }
-
- @After
- public void tearDown() {
- ShadowEntityHeaderController.reset();
- }
-
- @Test
- public void testSummaryUpdate_onePlan_basic() {
- final long now = System.currentTimeMillis();
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
- createTestDataPlan(info.cycleStart, info.cycleEnd);
-
- mController.updateState(mSummaryPreference);
-
- ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
- verify(mSummaryPreference).setLimitInfo(captor.capture());
- CharSequence value = captor.getValue();
- assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
-
- // TODO (b/170330084): return intent instead of null for mSummaryPreference
- verify(mSummaryPreference).setUsageInfo((info.cycleEnd / 1000) * 1000,
- now - UPDATE_BACKOFF_MS,
- CARRIER_NAME, 1 /* numPlans */);
- verify(mSummaryPreference).setChartEnabled(true);
- }
-
- @Test
- public void testSummaryUpdate_noPlan_basic() {
- final long now = System.currentTimeMillis();
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
-
- mController.updateState(mSummaryPreference);
-
- ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
- verify(mSummaryPreference).setLimitInfo(captor.capture());
- CharSequence value = captor.getValue();
- assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
-
- verify(mSummaryPreference).setUsageInfo(
- info.cycleEnd,
- -1L /* snapshotTime */,
- CARRIER_NAME,
- 0 /* numPlans */);
- verify(mSummaryPreference).setChartEnabled(true);
- }
-
- @Test
- public void testSummaryUpdate_noCarrier_basic() {
- final long now = System.currentTimeMillis();
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- doReturn(null).when(mSubscriptionInfo).getCarrierName();
- setupTestDataUsage(LIMIT1, USAGE1, -1L /* snapshotTime */);
-
- mController.updateState(mSummaryPreference);
-
- ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
- verify(mSummaryPreference).setLimitInfo(captor.capture());
- CharSequence value = captor.getValue();
- assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
-
- verify(mSummaryPreference).setUsageInfo(
- info.cycleEnd,
- -1L /* snapshotTime */,
- null /* carrierName */,
- 0 /* numPlans */);
- verify(mSummaryPreference).setChartEnabled(true);
- }
-
- @Test
- public void testSummaryUpdate_noPlanData_basic() {
- final long now = System.currentTimeMillis();
-
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- doReturn(null).when(mSubscriptionInfo).getCarrierName();
- setupTestDataUsage(-1L /* dataPlanSize */, USAGE1, -1L /* snapshotTime */);
-
- mController.updateState(mSummaryPreference);
-
- ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
- verify(mSummaryPreference).setLimitInfo(captor.capture());
- CharSequence value = captor.getValue();
- assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
- verify(mSummaryPreference).setUsageInfo(
- info.cycleEnd,
- -1L /* snapshotTime */,
- null /* carrierName */,
- 0 /* numPlans */);
- verify(mSummaryPreference).setChartEnabled(false);
- }
-
- @Test
- public void testSummaryUpdate_noLimitNoWarning() {
- final long now = System.currentTimeMillis();
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
- info.warningLevel = 0L;
- info.limitLevel = 0L;
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
-
- mController.updateState(mSummaryPreference);
- verify(mSummaryPreference).setLimitInfo(null);
- }
-
- @Test
- public void testSummaryUpdate_warningOnly() {
- final long now = System.currentTimeMillis();
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
- info.warningLevel = BillingCycleSettings.MIB_IN_BYTES;
- info.limitLevel = 0L;
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
-
- mController.updateState(mSummaryPreference);
-
- ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
- verify(mSummaryPreference).setLimitInfo(captor.capture());
- CharSequence value = captor.getValue();
- assertThat(value.toString()).isEqualTo("1.00 MB data warning");
- }
-
- @Test
- public void testSummaryUpdate_limitOnly() {
- final long now = System.currentTimeMillis();
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
- info.warningLevel = 0L;
- info.limitLevel = BillingCycleSettings.MIB_IN_BYTES;
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
-
- mController.updateState(mSummaryPreference);
-
- ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
- verify(mSummaryPreference).setLimitInfo(captor.capture());
- CharSequence value = captor.getValue();
- assertThat(value.toString()).isEqualTo("1.00 MB data limit");
- }
-
- @Test
- public void testSummaryUpdate_limitAndWarning() {
- final long now = System.currentTimeMillis();
- final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
- info.warningLevel = BillingCycleSettings.MIB_IN_BYTES;
- info.limitLevel = BillingCycleSettings.MIB_IN_BYTES;
-
- doReturn(info).when(mDataUsageController).getDataUsageInfo(any());
- setupTestDataUsage(LIMIT1, USAGE1, now - UPDATE_BACKOFF_MS);
-
- mController.updateState(mSummaryPreference);
-
- ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
- verify(mSummaryPreference).setLimitInfo(captor.capture());
- CharSequence value = captor.getValue();
- assertThat(value.toString()).isEqualTo("1.00 MB data warning / 1.00 MB data limit");
- }
-
- @Test
- public void testMobileData_preferenceAvailable() {
- assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
- }
-
- @Test
- public void testMobileData_noSim_preferenceDisabled() {
- final int subscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- mController.init(subscriptionId);
- mController.mDataUsageController = mDataUsageController;
- assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
- }
-
- private DataUsageController.DataUsageInfo createTestDataUsageInfo(long now) {
- DataUsageController.DataUsageInfo info = new DataUsageController.DataUsageInfo();
- info.carrier = CARRIER_NAME;
- info.period = PERIOD;
- info.startDate = now;
- info.limitLevel = LIMIT1;
- info.warningLevel = LIMIT1 >> 1;
- info.usageLevel = USAGE1;
- info.cycleStart = now - CYCLE_BACKOFF_MS;
- info.cycleEnd = info.cycleStart + CYCLE_LENGTH_MS;
- return info;
- }
-
- private void setupTestDataUsage(long dataPlanSize, long dataUsageSize, long snapshotTime) {
- doReturn(dataPlanSize).when(mSubscriptionPlan).getDataLimitBytes();
- doReturn(dataUsageSize).when(mSubscriptionPlan).getDataUsageBytes();
- doReturn(snapshotTime).when(mSubscriptionPlan).getDataUsageTime();
-
- doReturn(dataPlanSize).when(mDataInfoController).getSummaryLimit(any());
- }
-
- private void createTestDataPlan(long startTime, long endTime) {
- final RecurrenceRule recurrenceRule = new RecurrenceRule(
- Instant.ofEpochMilli(startTime).atZone(ZoneId.systemDefault()),
- Instant.ofEpochMilli(endTime).atZone(ZoneId.systemDefault()),
- null);
- doReturn(recurrenceRule).when(mSubscriptionPlan).getCycleRule();
- mSubscriptionPlans.add(mSubscriptionPlan);
- }
-}
diff --git a/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java
index c5cc856..314120a 100644
--- a/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/ForcePeakRefreshRatePreferenceControllerTest.java
@@ -25,12 +25,19 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
+import com.android.server.display.feature.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -50,6 +57,9 @@
private Context mContext;
private ForcePeakRefreshRatePreferenceController mController;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -61,7 +71,19 @@
}
@Test
- public void onPreferenceChange_preferenceChecked_shouldEnableForcePeak() {
+ @RequiresFlagsDisabled(Flags.FLAG_BACK_UP_SMOOTH_DISPLAY_AND_FORCE_PEAK_REFRESH_RATE)
+ public void onPreferenceChange_preferenceChecked_shouldEnableForcePeak_featureFlagOff() {
+ mController.mPeakRefreshRate = 88f;
+
+ mController.onPreferenceChange(mPreference, true);
+
+ assertThat(Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.MIN_REFRESH_RATE, NO_CONFIG)).isEqualTo(88f);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BACK_UP_SMOOTH_DISPLAY_AND_FORCE_PEAK_REFRESH_RATE)
+ public void onPreferenceChange_preferenceChecked_shouldEnableForcePeak_featureFlagOn() {
mController.mPeakRefreshRate = 88f;
mController.onPreferenceChange(mPreference, true);
@@ -88,6 +110,7 @@
mController.updateState(mPreference);
verify(mPreference).setChecked(true);
+ assertThat(mController.isForcePeakRefreshRateEnabled()).isTrue();
}
@Test
@@ -98,6 +121,7 @@
mController.updateState(mPreference);
verify(mPreference).setChecked(false);
+ assertThat(mController.isForcePeakRefreshRateEnabled()).isFalse();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java
index 8b2c445..cb0963b 100644
--- a/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/PeakRefreshRatePreferenceControllerTest.java
@@ -25,11 +25,18 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
+import com.android.server.display.feature.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -49,6 +56,9 @@
private PeakRefreshRatePreferenceController.DeviceConfigDisplaySettings
mDeviceConfigDisplaySettings;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -79,7 +89,19 @@
}
@Test
- public void setChecked_enableSmoothDisplay_setRefreshRateToInfinity() {
+ @RequiresFlagsDisabled(Flags.FLAG_BACK_UP_SMOOTH_DISPLAY_AND_FORCE_PEAK_REFRESH_RATE)
+ public void setChecked_enableSmoothDisplay_featureFlagOff() {
+ mController.mPeakRefreshRate = 88f;
+ mController.setChecked(true);
+
+ assertThat(Settings.System.getFloat(mContext.getContentResolver(),
+ Settings.System.PEAK_REFRESH_RATE, DEFAULT_REFRESH_RATE))
+ .isEqualTo(88f);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_BACK_UP_SMOOTH_DISPLAY_AND_FORCE_PEAK_REFRESH_RATE)
+ public void setChecked_enableSmoothDisplay_featureFlagOn() {
mController.mPeakRefreshRate = 88f;
mController.setChecked(true);
@@ -100,14 +122,16 @@
@Test
public void isChecked_enableSmoothDisplay_returnTrue() {
- enableSmoothDisplayPreference();
+ mController.mPeakRefreshRate = 88f;
+ mController.setChecked(true);
assertThat(mController.isChecked()).isTrue();
}
@Test
public void isChecked_disableSmoothDisplay_returnFalse() {
- disableSmoothDisplayPreference();
+ mController.mPeakRefreshRate = 88f;
+ mController.setChecked(false);
assertThat(mController.isChecked()).isFalse();
}
@@ -128,22 +152,4 @@
assertThat(mController.isChecked()).isFalse();
}
-
- private void enableSmoothDisplayPreference() {
- mController.mPeakRefreshRate = 88f;
-
- Settings.System.putFloat(
- mContext.getContentResolver(),
- Settings.System.PEAK_REFRESH_RATE,
- mController.mPeakRefreshRate);
- }
-
- private void disableSmoothDisplayPreference() {
- mController.mPeakRefreshRate = 88f;
-
- Settings.System.putFloat(
- mContext.getContentResolver(),
- Settings.System.PEAK_REFRESH_RATE,
- DEFAULT_REFRESH_RATE);
- }
}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataPlanRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataPlanRepositoryTest.kt
new file mode 100644
index 0000000..5ed1486
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataPlanRepositoryTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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 android.net.NetworkPolicy
+import android.telephony.SubscriptionPlan
+import android.util.Range
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkUsageData
+import com.android.settings.testutils.zonedDateTime
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class DataPlanRepositoryTest {
+
+ private object FakeNetworkCycleDataRepository : INetworkCycleDataRepository {
+ override fun getCycles(): List<Range<Long>> = emptyList()
+ override fun getPolicy() = null
+
+ override fun queryUsage(range: Range<Long>) = NetworkUsageData(
+ startTime = CYCLE_CYCLE_START_TIME,
+ endTime = CYCLE_CYCLE_END_TIME,
+ usage = CYCLE_BYTES,
+ )
+ }
+
+ private val repository = DataPlanRepositoryImpl(FakeNetworkCycleDataRepository)
+
+ private val policy = mock<NetworkPolicy> {
+ on { cycleIterator() } doReturn listOf(
+ Range(zonedDateTime(CYCLE_CYCLE_START_TIME), zonedDateTime(CYCLE_CYCLE_END_TIME)),
+ ).iterator()
+ }
+
+ @Test
+ fun getDataPlanInfo_hasSubscriptionPlan() {
+ val dataPlanInfo = repository.getDataPlanInfo(policy, listOf(SUBSCRIPTION_PLAN))
+
+ assertThat(dataPlanInfo).isEqualTo(
+ DataPlanInfo(
+ dataPlanCount = 1,
+ dataPlanSize = DATA_LIMIT_BYTES,
+ dataBarSize = DATA_LIMIT_BYTES,
+ dataPlanUse = DATA_USAGE_BYTES,
+ cycleEnd = PLAN_CYCLE_END_TIME,
+ snapshotTime = DATA_USAGE_TIME,
+ )
+ )
+ }
+
+ @Test
+ fun getDataPlanInfo_noSubscriptionPlan() {
+ val dataPlanInfo = repository.getDataPlanInfo(policy, emptyList())
+
+ assertThat(dataPlanInfo).isEqualTo(
+ DataPlanInfo(
+ dataPlanCount = 0,
+ dataPlanSize = SubscriptionPlan.BYTES_UNKNOWN,
+ dataBarSize = CYCLE_BYTES,
+ dataPlanUse = CYCLE_BYTES,
+ cycleEnd = CYCLE_CYCLE_END_TIME,
+ snapshotTime = SubscriptionPlan.TIME_UNKNOWN,
+ )
+ )
+ }
+
+ private companion object {
+ const val CYCLE_CYCLE_START_TIME = 1L
+ const val CYCLE_CYCLE_END_TIME = 2L
+ const val CYCLE_BYTES = 11L
+
+ const val PLAN_CYCLE_START_TIME = 100L
+ const val PLAN_CYCLE_END_TIME = 200L
+ const val DATA_LIMIT_BYTES = 300L
+ const val DATA_USAGE_BYTES = 400L
+ const val DATA_USAGE_TIME = 500L
+
+ val SUBSCRIPTION_PLAN: SubscriptionPlan = SubscriptionPlan.Builder.createNonrecurring(
+ zonedDateTime(PLAN_CYCLE_START_TIME),
+ zonedDateTime(PLAN_CYCLE_END_TIME),
+ ).apply {
+ setDataLimit(DATA_LIMIT_BYTES, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
+ setDataUsage(DATA_USAGE_BYTES, DATA_USAGE_TIME)
+ }.build()
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.kt
new file mode 100644
index 0000000..8ebb9c5
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2024 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 android.content.Context
+import android.net.NetworkPolicy
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.SubscriptionPlan
+import android.telephony.TelephonyManager
+import android.util.Range
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.preference.PreferenceManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.core.BasePreferenceController.AVAILABLE
+import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
+import com.android.settings.datausage.lib.INetworkCycleDataRepository
+import com.android.settings.datausage.lib.NetworkUsageData
+import com.android.settings.network.ProxySubscriptionManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class DataUsageSummaryPreferenceControllerTest {
+
+ private var policy: NetworkPolicy? = mock<NetworkPolicy>()
+
+ private val mockTelephonyManager = mock<TelephonyManager> {
+ on { isDataCapable } doReturn true
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
+ }
+
+ private val mockSubscriptionManager = mock<SubscriptionManager> {
+ on { getSubscriptionPlans(any()) } doReturn emptyList()
+ }
+
+ private val mockProxySubscriptionManager = mock<ProxySubscriptionManager> {
+ on { get() } doReturn mockSubscriptionManager
+ }
+
+ private val fakeNetworkCycleDataRepository = object : INetworkCycleDataRepository {
+ override fun getCycles(): List<Range<Long>> = emptyList()
+ override fun getPolicy() = policy
+ override fun queryUsage(range: Range<Long>) = NetworkUsageData.AllZero
+ }
+
+ private var dataPlanInfo = EMPTY_DATA_PLAN_INFO
+
+ private val fakeDataPlanRepository = object : DataPlanRepository {
+ override fun getDataPlanInfo(policy: NetworkPolicy, plans: List<SubscriptionPlan>) =
+ dataPlanInfo
+ }
+
+ private val controller = DataUsageSummaryPreferenceController(
+ context = context,
+ subId = SUB_ID,
+ proxySubscriptionManager = mockProxySubscriptionManager,
+ networkCycleDataRepositoryFactory = { fakeNetworkCycleDataRepository },
+ dataPlanRepositoryFactory = { fakeDataPlanRepository },
+ )
+
+ private val preference = mock<DataUsageSummaryPreference> {
+ on { key } doReturn controller.preferenceKey
+ }
+ private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
+
+ @Before
+ fun setUp() {
+ preferenceScreen.addPreference(preference)
+ }
+
+ @Test
+ fun getAvailabilityStatus_noMobileData_conditionallyUnavailable() {
+ mockTelephonyManager.stub {
+ on { isDataCapable } doReturn false
+ }
+
+ val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
+
+ assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+ }
+
+ @Test
+ fun getAvailabilityStatus_hasSubInfoAndPolicy_available() {
+ mockProxySubscriptionManager.stub {
+ on { getAccessibleSubscriptionInfo(SUB_ID) } doReturn SubscriptionInfo.Builder().build()
+ }
+
+ val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
+
+ assertThat(availabilityStatus).isEqualTo(AVAILABLE)
+ }
+
+ @Test
+ fun getAvailabilityStatus_noSubInfo_conditionallyUnavailable() {
+ mockProxySubscriptionManager.stub {
+ on { getAccessibleSubscriptionInfo(SUB_ID) } doReturn null
+ }
+
+ val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
+
+ assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+ }
+
+ @Test
+ fun getAvailabilityStatus_noPolicy_conditionallyUnavailable() {
+ policy = null
+
+ val availabilityStatus = controller.getAvailabilityStatus(SUB_ID)
+
+ assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE)
+ }
+
+ @Test
+ fun displayPreference_policyHasNoLimitInfo() {
+ policy = mock<NetworkPolicy>().apply {
+ warningBytes = NetworkPolicy.WARNING_DISABLED
+ limitBytes = NetworkPolicy.LIMIT_DISABLED
+ }
+
+ controller.displayPreference(preferenceScreen)
+
+ verify(preference).setLimitInfo(null)
+ verify(preference, never()).setLabels(any(), any())
+ }
+
+ @Test
+ fun displayPreference_policyWarningOnly() {
+ policy = mock<NetworkPolicy>().apply {
+ warningBytes = 1L
+ limitBytes = NetworkPolicy.LIMIT_DISABLED
+ }
+
+ controller.displayPreference(preferenceScreen)
+
+ val limitInfo = argumentCaptor {
+ verify(preference).setLimitInfo(capture())
+ }.firstValue.toString()
+ assertThat(limitInfo).isEqualTo("1 B data warning")
+ verify(preference).setLabels("0 B", "1 B")
+ }
+
+ @Test
+ fun displayPreference_policyLimitOnly() {
+ policy = mock<NetworkPolicy>().apply {
+ warningBytes = NetworkPolicy.WARNING_DISABLED
+ limitBytes = 1L
+ }
+
+ controller.displayPreference(preferenceScreen)
+
+ val limitInfo = argumentCaptor {
+ verify(preference).setLimitInfo(capture())
+ }.firstValue.toString()
+ assertThat(limitInfo).isEqualTo("1 B data limit")
+ verify(preference).setLabels("0 B", "1 B")
+ }
+
+ @Test
+ fun displayPreference_policyHasWarningAndLimit() {
+ policy = mock<NetworkPolicy>().apply {
+ warningBytes = BillingCycleSettings.GIB_IN_BYTES / 2
+ limitBytes = BillingCycleSettings.GIB_IN_BYTES
+ }
+
+ controller.displayPreference(preferenceScreen)
+
+ val limitInfo = argumentCaptor {
+ verify(preference).setLimitInfo(capture())
+ }.firstValue.toString()
+ assertThat(limitInfo).isEqualTo("512 MB data warning / 1.00 GB data limit")
+ verify(preference).setLabels("0 B", "1.00 GB")
+ }
+
+ @Test
+ fun onViewCreated_emptyDataPlanInfo() = runBlocking {
+ dataPlanInfo = EMPTY_DATA_PLAN_INFO
+ controller.displayPreference(preferenceScreen)
+ clearInvocations(preference)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ delay(100)
+
+ verify(preference).setUsageNumbers(
+ EMPTY_DATA_PLAN_INFO.dataPlanUse,
+ EMPTY_DATA_PLAN_INFO.dataPlanSize,
+ )
+ verify(preference).setChartEnabled(false)
+ verify(preference).setUsageInfo(
+ EMPTY_DATA_PLAN_INFO.cycleEnd,
+ EMPTY_DATA_PLAN_INFO.snapshotTime,
+ null,
+ EMPTY_DATA_PLAN_INFO.dataPlanCount,
+ )
+ }
+
+ @Test
+ fun onViewCreated_positiveDataPlanInfo() = runBlocking {
+ dataPlanInfo = POSITIVE_DATA_PLAN_INFO
+ controller.displayPreference(preferenceScreen)
+ clearInvocations(preference)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ delay(100)
+
+ verify(preference).setUsageNumbers(
+ POSITIVE_DATA_PLAN_INFO.dataPlanUse,
+ POSITIVE_DATA_PLAN_INFO.dataPlanSize,
+ )
+ verify(preference).setChartEnabled(true)
+ verify(preference).setLabels("0 B", "9 B")
+ val progress = argumentCaptor {
+ verify(preference).setProgress(capture())
+ }.firstValue
+ assertThat(progress).isEqualTo(0.8888889f)
+ verify(preference).setUsageInfo(
+ POSITIVE_DATA_PLAN_INFO.cycleEnd,
+ POSITIVE_DATA_PLAN_INFO.snapshotTime,
+ null,
+ POSITIVE_DATA_PLAN_INFO.dataPlanCount,
+ )
+ }
+
+ private companion object {
+ const val SUB_ID = 1234
+ val EMPTY_DATA_PLAN_INFO = DataPlanInfo(
+ dataPlanCount = 0,
+ dataPlanSize = SubscriptionPlan.BYTES_UNKNOWN,
+ dataBarSize = SubscriptionPlan.BYTES_UNKNOWN,
+ dataPlanUse = 0,
+ cycleEnd = null,
+ snapshotTime = SubscriptionPlan.TIME_UNKNOWN,
+ )
+ val POSITIVE_DATA_PLAN_INFO = DataPlanInfo(
+ dataPlanCount = 0,
+ dataPlanSize = 10L,
+ dataBarSize = 9L,
+ dataPlanUse = 8L,
+ cycleEnd = 7L,
+ snapshotTime = 6L,
+ )
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
index f0a5309..77fe843 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/NetworkCycleDataRepositoryTest.kt
@@ -23,10 +23,8 @@
import android.util.Range
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.testutils.zonedDateTime
import com.google.common.truth.Truth.assertThat
-import java.time.Instant
-import java.time.ZoneId
-import java.time.ZonedDateTime
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -96,9 +94,6 @@
)
}
- private fun zonedDateTime(epochMilli: Long): ZonedDateTime? =
- ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault())
-
private companion object {
const val CYCLE1_START_TIME = 1L
const val CYCLE1_END_TIME = 2L
diff --git a/tests/spa_unit/src/com/android/settings/testutils/DateTimeTestUtil.kt b/tests/spa_unit/src/com/android/settings/testutils/DateTimeTestUtil.kt
new file mode 100644
index 0000000..2a5f137
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/testutils/DateTimeTestUtil.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.testutils
+
+import java.time.Instant
+import java.time.ZoneId
+import java.time.ZonedDateTime
+
+fun zonedDateTime(epochMilli: Long): ZonedDateTime? =
+ ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault())
diff --git a/tests/unit/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java b/tests/unit/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
index fe7759d..d80c16a 100644
--- a/tests/unit/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
+++ b/tests/unit/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
@@ -17,6 +17,7 @@
package com.android.settings.datausage;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -90,7 +91,7 @@
@Test
public void testSetUsageInfo_withNoDataPlans_carrierInfoNotShown() {
- mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
+ mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getCarrierInfo(mHolder).getVisibility())
@@ -197,7 +198,7 @@
@Test
public void testSetUsageInfo_withNoDataPlans_usageTitleNotShown() {
- mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
+ mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getUsageTitle(mHolder).getVisibility()).isEqualTo(View.GONE);
@@ -216,7 +217,7 @@
public void testSetUsageInfo_cycleRemainingTimeIsLessOneDay() {
// just under one day
final long cycleEnd = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(23);
- mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
+ mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
@@ -229,7 +230,7 @@
@Test
public void testSetUsageInfo_cycleRemainingTimeNegativeDaysLeft_shouldDisplayNoneLeft() {
final long cycleEnd = System.currentTimeMillis() - 1L;
- mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
+ mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
@@ -243,7 +244,7 @@
final int daysLeft = 3;
final long cycleEnd = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(daysLeft)
+ TimeUnit.HOURS.toMillis(1);
- mSummaryPreference.setUsageInfo(cycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
+ mSummaryPreference.setUsageInfo(cycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getCycleTime(mHolder).getVisibility())
@@ -329,8 +330,7 @@
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
mSummaryPreference.setUsageNumbers(
BillingCycleSettings.MIB_IN_BYTES,
- 10 * BillingCycleSettings.MIB_IN_BYTES,
- true /* hasMobileData */);
+ 10 * BillingCycleSettings.MIB_IN_BYTES);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
@@ -349,8 +349,7 @@
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
mSummaryPreference.setUsageNumbers(
11 * BillingCycleSettings.MIB_IN_BYTES,
- 10 * BillingCycleSettings.MIB_IN_BYTES,
- true /* hasMobileData */);
+ 10 * BillingCycleSettings.MIB_IN_BYTES);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
@@ -364,9 +363,9 @@
@Test
public void testSetUsageInfo_withUsageInfo_dataUsageShown() {
- mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 0 /* numPlans */);
+ mSummaryPreference.setUsageInfo(mCycleEnd, -1, FAKE_CARRIER, 0 /* numPlans */);
mSummaryPreference.setUsageNumbers(
- BillingCycleSettings.MIB_IN_BYTES, -1L, true /* hasMobileData */);
+ BillingCycleSettings.MIB_IN_BYTES, -1L);
mSummaryPreference.onBindViewHolder(mHolder);
assertThat(mSummaryPreference.getDataUsed(mHolder).getText().toString())
@@ -383,8 +382,7 @@
mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, FAKE_CARRIER, 1 /* numPlans */);
mSummaryPreference.setUsageNumbers(
BillingCycleSettings.MIB_IN_BYTES,
- 10 * BillingCycleSettings.MIB_IN_BYTES,
- true /* hasMobileData */);
+ 10 * BillingCycleSettings.MIB_IN_BYTES);
int data_used_formatted_id = ResourcesUtils.getResourcesId(
mContext, "string", "data_used_formatted");
diff --git a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
index 41b6b27..6213f8e 100644
--- a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
+++ b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
@@ -16,13 +16,20 @@
package com.android.settings.network;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkPolicyManager;
@@ -59,6 +66,9 @@
private TelephonyManager mTelephonyManager;
@Mock
private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
+ private ContentProvider mContentProvider;;
+
private Context mContext;
private ResetNetworkOperationBuilder mBuilder;
@@ -67,6 +77,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
+ doReturn(ContentResolver.wrap(mContentProvider)).when(mContext).getContentResolver();
mBuilder = spy(new ResetNetworkOperationBuilder(mContext));
}
@@ -171,4 +182,42 @@
verify(mTelephonyManager, times(2)).resetIms(anyInt());
}
+
+ @Test
+ public void restartPhoneProcess_withoutTelephonyContentProvider_shouldNotCrash() {
+ doThrow(new IllegalArgumentException()).when(mContentProvider).call(
+ anyString(), anyString(), anyString(), any());
+
+ mBuilder.restartPhoneProcess();
+ }
+
+ @Test
+ public void restartRild_withoutTelephonyContentProvider_shouldNotCrash() {
+ doThrow(new IllegalArgumentException()).when(mContentProvider).call(
+ anyString(), anyString(), anyString(), any());
+
+ mBuilder.restartRild();
+ }
+
+ @Test
+ public void restartPhoneProcess_withTelephonyContentProvider_shouldCallRestartPhoneProcess() {
+ mBuilder.restartPhoneProcess();
+
+ verify(mContentProvider).call(
+ eq(mBuilder.getResetTelephonyContentProviderAuthority()),
+ eq(ResetNetworkOperationBuilder.METHOD_RESTART_PHONE_PROCESS),
+ isNull(),
+ isNull());
+ }
+
+ @Test
+ public void restartRild_withTelephonyContentProvider_shouldCallRestartRild() {
+ mBuilder.restartRild();
+
+ verify(mContentProvider).call(
+ eq(mBuilder.getResetTelephonyContentProviderAuthority()),
+ eq(ResetNetworkOperationBuilder.METHOD_RESTART_RILD),
+ isNull(),
+ isNull());
+ }
}