Merge "Fix a CHAR LIMIT mistake for a BT message as requested by linguists" into main
diff --git a/Android.bp b/Android.bp
index 0777aa7..2699c38 100644
--- a/Android.bp
+++ b/Android.bp
@@ -89,6 +89,7 @@
"SettingsLib",
"SettingsLibActivityEmbedding",
"aconfig_settings_flags_lib",
+ "accessibility_settings_flags_lib",
"app-usage-event-protos-lite",
"battery-event-protos-lite",
"battery-usage-slot-protos-lite",
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index 37b03ba..de6d0af 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -35,4 +35,15 @@
java_aconfig_library {
name: "MediaDrmSettingsFlagsLib",
aconfig_declarations: "media_drm_flags",
-}
\ No newline at end of file
+}
+
+aconfig_declarations {
+ name: "accessibility_flags",
+ package: "com.android.settings.accessibility",
+ srcs: ["accessibility/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "accessibility_settings_flags_lib",
+ aconfig_declarations: "accessibility_flags",
+}
diff --git a/aconfig/accessibility/OWNERS b/aconfig/accessibility/OWNERS
new file mode 100644
index 0000000..7a76c21
--- /dev/null
+++ b/aconfig/accessibility/OWNERS
@@ -0,0 +1 @@
+include /src/com/android/settings/accessibility/OWNERS
diff --git a/aconfig/accessibility/accessibility_flags.aconfig b/aconfig/accessibility/accessibility_flags.aconfig
new file mode 100644
index 0000000..7f1f909
--- /dev/null
+++ b/aconfig/accessibility/accessibility_flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.settings.accessibility"
+
+# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
+
+flag {
+ name: "remove_qs_tooltip_in_suw"
+ namespace: "accessibility"
+ description: "Don't show quick settings tooltip in SUW, since the user can't use quick settings there."
+ bug: "294560581"
+}
\ No newline at end of file
diff --git a/aconfig/settings_accessibility_flag_declarations.aconfig b/aconfig/settings_accessibility_flag_declarations_legacy.aconfig
similarity index 86%
rename from aconfig/settings_accessibility_flag_declarations.aconfig
rename to aconfig/settings_accessibility_flag_declarations_legacy.aconfig
index 246f983..acdce96 100644
--- a/aconfig/settings_accessibility_flag_declarations.aconfig
+++ b/aconfig/settings_accessibility_flag_declarations_legacy.aconfig
@@ -1,5 +1,8 @@
package: "com.android.settings.flags"
+# NOTE: Don't add new accessibility flags here, since the package name doesn't follow
+# the best practice for setting's feature flag go/settings-trunk-stable
+
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
# NOTE: All Settings flags share the same Flags class, so prefix our
diff --git a/res/layout/content_protection_preference_fragment.xml b/res/layout/content_protection_preference_fragment.xml
index 4c7352e..8bf6582 100644
--- a/res/layout/content_protection_preference_fragment.xml
+++ b/res/layout/content_protection_preference_fragment.xml
@@ -17,6 +17,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="content_protection_preference_subpage"
android:title="@string/content_protection_preference_title">
<com.android.settingslib.widget.TopIntroPreference
diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
index 6bd8747..f268a40 100644
--- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
+++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
+import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
@@ -33,6 +34,8 @@
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
+import com.google.android.setupcompat.util.WizardManagerHelper;
+
import java.util.Optional;
/**
@@ -207,6 +210,13 @@
return;
}
+ if (Flags.removeQsTooltipInSuw()
+ && mContext instanceof Activity
+ && WizardManagerHelper.isAnySetupWizard(((Activity) mContext).getIntent())) {
+ // Don't show QuickSettingsTooltip in Setup Wizard
+ return;
+ }
+
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
mContext, tileComponentName)) {
// Returns if quick settings tooltip only show once.
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 427cad9..c76bb8b 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -905,6 +905,14 @@
return;
}
+ Activity activity = getActivity();
+ if (com.android.settings.accessibility.Flags.removeQsTooltipInSuw()
+ && activity != null
+ && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+ // Don't show QuickSettingsTooltip in Setup Wizard
+ return;
+ }
+
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
getContext(), tileComponentName)) {
// Returns if quick settings tooltip only show once.
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 807f043..e45657f 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -53,6 +53,7 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.deviceinfo.StorageWizardMoveConfirm;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
@@ -359,6 +360,8 @@
mButtonsPref.setButton1Enabled(false);
// Invoke uninstall or clear user data based on sysPackage
String packageName = mAppEntry.info.packageName;
+ DynamicDenylistManager.getInstance(getContext())
+ .resetDenylistIfNeeded(packageName, /* force= */ false);
Log.i(TAG, "Clearing user data for package : " + packageName);
if (mClearDataObserver == null) {
mClearDataObserver = new ClearUserDataObserver();
diff --git a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
index 6da3e52..b2b7512 100644
--- a/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
+++ b/src/com/android/settings/applications/manageapplications/ResetAppsHelper.java
@@ -39,6 +39,7 @@
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryOptimizeUtils;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import java.util.Arrays;
import java.util.List;
@@ -155,6 +156,8 @@
}
mAom.resetAllModes();
BatteryOptimizeUtils.resetAppOptimizationMode(mContext, mIPm, mAom);
+ DynamicDenylistManager.getInstance(mContext)
+ .resetDenylistIfNeeded(/* packageName= */ null, /* force= */ true);
final int[] restrictedUids = mNpm.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
final int currentUserId = ActivityManager.getCurrentUser();
for (int uid : restrictedUids) {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
index b0f8b8f..0d2b53a 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDevicePreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.connecteddevice.audiosharing;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastAssistant;
@@ -43,16 +42,12 @@
import com.android.settings.flags.Flags;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.google.common.collect.ImmutableList;
-
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -378,13 +373,14 @@
// Do nothing for ineligible (non LE audio) remote device when no sharing session.
} else {
Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
- fetchConnectedDevicesByGroupId();
+ AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
// Handle connected eligible (LE audio) remote device
if (isBroadcasting()) {
// Show audio sharing switch or join dialog according to device count in the sharing
// session.
ArrayList<AudioSharingDeviceItem> deviceItemsInSharingSession =
- buildDeviceItemsInSharingSession(groupedDevices);
+ AudioSharingUtils.buildOrderedDeviceItemsInSharingSession(
+ groupedDevices, mLocalBtManager);
// Show audio sharing switch dialog when the third eligible (LE audio) remote device
// connected during a sharing session.
if (deviceItemsInSharingSession.size() >= 2) {
@@ -432,8 +428,7 @@
if (device.getGroupId() == cachedDevice.getGroupId()) {
continue;
}
- deviceItems.add(
- new AudioSharingDeviceItem(device.getName(), device.getGroupId()));
+ deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(device));
}
// Show audio sharing join dialog when the second eligible (LE audio) remote device
// connect and no sharing session.
@@ -494,52 +489,6 @@
return mBroadcast != null && mBroadcast.isEnabled(null);
}
- private Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId() {
- // TODO: filter out devices with le audio disabled.
- List<BluetoothDevice> connectedDevices =
- mAssistant == null ? ImmutableList.of() : mAssistant.getConnectedDevices();
- Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
- CachedBluetoothDeviceManager cacheManager = mLocalBtManager.getCachedDeviceManager();
- for (BluetoothDevice device : connectedDevices) {
- CachedBluetoothDevice cachedDevice = cacheManager.findDevice(device);
- if (cachedDevice == null) {
- Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
- continue;
- }
- int groupId = cachedDevice.getGroupId();
- if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
- Log.d(
- TAG,
- "Skip device due to no valid group id: " + device.getAnonymizedAddress());
- continue;
- }
- if (!groupedDevices.containsKey(groupId)) {
- groupedDevices.put(groupId, new ArrayList<>());
- }
- groupedDevices.get(groupId).add(cachedDevice);
- }
- return groupedDevices;
- }
-
- private ArrayList<AudioSharingDeviceItem> buildDeviceItemsInSharingSession(
- Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
- ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
- for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
- for (CachedBluetoothDevice device : devices) {
- List<BluetoothLeBroadcastReceiveState> sourceList =
- mAssistant.getAllSources(device.getDevice());
- if (!sourceList.isEmpty()) {
- // Use random device in the group within the sharing session to
- // represent the group.
- deviceItems.add(
- new AudioSharingDeviceItem(device.getName(), device.getGroupId()));
- break;
- }
- }
- }
- return deviceItems;
- }
-
private void addSourceToTargetDevices(List<BluetoothDevice> sinks) {
if (sinks.isEmpty() || mBroadcast == null || mAssistant == null) {
Log.d(TAG, "Skip adding source to target.");
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 3f9f48e..8b82fe9 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -16,13 +16,11 @@
package com.android.settings.connecteddevice.audiosharing;
-import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
import android.widget.CompoundButton;
@@ -37,17 +35,14 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.flags.Flags;
import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.utils.ThreadUtils;
-import com.google.common.collect.ImmutableList;
-
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -273,18 +268,17 @@
mSwitchBar.setEnabled(true);
return;
}
- Map<Integer, List<CachedBluetoothDevice>> groupedDevices = fetchConnectedDevicesByGroupId();
+ Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
+ AudioSharingUtils.fetchConnectedDevicesByGroupId(mBtManager);
ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
Optional<Integer> activeGroupId = Optional.empty();
for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
// Use random device in the group to represent the group.
CachedBluetoothDevice device = devices.get(0);
- // TODO: add BluetoothUtils.isActiveLeAudioDevice to avoid directly using isActiveDevice
- if (device.isActiveDevice(BluetoothProfile.LE_AUDIO)) {
+ if (BluetoothUtils.isActiveLeAudioDevice(device)) {
activeGroupId = Optional.of(device.getGroupId());
} else {
- AudioSharingDeviceItem item =
- new AudioSharingDeviceItem(device.getName(), device.getGroupId());
+ AudioSharingDeviceItem item = AudioSharingUtils.buildAudioSharingDeviceItem(device);
deviceItems.add(item);
}
}
@@ -347,31 +341,6 @@
return mBroadcast != null && mBroadcast.isEnabled(null);
}
- private Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId() {
- // TODO: filter out devices with le audio disabled.
- List<BluetoothDevice> connectedDevices =
- mAssistant == null ? ImmutableList.of() : mAssistant.getConnectedDevices();
- Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
- CachedBluetoothDeviceManager cacheManager = mBtManager.getCachedDeviceManager();
- for (BluetoothDevice device : connectedDevices) {
- CachedBluetoothDevice cachedDevice = cacheManager.findDevice(device);
- if (cachedDevice == null) {
- Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
- continue;
- }
- int groupId = cachedDevice.getGroupId();
- if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
- Log.d(TAG, "Skip device due to no valid group id");
- continue;
- }
- if (!groupedDevices.containsKey(groupId)) {
- groupedDevices.put(groupId, new ArrayList<>());
- }
- groupedDevices.get(groupId).add(cachedDevice);
- }
- return groupedDevices;
- }
-
private void addSourceToTargetDevices(List<BluetoothDevice> sinks) {
if (sinks.isEmpty() || mBroadcast == null || mAssistant == null) {
Log.d(TAG, "Skip adding source to target.");
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
new file mode 100644
index 0000000..4ece70e
--- /dev/null
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 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.connecteddevice.audiosharing;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.util.Log;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AudioSharingUtils {
+ private static final String TAG = "AudioSharingUtils";
+
+ /**
+ * Fetch {@link CachedBluetoothDevice}s connected to the broadcast assistant. The devices are
+ * grouped by CSIP group id.
+ *
+ * @param localBtManager The BT manager to provide BT functions.
+ * @return A map of connected devices grouped by CSIP group id.
+ */
+ public static Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId(
+ LocalBluetoothManager localBtManager) {
+ Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
+ LocalBluetoothLeBroadcastAssistant assistant =
+ localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ if (assistant == null) return groupedDevices;
+ // TODO: filter out devices with le audio disabled.
+ List<BluetoothDevice> connectedDevices = assistant.getConnectedDevices();
+ CachedBluetoothDeviceManager cacheManager = localBtManager.getCachedDeviceManager();
+ for (BluetoothDevice device : connectedDevices) {
+ CachedBluetoothDevice cachedDevice = cacheManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.d(TAG, "Skip device due to not being cached: " + device.getAnonymizedAddress());
+ continue;
+ }
+ int groupId = cachedDevice.getGroupId();
+ if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ Log.d(
+ TAG,
+ "Skip device due to no valid group id: " + device.getAnonymizedAddress());
+ continue;
+ }
+ if (!groupedDevices.containsKey(groupId)) {
+ groupedDevices.put(groupId, new ArrayList<>());
+ }
+ groupedDevices.get(groupId).add(cachedDevice);
+ }
+ return groupedDevices;
+ }
+
+ /**
+ * Fetch a list of {@link AudioSharingDeviceItem}s in the audio sharing session.
+ *
+ * @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
+ * id.
+ * @param localBtManager The BT manager to provide BT functions.
+ * @return A list of connected devices in the audio sharing session.
+ */
+ public static ArrayList<AudioSharingDeviceItem> buildOrderedDeviceItemsInSharingSession(
+ Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
+ LocalBluetoothManager localBtManager) {
+ ArrayList<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
+ LocalBluetoothLeBroadcastAssistant assistant =
+ localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ if (assistant == null) return deviceItems;
+ CachedBluetoothDevice activeDevice = null;
+ List<CachedBluetoothDevice> inactiveDevices = new ArrayList<>();
+ for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
+ for (CachedBluetoothDevice device : devices) {
+ List<BluetoothLeBroadcastReceiveState> sourceList =
+ assistant.getAllSources(device.getDevice());
+ if (!sourceList.isEmpty()) {
+ // Use random device in the group within the sharing session to
+ // represent the group.
+ if (BluetoothUtils.isActiveLeAudioDevice(device)) {
+ activeDevice = device;
+ } else {
+ inactiveDevices.add(device);
+ }
+ break;
+ }
+ }
+ }
+ if (activeDevice != null) {
+ deviceItems.add(buildAudioSharingDeviceItem(activeDevice));
+ }
+ inactiveDevices.stream()
+ .sorted(CachedBluetoothDevice::compareTo)
+ .forEach(
+ device -> {
+ deviceItems.add(buildAudioSharingDeviceItem(device));
+ });
+ return deviceItems;
+ }
+
+ /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
+ public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
+ CachedBluetoothDevice cachedDevice) {
+ return new AudioSharingDeviceItem(cachedDevice.getName(), cachedDevice.getGroupId());
+ }
+}
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index 38f09f4..fb28d68 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -44,6 +44,7 @@
import com.android.settings.datausage.lib.AppDataUsageDetailsRepository;
import com.android.settings.datausage.lib.NetworkTemplates;
import com.android.settings.datausage.lib.NetworkUsageDetailsData;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.AppItem;
@@ -325,7 +326,8 @@
private boolean getAppRestrictBackground() {
final int uid = mAppItem.key;
final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
- return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+ return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0
+ && DynamicDenylistManager.getInstance(mContext).isInManualDenylist(uid);
}
private boolean getUnrestrictData() {
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index b4b6b8c..6e99453 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -23,6 +23,7 @@
import android.net.NetworkPolicyManager;
import android.util.SparseIntArray;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;
@@ -39,6 +40,7 @@
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final NetworkPolicyManager mPolicyManager;
+ private final DynamicDenylistManager mDynamicDenylistManager;
private final ArrayList<Listener> mListeners = new ArrayList<>();
private SparseIntArray mUidPolicies = new SparseIntArray();
private boolean mAllowlistInitialized;
@@ -50,6 +52,7 @@
mContext = context.getApplicationContext();
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
mPolicyManager = NetworkPolicyManager.from(mContext);
+ mDynamicDenylistManager = DynamicDenylistManager.getInstance(mContext);
}
public void addListener(Listener listener) {
@@ -83,7 +86,7 @@
public void setIsAllowlisted(int uid, String packageName, boolean allowlisted) {
final int policy = allowlisted ? POLICY_ALLOW_METERED_BACKGROUND : POLICY_NONE;
- mPolicyManager.setUidPolicy(uid, policy);
+ mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
mUidPolicies.put(uid, policy);
if (allowlisted) {
mMetricsFeatureProvider.action(
@@ -113,7 +116,7 @@
public void setIsDenylisted(int uid, String packageName, boolean denylisted) {
final int policy = denylisted ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE;
- mPolicyManager.setUidPolicy(uid, policy);
+ mDynamicDenylistManager.setUidPolicyLocked(uid, policy);
mUidPolicies.put(uid, policy);
if (denylisted) {
mMetricsFeatureProvider.action(
@@ -123,7 +126,8 @@
public boolean isDenylisted(int uid) {
loadDenylist();
- return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND;
+ return mUidPolicies.get(uid, POLICY_NONE) == POLICY_REJECT_METERED_BACKGROUND
+ && mDynamicDenylistManager.isInManualDenylist(uid);
}
private void loadDenylist() {
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
index 5d9d047..dd49c8b 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
@@ -26,6 +26,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.batterysaver.BatterySaverScheduleRadioButtonsController;
+import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import java.util.List;
@@ -50,6 +51,8 @@
context = context.getApplicationContext();
verifySaverConfiguration(context);
verifyBatteryOptimizeModes(context);
+ // Initialize and sync settings into SharedPreferences for migration.
+ DynamicDenylistManager.getInstance(context);
}
/** Avoid users set important apps into the unexpected battery optimize modes */
diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
index be72e56..7eae7eb 100644
--- a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
+++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
@@ -16,12 +16,24 @@
package com.android.settings.fuelgauge.datasaver;
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+import static com.android.settings.fuelgauge.BatteryUtils.UID_ZERO;
+
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.NetworkPolicyManager;
+import android.util.ArraySet;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
+import java.util.List;
+import java.util.Set;
+
/** A class to dynamically manage per apps {@link NetworkPolicyManager} POLICY_ flags. */
public final class DynamicDenylistManager {
@@ -29,47 +41,157 @@
private static final String PREF_KEY_MANUAL_DENY = "manual_denylist_preference";
private static final String PREF_KEY_DYNAMIC_DENY = "dynamic_denylist_preference";
+ private static DynamicDenylistManager sInstance;
+
private final Context mContext;
private final NetworkPolicyManager mNetworkPolicyManager;
+ private final Object mLock = new Object();
- private static DynamicDenylistManager sInstance;
+ @VisibleForTesting
+ static final String PREF_KEY_MANUAL_DENYLIST_SYNCED = "manual_denylist_synced";
/** @return a DynamicDenylistManager object */
public static DynamicDenylistManager getInstance(Context context) {
synchronized (DynamicDenylistManager.class) {
if (sInstance == null) {
- sInstance = new DynamicDenylistManager(context);
+ sInstance = new DynamicDenylistManager(
+ context, NetworkPolicyManager.from(context));
}
return sInstance;
}
}
- DynamicDenylistManager(Context context) {
+ @VisibleForTesting
+ DynamicDenylistManager(Context context, NetworkPolicyManager networkPolicyManager) {
mContext = context.getApplicationContext();
- mNetworkPolicyManager = NetworkPolicyManager.from(mContext);
+ mNetworkPolicyManager = networkPolicyManager;
+ syncPolicyIfNeeded();
}
- /** Update the target uid policy in {@link #getManualDenylistPref()}. */
- public void updateManualDenylist(String uid, int policy) {
- if (policy != NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND) {
- getManualDenylistPref().edit().remove(uid).apply();
- } else {
- getManualDenylistPref().edit().putInt(uid, policy).apply();
+ /** Sync the policy from {@link NetworkPolicyManager} if needed. */
+ private void syncPolicyIfNeeded() {
+ if (getManualDenylistPref().contains(PREF_KEY_MANUAL_DENYLIST_SYNCED)) {
+ Log.i(TAG, "syncPolicyIfNeeded() ignore synced manual denylist");
+ return;
+ }
+
+ final SharedPreferences.Editor editor = getManualDenylistPref().edit();
+ final int[] existedUids = mNetworkPolicyManager
+ .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
+ if (existedUids != null && existedUids.length != 0) {
+ for (int uid : existedUids) {
+ editor.putInt(String.valueOf(uid), POLICY_REJECT_METERED_BACKGROUND);
+ }
+ }
+ editor.putInt(PREF_KEY_MANUAL_DENYLIST_SYNCED, POLICY_NONE).apply();
+ }
+
+ /** Set policy flags for specific UID. */
+ public void setUidPolicyLocked(int uid, int policy) {
+ synchronized (mLock) {
+ mNetworkPolicyManager.setUidPolicy(uid, policy);
+ }
+ updateDenylistPref(uid, policy);
+ }
+
+ /** Suggest a list of package to set as POLICY_REJECT. */
+ public void setDenylist(List<String> packageNameList) {
+ final Set<Integer> denylistTargetUids = new ArraySet<>(packageNameList.size());
+ for (String packageName : packageNameList) {
+ try {
+ final int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ if (uid == UID_ZERO) {
+ continue;
+ }
+ denylistTargetUids.add(uid);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unknown package name: " + packageName, e);
+ }
+ }
+
+ final Set<Integer> manualDenylistUids = getDenylistAllUids(getManualDenylistPref());
+ denylistTargetUids.removeAll(manualDenylistUids);
+
+ final Set<Integer> lastDynamicDenylistUids = getDenylistAllUids(getDynamicDenylistPref());
+ if (lastDynamicDenylistUids.equals(denylistTargetUids)) {
+ Log.i(TAG, "setDenylist() ignore the same denylist with size: "
+ + lastDynamicDenylistUids.size());
+ return;
+ }
+
+ // Store target denied uids into DynamicDenylistPref.
+ final SharedPreferences.Editor editor = getDynamicDenylistPref().edit();
+ editor.clear();
+ denylistTargetUids.forEach(
+ uid -> editor.putInt(String.valueOf(uid), POLICY_REJECT_METERED_BACKGROUND));
+ editor.apply();
+
+ // Set new added UIDs into REJECT policy.
+ synchronized (mLock) {
+ for (int uid : denylistTargetUids) {
+ if (!lastDynamicDenylistUids.contains(uid)) {
+ mNetworkPolicyManager.setUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
+ }
+ }
+ }
+ // Unset removed UIDs back to NONE policy.
+ synchronized (mLock) {
+ for (int uid : lastDynamicDenylistUids) {
+ if (!denylistTargetUids.contains(uid)) {
+ mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE);
+ }
+ }
}
}
/** Return true if the target uid is in {@link #getManualDenylistPref()}. */
- public boolean isInManualDenylist(String uid) {
- return getManualDenylistPref().contains(uid);
+ public boolean isInManualDenylist(int uid) {
+ return getManualDenylistPref().contains(String.valueOf(uid));
}
- /** Clear all data in {@link #getManualDenylistPref()} */
- public void clearManualDenylistPref() {
+ /** Reset the UIDs in the denylist if needed. */
+ public void resetDenylistIfNeeded(String packageName, boolean force) {
+ if (!force && !SETTINGS_PACKAGE_NAME.equals(packageName)) {
+ return;
+ }
+ synchronized (mLock) {
+ for (int uid : mNetworkPolicyManager
+ .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
+ if (!getDenylistAllUids(getManualDenylistPref()).contains(uid)) {
+ mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE);
+ }
+ }
+ }
+ clearSharedPreferences();
+ }
+
+ private Set<Integer> getDenylistAllUids(SharedPreferences sharedPreferences) {
+ final ArraySet<Integer> uids = new ArraySet<>();
+ for (String key : sharedPreferences.getAll().keySet()) {
+ if (PREF_KEY_MANUAL_DENYLIST_SYNCED.equals(key)) {
+ continue;
+ }
+ try {
+ uids.add(Integer.parseInt(key));
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "getDenylistAllUids() unexpected format for " + key);
+ }
+ }
+ return uids;
+ }
+
+ void updateDenylistPref(int uid, int policy) {
+ final String uidString = String.valueOf(uid);
+ if (policy != POLICY_REJECT_METERED_BACKGROUND) {
+ getManualDenylistPref().edit().remove(uidString).apply();
+ } else {
+ getManualDenylistPref().edit().putInt(uidString, policy).apply();
+ }
+ getDynamicDenylistPref().edit().remove(uidString).apply();
+ }
+
+ void clearSharedPreferences() {
getManualDenylistPref().edit().clear().apply();
- }
-
- /** Clear all data in {@link #getDynamicDenylistPref()} */
- public void clearDynamicDenylistPref() {
getDynamicDenylistPref().edit().clear().apply();
}
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 70d4d7d..a0db4ce 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -31,6 +31,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserProperties;
import android.content.res.Configuration;
import android.graphics.Color;
import android.hardware.biometrics.BiometricConstants;
@@ -61,11 +62,6 @@
public class ConfirmDeviceCredentialActivity extends FragmentActivity {
public static final String TAG = ConfirmDeviceCredentialActivity.class.getSimpleName();
- // The normal flow that apps go through
- private static final int CREDENTIAL_NORMAL = 1;
- // Unlocks the managed profile when the primary profile is unlocked
- private static final int CREDENTIAL_MANAGED = 2;
-
private static final String TAG_BIOMETRIC_FRAGMENT = "fragment";
public static class InternalActivity extends ConfirmDeviceCredentialActivity {
@@ -84,7 +80,9 @@
private String mTitle;
private CharSequence mDetails;
private int mUserId;
- private int mCredentialMode;
+ // Used to force the verification path required to unlock profile that shares credentials with
+ // with parent
+ private boolean mForceVerifyPath = false;
private boolean mGoingToBackground;
private boolean mWaitingForBiometricCallback;
@@ -189,7 +187,9 @@
}
final int effectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
final boolean isEffectiveUserManagedProfile =
- UserManager.get(this).isManagedProfile(effectiveUserId);
+ mUserManager.isManagedProfile(effectiveUserId);
+ final UserProperties userProperties =
+ mUserManager.getUserProperties(UserHandle.of(mUserId));
// if the client app did not hand in a title and we are about to show the work challenge,
// check whether there is a policy setting the organization name and use that as title
if ((mTitle == null) && isEffectiveUserManagedProfile) {
@@ -278,7 +278,19 @@
.setForceVerifyPath(true)
.show();
} else if (isEffectiveUserManagedProfile && isInternalActivity()) {
- mCredentialMode = CREDENTIAL_MANAGED;
+ // When the mForceVerifyPath is set to true, we launch the real confirm credential
+ // activity with an explicit but fake challenge value (0L). This will result in
+ // ConfirmLockPassword calling verifyTiedProfileChallenge() (if it's a profile with
+ // unified challenge), due to the difference between
+ // ConfirmLockPassword.startVerifyPassword() and
+ // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here
+ // is necessary when this is part of the turning on work profile flow, because it forces
+ // unlocking the work profile even before the profile is running.
+ // TODO: Remove the duplication of checkPassword and verifyPassword in
+ // ConfirmLockPassword,
+ // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to
+ // use, which optionally accepts a challenge.
+ mForceVerifyPath = true;
if (isBiometricAllowed(effectiveUserId, mUserId)) {
showBiometricPrompt(promptInfo);
launchedBiometric = true;
@@ -286,8 +298,19 @@
showConfirmCredentials();
launchedCDC = true;
}
+ } else if (android.os.Flags.allowPrivateProfile()
+ && userProperties != null
+ && userProperties.isAuthAlwaysRequiredToDisableQuietMode()
+ && isInternalActivity()) {
+ // Force verification path is required to be invoked as we might need to verify the tied
+ // profile challenge if the profile is using the unified challenge mode. This would
+ // result in ConfirmLockPassword.startVerifyPassword/
+ // ConfirmLockPattern.startVerifyPattern being called instead of the
+ // startCheckPassword/startCheckPattern
+ mForceVerifyPath = userProperties.isCredentialShareableWithParent();
+ showConfirmCredentials();
+ launchedCDC = true;
} else {
- mCredentialMode = CREDENTIAL_NORMAL;
if (isBiometricAllowed(effectiveUserId, mUserId)) {
// Don't need to check if biometrics / pin/pattern/pass are enrolled. It will go to
// onAuthenticationError and do the right thing automatically.
@@ -313,11 +336,8 @@
private String getTitleFromCredentialType(@LockPatternUtils.CredentialType int credentialType,
boolean isEffectiveUserManagedProfile) {
- int overrideStringId;
- int defaultStringId;
switch (credentialType) {
case LockPatternUtils.CREDENTIAL_TYPE_PIN:
-
if (isEffectiveUserManagedProfile) {
return mDevicePolicyManager.getResources().getString(
CONFIRM_WORK_PROFILE_PIN_HEADER,
@@ -410,29 +430,15 @@
* Shows ConfirmDeviceCredentials for normal apps.
*/
private void showConfirmCredentials() {
- boolean launched = false;
- ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this)
+ boolean launched = new ChooseLockSettingsHelper.Builder(this)
.setHeader(mTitle)
.setDescription(mDetails)
.setExternal(true)
.setUserId(mUserId)
- .setTaskOverlay(mTaskOverlay);
- // The only difference between CREDENTIAL_MANAGED and CREDENTIAL_NORMAL is that for
- // CREDENTIAL_MANAGED, we launch the real confirm credential activity with an explicit
- // but fake challenge value (0L). This will result in ConfirmLockPassword calling
- // verifyTiedProfileChallenge() (if it's a profile with unified challenge), due to the
- // difference between ConfirmLockPassword.startVerifyPassword() and
- // ConfirmLockPassword.startCheckPassword(). Calling verifyTiedProfileChallenge() here is
- // necessary when this is part of the turning on work profile flow, because it forces
- // unlocking the work profile even before the profile is running.
- // TODO: Remove the duplication of checkPassword and verifyPassword in ConfirmLockPassword,
- // LockPatternChecker and LockPatternUtils. verifyPassword should be the only API to use,
- // which optionally accepts a challenge.
- if (mCredentialMode == CREDENTIAL_MANAGED) {
- launched = builder.setForceVerifyPath(true).show();
- } else if (mCredentialMode == CREDENTIAL_NORMAL) {
- launched = builder.show();
- }
+ .setTaskOverlay(mTaskOverlay)
+ .setForceVerifyPath(mForceVerifyPath)
+ .show();
+
if (!launched) {
Log.d(TAG, "No pin/pattern/pass set");
setResult(Activity.RESULT_OK);
diff --git a/src/com/android/settings/privatespace/HidePrivateSpaceController.java b/src/com/android/settings/privatespace/HidePrivateSpaceController.java
index b972a3f..8a0f167 100644
--- a/src/com/android/settings/privatespace/HidePrivateSpaceController.java
+++ b/src/com/android/settings/privatespace/HidePrivateSpaceController.java
@@ -16,10 +16,10 @@
package com.android.settings.privatespace;
-import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL;
import android.content.Context;
-import android.provider.Settings;
import com.android.settings.core.TogglePreferenceController;
@@ -28,11 +28,11 @@
* in All Apps.
*/
public class HidePrivateSpaceController extends TogglePreferenceController {
- private static final int DISABLED_VALUE = 0;
- private static final int ENABLED_VALUE = 1;
+ private final PrivateSpaceMaintainer mPrivateSpaceMaintainer;
public HidePrivateSpaceController(Context context, String key) {
super(context, key);
+ mPrivateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(context);
}
@Override
@@ -43,14 +43,15 @@
@Override
public boolean isChecked() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- HIDE_PRIVATESPACE_ENTRY_POINT, DISABLED_VALUE) != DISABLED_VALUE;
+ return mPrivateSpaceMaintainer.getHidePrivateSpaceEntryPointSetting()
+ != HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
}
@Override
public boolean setChecked(boolean isChecked) {
- Settings.Secure.putInt(mContext.getContentResolver(), HIDE_PRIVATESPACE_ENTRY_POINT,
- isChecked ? ENABLED_VALUE : DISABLED_VALUE);
+ mPrivateSpaceMaintainer.setHidePrivateSpaceEntryPointSetting(
+ isChecked ? HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL
+ : HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
return true;
}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index e6094ce..341110b 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -17,6 +17,7 @@
package com.android.settings.privatespace;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT;
import android.app.ActivityManager;
import android.app.IActivityManager;
@@ -27,6 +28,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;
@@ -40,6 +42,7 @@
/** A class to help with the creation / deletion of Private Space */
public class PrivateSpaceMaintainer {
private static final String TAG = "PrivateSpaceMaintainer";
+
@GuardedBy("this")
private static PrivateSpaceMaintainer sPrivateSpaceMaintainer;
@@ -49,6 +52,10 @@
private UserHandle mUserHandle;
private final KeyguardManager mKeyguardManager;
+ /** This is the default value for the hide private space entry point settings. */
+ public static final int HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL = 0;
+ public static final int HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL = 1;
+
public enum ErrorDeletingPrivateSpace {
DELETE_PS_ERROR_NONE,
DELETE_PS_ERROR_NO_PRIVATE_SPACE,
@@ -91,6 +98,7 @@
}
Log.i(TAG, "Private space created with id: " + mUserHandle.getIdentifier());
+ resetPrivateSpaceSettings();
}
return true;
}
@@ -197,4 +205,21 @@
return doesPrivateSpaceExist()
&& mKeyguardManager.isDeviceSecure(mUserHandle.getIdentifier());
}
+
+ /** Sets the setting to show PS entry point to the provided value. */
+ public void setHidePrivateSpaceEntryPointSetting(int value) {
+ Settings.Secure.putInt(mContext.getContentResolver(), HIDE_PRIVATESPACE_ENTRY_POINT, value);
+ }
+
+ /** @return the setting to show PS entry point. */
+ public int getHidePrivateSpaceEntryPointSetting() {
+ return Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ HIDE_PRIVATESPACE_ENTRY_POINT,
+ HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
+ }
+
+ private void resetPrivateSpaceSettings() {
+ setHidePrivateSpaceEntryPointSetting(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
+ }
}
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 64699ff..29d136f 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -1724,6 +1724,9 @@
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
boolean enabled) {
final List<SearchIndexableRaw> rawData = new ArrayList<>();
+ if (!UserManager.supportsMultipleUsers()) {
+ return rawData;
+ }
SearchIndexableRaw allowMultipleUsersResult = new SearchIndexableRaw(context);
diff --git a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
index 87cd544..14306e0 100644
--- a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
@@ -26,31 +26,39 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Activity;
import android.content.ComponentName;
-import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.LayoutInflater;
import android.widget.PopupWindow;
import android.widget.SeekBar;
+import androidx.fragment.app.testing.EmptyFragmentActivity;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.testutils.shadow.ShadowFragment;
-import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.LabeledSeekBarPreference;
+import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
+
+import com.google.android.setupcompat.util.WizardManagerHelper;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;
@@ -64,10 +72,16 @@
@LooperMode(LooperMode.Mode.LEGACY)
@Config(shadows = {ShadowInteractionJankMonitor.class})
public class PreviewSizeSeekBarControllerTest {
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Rule
+ public ActivityScenarioRule<EmptyFragmentActivity> rule =
+ new ActivityScenarioRule<>(EmptyFragmentActivity.class);
private static final String FONT_SIZE_KEY = "font_size";
private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
- @Spy
- private final Context mContext = ApplicationProvider.getApplicationContext();
+ private Activity mContext;
private PreviewSizeSeekBarController mSeekBarController;
private FontSizeData mFontSizeData;
private LabeledSeekBarPreference mSeekBarPreference;
@@ -91,7 +105,9 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ ShadowInteractionJankMonitor.reset();
+ rule.getScenario().onActivity(activity -> mContext = activity);
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
mFragment = spy(new TestFragment());
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
@@ -197,6 +213,24 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_REMOVE_QS_TOOLTIP_IN_SUW)
+ public void onProgressChanged_inSuw_toolTipShouldNotShown() {
+ Intent intent = mContext.getIntent();
+ intent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
+ mContext.setIntent(intent);
+ mSeekBarController.displayPreference(mPreferenceScreen);
+
+ // Simulate changing the progress for the first time
+ int newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
+ mSeekBarPreference.setProgress(newProgress);
+ mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
+ newProgress,
+ /* fromUser= */ false);
+
+ assertThat(getLatestPopupWindow()).isNull();
+ }
+
+ @Test
public void onProgressChanged_tooltipViewHasBeenShown_notShowTooltipView() {
mSeekBarController.displayPreference(mPreferenceScreen);
// Simulate changing the progress for the first time
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 66211a2..2c59c26 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -60,6 +60,8 @@
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.widget.TopIntroPreference;
+import com.google.android.setupcompat.util.WizardManagerHelper;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -302,6 +304,20 @@
}
@Test
+ @RequiresFlagsEnabled(com.android.settings.accessibility.Flags.FLAG_REMOVE_QS_TOOLTIP_IN_SUW)
+ @Config(shadows = ShadowFragment.class)
+ public void onPreferenceToggledOnEnabledService_inSuw_toolTipViewShouldNotShow() {
+ Intent suwIntent = new Intent();
+ suwIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
+ when(mActivity.getIntent()).thenReturn(suwIntent);
+
+ mFragment.onPreferenceToggled(
+ ToggleFeaturePreferenceFragment.KEY_USE_SERVICE_PREFERENCE, /* enabled= */ true);
+
+ assertThat(getLatestPopupWindow()).isNull();
+ }
+
+ @Test
@Config(shadows = ShadowFragment.class)
public void onPreferenceToggledOnEnabledService_tooltipViewShown_notShowTooltipView() {
mFragment.onPreferenceToggled(
diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
index 7b7c7a6..1d841fa 100644
--- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
@@ -239,6 +239,7 @@
ReflectionHelpers.setField(mFragment, "mUnrestrictedData", unrestrictedDataPref);
ReflectionHelpers.setField(mFragment, "mDataSaverBackend", dataSaverBackend);
ReflectionHelpers.setField(mFragment.services, "mPolicyManager", networkPolicyManager);
+ ReflectionHelpers.setField(mFragment, "mContext", RuntimeEnvironment.application);
when(mFragment.getListView()).thenReturn(mock(RecyclerView.class));
ShadowRestrictedLockUtilsInternal.setRestricted(true);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
index cdf1514..bfa7cfa 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
@@ -19,138 +19,373 @@
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+import static com.android.settings.fuelgauge.datasaver.DynamicDenylistManager.PREF_KEY_MANUAL_DENYLIST_SYNCED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+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 static org.mockito.Mockito.when;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.NetworkPolicyManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.Collections;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
public class DynamicDenylistManagerTest {
- private static final String FAKE_UID_1 = "package_uid_1";
- private static final String FAKE_UID_2 = "package_uid_2";
+ private static final int[] EMPTY_ARRAY = new int[]{};
+ private static final String FAKE_UID_1 = "1001";
+ private static final String FAKE_UID_2 = "1002";
+ private static final int FAKE_UID_1_INT = Integer.parseInt(FAKE_UID_1);
+ private static final int FAKE_UID_2_INT = Integer.parseInt(FAKE_UID_2);
private SharedPreferences mManualDenyListPref;
private SharedPreferences mDynamicDenyListPref;
private DynamicDenylistManager mDynamicDenylistManager;
- private Context mContext;
+
+ @Mock
+ private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
+ private PackageManager mPackageManager;
@Before
public void setUp() {
- mContext = RuntimeEnvironment.application.getApplicationContext();
- mDynamicDenylistManager = new DynamicDenylistManager(mContext);
- mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref();
- mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref();
+ MockitoAnnotations.initMocks(this);
}
@After
public void tearDown() {
- mDynamicDenylistManager.clearManualDenylistPref();
- mDynamicDenylistManager.clearDynamicDenylistPref();
+ mDynamicDenylistManager.clearSharedPreferences();
}
@Test
- public void getManualDenylistPref_isEmpty() {
- assertThat(mManualDenyListPref.getAll()).isEmpty();
+ public void init_withoutExistedRejectPolicy_createWithExpectedValue() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+
+ assertThat(mManualDenyListPref.getAll()).hasSize(1);
+ assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
}
@Test
- public void getDynamicDenylistPref_isEmpty() {
- assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+ public void init_withExistedRejectPolicy_createWithExpectedValue() {
+ initDynamicDenylistManager(new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+ assertThat(mManualDenyListPref.getAll()).hasSize(3);
+ assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_2));
}
@Test
public void getManualDenylistPref_initiated_containsExpectedValue() {
- mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ initDynamicDenylistManager(EMPTY_ARRAY);
- assertThat(mManualDenyListPref.getAll().size()).isEqualTo(1);
+ setupPreference(mManualDenyListPref, FAKE_UID_1);
+
assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
}
@Test
public void getDynamicDenylistPref_initiated_containsExpectedValue() {
- mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ initDynamicDenylistManager(EMPTY_ARRAY);
- assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+ setupPreference(mDynamicDenyListPref, FAKE_UID_1);
+
assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
}
@Test
public void updateManualDenylist_policyReject_addsUid() {
- mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+ initDynamicDenylistManager(EMPTY_ARRAY);
- assertThat(mManualDenyListPref.getAll()).hasSize(1);
+ mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT,
+ POLICY_REJECT_METERED_BACKGROUND);
+
assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
}
@Test
public void updateManualDenylist_policyNone_removesUid() {
- mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ initDynamicDenylistManager(EMPTY_ARRAY);
+ setupPreference(mManualDenyListPref, FAKE_UID_1);
assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
- mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_NONE);
+ mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT, POLICY_NONE);
- assertThat(mManualDenyListPref.getAll()).isEmpty();
+ assertFalse(mManualDenyListPref.contains(FAKE_UID_1));
}
@Test
public void updateManualDenylist_samePolicy_doNothing() {
- mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ initDynamicDenylistManager(EMPTY_ARRAY);
+ setupPreference(mManualDenyListPref, FAKE_UID_1);
assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ assertThat(mManualDenyListPref.getAll()).hasSize(2);
- mDynamicDenylistManager.updateManualDenylist(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND);
+ mDynamicDenylistManager.updateDenylistPref(FAKE_UID_1_INT,
+ POLICY_REJECT_METERED_BACKGROUND);
- assertThat(mManualDenyListPref.getAll()).hasSize(1);
+ assertThat(mManualDenyListPref.getAll()).hasSize(2);
}
@Test
- public void isManualDenylist_returnsFalse() {
- assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+ public void setUidPolicyLocked_invokeSetUidPolicy() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+
+ mDynamicDenylistManager.setUidPolicyLocked(FAKE_UID_1_INT,
+ POLICY_REJECT_METERED_BACKGROUND);
+
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_1_INT),
+ eq(POLICY_REJECT_METERED_BACKGROUND));
}
@Test
- public void isManualDenylist_incorrectUid_returnsFalse() {
+ public void setDenylist_emptyListAndNoData_doNothing() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+
+ mDynamicDenylistManager.setDenylist(Collections.emptyList());
+
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
+ }
+
+ @Test
+ public void setDenylist_uidDeniedAlready_doNothing()
+ throws PackageManager.NameNotFoundException {
+ when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(FAKE_UID_1_INT);
+ initDynamicDenylistManager(new int[]{FAKE_UID_1_INT});
+
+ mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1));
+
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
+ }
+
+ @Test
+ public void setDenylist_sameList_doNothing() throws PackageManager.NameNotFoundException {
+ when(mPackageManager.getPackageUid(eq(FAKE_UID_1), eq(0))).thenReturn(FAKE_UID_1_INT);
+ when(mPackageManager.getPackageUid(eq(FAKE_UID_2), eq(0))).thenReturn(FAKE_UID_2_INT);
+ initDynamicDenylistManager(EMPTY_ARRAY);
+ setupPreference(mDynamicDenyListPref, FAKE_UID_2, FAKE_UID_1);
+
+ mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1, FAKE_UID_2));
+
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), anyInt());
+ }
+
+ @Test
+ public void setDenylist_newListWithOldData_modifyPolicyNoneAndReject()
+ throws PackageManager.NameNotFoundException {
+ when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(
+ Integer.parseInt(FAKE_UID_1));
+ initDynamicDenylistManager(EMPTY_ARRAY);
+ setupPreference(mDynamicDenyListPref, FAKE_UID_2);
+
+ mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1));
+
+ verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE);
+ verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT,
+ POLICY_REJECT_METERED_BACKGROUND);
+ assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+ assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+ }
+
+ @Test
+ public void setDenylist_newListWithoutOldData_modifyPolicyReject()
+ throws PackageManager.NameNotFoundException {
+ when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(
+ Integer.parseInt(FAKE_UID_1));
+ initDynamicDenylistManager(EMPTY_ARRAY);
+
+ mDynamicDenylistManager.setDenylist(List.of(FAKE_UID_1));
+
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_1_INT,
+ POLICY_REJECT_METERED_BACKGROUND);
+ assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+ assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
+ }
+
+ @Test
+ public void setDenylist_emptyListWithOldData_modifyPolicyNone() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+ setupPreference(mDynamicDenyListPref, FAKE_UID_2);
+
+ mDynamicDenylistManager.setDenylist(Collections.emptyList());
+
+ verify(mNetworkPolicyManager).setUidPolicy(FAKE_UID_2_INT, POLICY_NONE);
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(),
+ eq(POLICY_REJECT_METERED_BACKGROUND));
+ assertThat(mDynamicDenyListPref.getAll()).isEmpty();
+ }
+
+ @Test
+ public void isInManualDenylist_returnsFalse() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+
+ assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
+ }
+
+ @Test
+ public void isInManualDenylist_incorrectUid_returnsFalse() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+
mManualDenyListPref.edit().putInt(FAKE_UID_2, POLICY_REJECT_METERED_BACKGROUND).apply();
- assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+ assertFalse(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
}
@Test
- public void isManualDenylist_initiated_returnsTrue() {
+ public void isInManualDenylist_initiated_returnsTrue() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+
mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
- assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1));
+ assertTrue(mDynamicDenylistManager.isInManualDenylist(FAKE_UID_1_INT));
}
@Test
- public void clearManualDenylistPref_isEmpty() {
- mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ public void resetDenylistIfNeeded_nullPackageName_doNothing() {
+ initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded(null, false);
+
assertThat(mManualDenyListPref.getAll()).hasSize(1);
- assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ }
- mDynamicDenylistManager.clearManualDenylistPref();
+ @Test
+ public void resetDenylistIfNeeded_invalidPackageName_doNothing() {
+ initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", false);
+
+ assertThat(mManualDenyListPref.getAll()).hasSize(1);
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ }
+
+ @Test
+ public void resetDenylistIfNeeded_denylistUnchanged_doNothingWithPolicy() {
+ initDynamicDenylistManager(new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false);
+
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ }
+
+ @Test
+ public void resetDenylistIfNeeded_denylistChanged_resetAndClear() {
+ initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, false);
+
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ }
+
+ @Test
+ public void resetDenylistIfNeeded_forceResetWithNullPackageName_resetAndClear() {
+ initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_2_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded(null, true);
+
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ verify(mNetworkPolicyManager).setUidPolicy(eq(FAKE_UID_2_INT), eq(POLICY_NONE));
+ }
+
+ @Test// 4
+ public void resetDenylistIfNeeded_forceResetWithInvalidPackageName_resetAndClear() {
+ initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded("invalid_package_name", true);
+
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ }
+
+ @Test
+ public void resetDenylistIfNeeded_forceResetButDenylistUnchanged_doNothingWithPolicy() {
+ initDynamicDenylistManager(new int[]{FAKE_UID_1_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true);
+
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ verify(mNetworkPolicyManager, never()).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ }
+
+ @Test
+ public void resetDenylistIfNeeded_forceResetWithDenylistChanged_resetAndClear() {
+ initDynamicDenylistManager(new int[0], new int[]{FAKE_UID_1_INT, FAKE_UID_2_INT});
+
+ mDynamicDenylistManager.resetDenylistIfNeeded(SETTINGS_PACKAGE_NAME, true);
+
+ assertThat(mManualDenyListPref.getAll()).isEmpty();
+ verify(mNetworkPolicyManager, times(2)).setUidPolicy(anyInt(), eq(POLICY_NONE));
+ }
+
+ @Test
+ public void clearSharedPreferences_manualDenyListPrefIsEmpty() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+ mManualDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
+ assertThat(mManualDenyListPref.getAll()).hasSize(2);
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
+
+ mDynamicDenylistManager.clearSharedPreferences();
assertThat(mManualDenyListPref.getAll()).isEmpty();
}
@Test
- public void clearDynamicDenylistPref_isEmpty() {
+ public void clearSharedPreferences_dynamicDenyListPrefIsEmpty() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
mDynamicDenyListPref.edit().putInt(FAKE_UID_1, POLICY_REJECT_METERED_BACKGROUND).apply();
assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
assertTrue(mDynamicDenyListPref.contains(FAKE_UID_1));
- mDynamicDenylistManager.clearDynamicDenylistPref();
+ mDynamicDenylistManager.clearSharedPreferences();
assertThat(mDynamicDenyListPref.getAll()).isEmpty();
}
+
+ private void initDynamicDenylistManager(int[] preload) {
+ initDynamicDenylistManager(preload, preload);
+ }
+ private void initDynamicDenylistManager(int[] preload1, int[] preload2) {
+ final Context context = spy(RuntimeEnvironment.application.getApplicationContext());
+ when(context.getApplicationContext()).thenReturn(context);
+ when(context.getPackageManager()).thenReturn(mPackageManager);
+ when(mNetworkPolicyManager.getUidsWithPolicy(anyInt()))
+ .thenReturn(preload1).thenReturn(preload2);
+ mDynamicDenylistManager = new DynamicDenylistManager(context, mNetworkPolicyManager);
+ mManualDenyListPref = mDynamicDenylistManager.getManualDenylistPref();
+ mDynamicDenyListPref = mDynamicDenylistManager.getDynamicDenylistPref();
+ }
+
+ private void setupPreference(SharedPreferences sharedPreferences, String... uids) {
+ for (String uid : uids) {
+ sharedPreferences.edit().putInt(uid, POLICY_REJECT_METERED_BACKGROUND).apply();
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
index 3b5dbd6..d394582 100644
--- a/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/security/ContentProtectionPreferenceFragmentTest.java
@@ -113,8 +113,14 @@
final List<String> allKeys =
XmlTestUtils.getKeysFromPreferenceXml(
mContext, R.layout.content_protection_preference_fragment);
+ final List<String> nonIndexableKeysExpected =
+ List.of(
+ "content_protection_preference_top_intro",
+ "content_protection_preference_subpage_illustration",
+ "content_protection_preference_user_consent_work_profile_switch");
assertThat(allKeys).containsAtLeastElementsIn(nonIndexableKeys);
+ assertThat(nonIndexableKeys).isEqualTo(nonIndexableKeysExpected);
}
@Test
@@ -132,7 +138,7 @@
XmlTestUtils.getKeysFromPreferenceXml(
mContext, R.layout.content_protection_preference_fragment);
- assertThat(nonIndexableKeys).containsAnyIn(allKeys);
+ assertThat(nonIndexableKeys).isEqualTo(allKeys);
}
@Test
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
new file mode 100644
index 0000000..0a2f3d1
--- /dev/null
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2023 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.privatespace;
+
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL;
+import static com.android.settings.privatespace.PrivateSpaceMaintainer.HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PrivateSpaceMaintainerTest {
+ private Context mContext;
+ private ContentResolver mContentResolver;
+
+ /** Required setup before a test. */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+ mContentResolver = mContext.getContentResolver();
+ }
+
+ /** Tests that {@link PrivateSpaceMaintainer#deletePrivateSpace()} deletes PS when PS exists. */
+ @Test
+ public void deletePrivateSpace_psExists_deletesPS() {
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(mContext);
+ privateSpaceMaintainer.createPrivateSpace();
+ ErrorDeletingPrivateSpace errorDeletingPrivateSpace =
+ privateSpaceMaintainer.deletePrivateSpace();
+ assertThat(errorDeletingPrivateSpace)
+ .isEqualTo(ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE);
+ assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isFalse();
+ }
+
+ /**
+ * Tests that {@link PrivateSpaceMaintainer#deletePrivateSpace()} returns error when PS does
+ * not exist.
+ */
+ @Test
+ public void deletePrivateSpace_psDoesNotExist_returnsNoPSError() {
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(mContext);
+ ErrorDeletingPrivateSpace errorDeletingPrivateSpace =
+ privateSpaceMaintainer.deletePrivateSpace();
+ assertThat(errorDeletingPrivateSpace)
+ .isEqualTo(ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NO_PRIVATE_SPACE);
+ assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isFalse();
+ }
+
+ /** Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exists creates PS. */
+ @Test
+ public void createPrivateSpace_psDoesNotExist_createsPS() {
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(mContext);
+ privateSpaceMaintainer.deletePrivateSpace();
+ assertThat(privateSpaceMaintainer.createPrivateSpace()).isTrue();
+ assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+ }
+
+ /**
+ * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exists still
+ * returns true.
+ */
+ @Test
+ public void createPrivateSpace_psExists_returnsFalse() {
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(mContext);
+ privateSpaceMaintainer.deletePrivateSpace();
+ assertThat(privateSpaceMaintainer.createPrivateSpace()).isTrue();
+ assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+ assertThat(privateSpaceMaintainer.createPrivateSpace()).isTrue();
+ }
+
+ /**
+ * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when no PS exists resets PS
+ * Settings.
+ */
+ @Test
+ public void createPrivateSpace_psDoesNotExist_resetsPSSettings() {
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(mContext);
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT,
+ HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL);
+
+ privateSpaceMaintainer.deletePrivateSpace();
+ privateSpaceMaintainer.createPrivateSpace();
+ assertThat(privateSpaceMaintainer.getHidePrivateSpaceEntryPointSetting())
+ .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
+ }
+
+ /**
+ * Tests that {@link PrivateSpaceMaintainer#createPrivateSpace()} when PS exist does not reset
+ * PS Settings.
+ */
+ @Test
+ public void createPrivateSpace_psExists_doesNotResetPSSettings() {
+ PrivateSpaceMaintainer privateSpaceMaintainer =
+ PrivateSpaceMaintainer.getInstance(mContext);
+ privateSpaceMaintainer.createPrivateSpace();
+ Settings.Secure.putInt(
+ mContentResolver,
+ Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT,
+ HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL);
+
+ privateSpaceMaintainer.createPrivateSpace();
+ assertThat(privateSpaceMaintainer.getHidePrivateSpaceEntryPointSetting())
+ .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL);
+ }
+}