Merge "Add power consumption footer in screen timeout page." into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 75c6fbb..26386c5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3389,16 +3389,6 @@
<action android:name="android.settings.USER_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
- <intent-filter>
- <action android:name="com.android.settings.action.SETTINGS" />
- </intent-filter>
- <meta-data android:name="com.android.settings.order" android:value="-45"/>
- <meta-data android:name="com.android.settings.category"
- android:value="com.android.settings.category.ia.system" />
- <meta-data android:name="com.android.settings.summary_uri"
- android:value="content://com.android.settings.dashboard.SummaryProvider/user" />
- <meta-data android:name="com.android.settings.icon"
- android:resource="@drawable/ic_settings_multiuser" />
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.users.UserSettings" />
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml
index 1f5559f..54dc195 100644
--- a/res/xml/system_dashboard_fragment.xml
+++ b/res/xml/system_dashboard_fragment.xml
@@ -93,6 +93,14 @@
</Preference>
<Preference
+ android:key="system_multiuser"
+ android:title="@string/user_settings_title"
+ android:icon="@drawable/ic_settings_multiuser"
+ android:order="-45"
+ android:fragment="com.android.settings.users.UserSettings"
+ settings:controller="com.android.settings.users.MultiUserPreferenceController"/>
+
+ <Preference
android:key="reset_dashboard"
android:title="@string/reset_dashboard_title"
android:icon="@drawable/ic_restore"
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 4c20231..3b0dd40 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -781,12 +781,6 @@
Utils.isBandwidthControlEnabled(), isAdmin)
|| somethingChanged;
- somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
- Settings.UserSettingsActivity.class.getName()),
- UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
- && !Utils.isMonkeyRunning(), isAdmin)
- || somethingChanged;
-
final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
&& !Utils.isMonkeyRunning();
somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index eeca139..f7e341b 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1209,7 +1209,9 @@
for (UserHandle userHandle : profiles) {
UserProperties userProperties = userManager.getUserProperties(userHandle);
if (userProperties.getShowInSettings() == UserProperties.SHOW_IN_SETTINGS_SEPARATE) {
- if (Flags.allowPrivateProfile() && userProperties.getHideInSettingsInQuietMode()) {
+ if (Flags.allowPrivateProfile()
+ && userProperties.getShowInQuietMode()
+ == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN) {
if (!userManager.isQuietModeEnabled(userHandle)) {
return true;
} else {
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index b2cdb77..3e6feb7 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -42,6 +42,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -312,7 +313,8 @@
&& !(Flags.allowPrivateProfile() && profile.isPrivateProfile())
&& (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0))
&& !(mUm.getUserProperties(profile.getUserHandle())
- .getHideInSettingsInQuietMode() && profile.isQuietModeEnabled())) {
+ .getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
+ && profile.isQuietModeEnabled())) {
updateProfileUi(profile);
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
index 9ebe26d..21f1c0e 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingBasePreferenceController.java
@@ -64,7 +64,7 @@
mPreference.setVisible(isVisible);
}
- private boolean isBroadcasting() {
+ protected boolean isBroadcasting() {
return mBroadcast != null && mBroadcast.isEnabled(null);
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
index cf5881b..7f90ceb 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java
@@ -22,6 +22,7 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
+import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.widget.SettingsMainSwitchBar;
@@ -34,6 +35,7 @@
private AudioSharingDeviceVolumeGroupController mAudioSharingDeviceVolumeGroupController;
private CallsAndAlarmsPreferenceController mCallsAndAlarmsPreferenceController;
private AudioSharingNamePreferenceController mAudioSharingNamePreferenceController;
+ private AudioStreamsCategoryController mAudioStreamsCategoryController;
public AudioSharingDashboardFragment() {
super();
@@ -73,6 +75,7 @@
mCallsAndAlarmsPreferenceController = use(CallsAndAlarmsPreferenceController.class);
mCallsAndAlarmsPreferenceController.init(this);
mAudioSharingNamePreferenceController = use(AudioSharingNamePreferenceController.class);
+ mAudioStreamsCategoryController = use(AudioStreamsCategoryController.class);
}
@Override
@@ -98,5 +101,6 @@
mAudioSharingDeviceVolumeGroupController.updateVisibility(isVisible);
mCallsAndAlarmsPreferenceController.updateVisibility(isVisible);
mAudioSharingNamePreferenceController.updateVisibility(isVisible);
+ mAudioStreamsCategoryController.updateVisibility(isVisible);
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
index 9210074..a43e098 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingUtils.java
@@ -35,6 +35,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
public class AudioSharingUtils {
@@ -230,6 +231,31 @@
return false;
}
+ /**
+ * Retrieves the one and only active Bluetooth LE Audio sink device, regardless if the device is
+ * currently in an audio sharing session.
+ *
+ * @param manager The LocalBluetoothManager instance used to fetch connected devices.
+ * @return An Optional containing the active LE Audio device, or an empty Optional if not found.
+ */
+ public static Optional<CachedBluetoothDevice> getActiveSinkOnAssistant(
+ LocalBluetoothManager manager) {
+ if (manager == null) {
+ Log.w(TAG, "getActiveSinksOnAssistant(): LocalBluetoothManager is null!");
+ return Optional.empty();
+ }
+ var groupedDevices = AudioSharingUtils.fetchConnectedDevicesByGroupId(manager);
+ var leadDevices =
+ AudioSharingUtils.buildOrderedConnectedLeadDevices(manager, groupedDevices, false);
+
+ if (!leadDevices.isEmpty() && AudioSharingUtils.isActiveLeAudioDevice(leadDevices.get(0))) {
+ return Optional.of(leadDevices.get(0));
+ } else {
+ Log.w(TAG, "getActiveSinksOnAssistant(): No active lead device!");
+ }
+ return Optional.empty();
+ }
+
/** Toast message on main thread. */
public static void toastMessage(Context context, String message) {
ThreadUtils.postOnMainThread(
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
index cf79596..0d3b1b1 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdater.java
@@ -30,8 +30,6 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.utils.ThreadUtils;
-import java.util.Optional;
-
public class AudioStreamsActiveDeviceSummaryUpdater implements BluetoothCallback {
private static final String TAG = "AudioStreamsActiveDeviceSummaryUpdater";
private static final boolean DEBUG = BluetoothUtils.D;
@@ -82,31 +80,13 @@
}
private String getSummary() {
- var activeSink = getActiveSinkOnAssistant(mBluetoothManager);
+ var activeSink = AudioSharingUtils.getActiveSinkOnAssistant(mBluetoothManager);
if (activeSink.isEmpty()) {
return "No active LE Audio device";
}
return activeSink.get().getName();
}
- private static Optional<CachedBluetoothDevice> getActiveSinkOnAssistant(
- LocalBluetoothManager manager) {
- if (manager == null) {
- Log.w(TAG, "getActiveSinksOnAssistant(): LocalBluetoothManager is null!");
- return Optional.empty();
- }
- var groupedDevices = AudioSharingUtils.fetchConnectedDevicesByGroupId(manager);
- var leadDevices =
- AudioSharingUtils.buildOrderedConnectedLeadDevices(manager, groupedDevices, false);
-
- if (!leadDevices.isEmpty() && AudioSharingUtils.isActiveLeAudioDevice(leadDevices.get(0))) {
- return Optional.of(leadDevices.get(0));
- } else {
- Log.w(TAG, "getActiveSinksOnAssistant(): No active lead device!");
- }
- return Optional.empty();
- }
-
/** Interface definition for a callback to be invoked when the summary has been changed. */
interface OnSummaryChangeListener {
/**
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
index 84a7be9..f80fdab 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsCategoryController.java
@@ -16,15 +16,64 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.settings.bluetooth.Utils;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingBasePreferenceController;
+import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils;
import com.android.settings.flags.Flags;
-import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.utils.ThreadUtils;
-public class AudioStreamsCategoryController extends PreferenceCategoryController {
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class AudioStreamsCategoryController extends AudioSharingBasePreferenceController
+ implements DefaultLifecycleObserver {
+ private static final String TAG = "AudioStreamsCategoryController";
+ private static final boolean DEBUG = BluetoothUtils.D;
+ private final LocalBluetoothManager mLocalBtManager;
+ private final Executor mExecutor;
+ private final BluetoothCallback mBluetoothCallback =
+ new BluetoothCallback() {
+ @Override
+ public void onActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ if (bluetoothProfile == BluetoothProfile.LE_AUDIO) {
+ updateVisibility(isBroadcasting());
+ }
+ }
+ };
public AudioStreamsCategoryController(Context context, String key) {
super(context, key);
+ mLocalBtManager = Utils.getLocalBtManager(mContext);
+ mExecutor = Executors.newSingleThreadExecutor();
+ }
+
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner) {
+ if (mLocalBtManager != null) {
+ mLocalBtManager.getEventManager().registerCallback(mBluetoothCallback);
+ }
+ updateVisibility(isBroadcasting());
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner) {
+ if (mLocalBtManager != null) {
+ mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
+ }
}
@Override
@@ -33,4 +82,23 @@
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
}
+
+ @Override
+ public void updateVisibility(boolean isBroadcasting) {
+ mExecutor.execute(
+ () -> {
+ boolean hasActiveLe =
+ AudioSharingUtils.getActiveSinkOnAssistant(mLocalBtManager).isPresent();
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "updateVisibility() isBroadcasting : "
+ + isBroadcasting
+ + " hasActiveLe : "
+ + hasActiveLe);
+ }
+ ThreadUtils.postOnMainThread(
+ () -> super.updateVisibility(hasActiveLe && !isBroadcasting));
+ });
+ }
}
diff --git a/src/com/android/settings/dashboard/SummaryProvider.java b/src/com/android/settings/dashboard/SummaryProvider.java
index 6acc663..cd388b9 100644
--- a/src/com/android/settings/dashboard/SummaryProvider.java
+++ b/src/com/android/settings/dashboard/SummaryProvider.java
@@ -20,15 +20,10 @@
import android.content.ContentProvider;
import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.UserInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import com.android.settings.R;
import com.android.settings.backup.BackupSettingsHelper;
/** Provide preference summary for injected items. */
@@ -44,14 +39,6 @@
bundle.putString(META_DATA_PREFERENCE_SUMMARY,
new BackupSettingsHelper(getContext()).getSummary());
break;
- case USER:
- final Context context = getContext();
- final UserInfo info = context.getSystemService(UserManager.class).getUserInfo(
- UserHandle.myUserId());
- bundle.putString(META_DATA_PREFERENCE_SUMMARY,
- context.getString(R.string.users_summary,
- info.name));
- break;
default:
throw new IllegalArgumentException("Unknown Uri format: " + uri);
}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
index 561a51a..eaad7f3 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectDialog.java
@@ -235,7 +235,7 @@
private static boolean shouldHideUserInQuietMode(
UserHandle userHandle, UserManager userManager) {
UserProperties userProperties = userManager.getUserProperties(userHandle);
- return userProperties.getHideInSettingsInQuietMode()
+ return userProperties.getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
&& userManager.isQuietModeEnabled(userHandle);
}
}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
index dd49c8b..066e0fc 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsMigrateChecker.java
@@ -51,8 +51,7 @@
context = context.getApplicationContext();
verifySaverConfiguration(context);
verifyBatteryOptimizeModes(context);
- // Initialize and sync settings into SharedPreferences for migration.
- DynamicDenylistManager.getInstance(context);
+ DynamicDenylistManager.getInstance(context).onBootComplete();
}
/** Avoid users set important apps into the unexpected battery optimize modes */
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
index 9fb70fa..bfa501c 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java
@@ -63,7 +63,7 @@
}
final String action = intent.getAction();
Log.d(TAG, "onReceive:" + action);
- if (DatabaseUtils.isWorkProfile(context)) {
+ if (com.android.settingslib.fuelgauge.BatteryUtils.isWorkProfile(context)) {
Log.w(TAG, "do nothing for work profile action=" + action);
return;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
index 4f2ee79..095a65a 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
@@ -37,6 +37,7 @@
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
+import com.android.settingslib.fuelgauge.BatteryUtils;
import java.time.Clock;
import java.time.Duration;
@@ -109,7 +110,7 @@
@Override
public boolean onCreate() {
- if (DatabaseUtils.isWorkProfile(getContext())) {
+ if (BatteryUtils.isWorkProfile(getContext())) {
Log.w(TAG, "do not create provider for work profile");
return false;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
index 94f9dc3..dd48483 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
@@ -27,6 +27,7 @@
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.fuelgauge.BatteryUtils;
import java.time.Duration;
@@ -56,7 +57,7 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent == null ? "" : intent.getAction();
- if (DatabaseUtils.isWorkProfile(context)) {
+ if (BatteryUtils.isWorkProfile(context)) {
Log.w(TAG, "do not start job for work profile action=" + action);
return;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index f96ed5b..7160da4 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -159,12 +159,6 @@
private DatabaseUtils() {}
- /** Returns true if current user is a work profile user. */
- public static boolean isWorkProfile(Context context) {
- final UserManager userManager = context.getSystemService(UserManager.class);
- return userManager.isManagedProfile();
- }
-
/** Returns the latest timestamp current user data in app usage event table. */
public static long getAppUsageStartTimestampOfUser(
Context context, final long userId, final long earliestTimestamp) {
@@ -502,7 +496,7 @@
/** Returns the context with profile parent identity when current user is work profile. */
public static Context getParentContext(Context context) {
- if (isWorkProfile(context)) {
+ if (com.android.settingslib.fuelgauge.BatteryUtils.isWorkProfile(context)) {
try {
return context.createPackageContextAsUser(
/* packageName= */ context.getPackageName(),
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
index bd77feb..5c73adb 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
@@ -24,6 +24,7 @@
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
+import com.android.settingslib.fuelgauge.BatteryUtils;
/** Receives the periodic alarm {@link PendingIntent} callback. */
public final class PeriodicJobReceiver extends BroadcastReceiver {
@@ -49,7 +50,7 @@
Log.w(TAG, "receive unexpected action=" + action);
return;
}
- if (DatabaseUtils.isWorkProfile(context)) {
+ if (BatteryUtils.isWorkProfile(context)) {
BatteryUsageLogUtils.writeLog(
context, Action.SCHEDULE_JOB, "do not refresh job for work profile");
Log.w(TAG, "do not refresh job for work profile action=" + action);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
index 7dc7700..ff953e7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
@@ -23,7 +23,7 @@
import android.net.Uri;
import android.util.Log;
-import com.android.settings.fuelgauge.batteryusage.DatabaseUtils;
+import com.android.settingslib.fuelgauge.BatteryUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -49,7 +49,7 @@
Log.w(TAG, "failed to dump BatteryUsage state: null application context");
return;
}
- if (DatabaseUtils.isWorkProfile(context)) {
+ if (BatteryUtils.isWorkProfile(context)) {
Log.w(TAG, "ignore battery usage states dump in the work profile");
return;
}
diff --git a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
index e9e1218..4a25238 100644
--- a/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
+++ b/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManager.java
@@ -23,12 +23,14 @@
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.io.PrintWriter;
import java.util.Set;
/** A class to dynamically manage per apps {@link NetworkPolicyManager} POLICY_ flags. */
@@ -137,16 +139,34 @@
return;
}
synchronized (mLock) {
- for (int uid : mNetworkPolicyManager
- .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
- if (!getDenylistAllUids(getManualDenylistPref()).contains(uid)) {
- mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE);
+ final int[] uids = mNetworkPolicyManager
+ .getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
+ if (uids != null && uids.length != 0) {
+ for (int uid : uids) {
+ if (!getDenylistAllUids(getManualDenylistPref()).contains(uid)) {
+ mNetworkPolicyManager.setUidPolicy(uid, POLICY_NONE);
+ }
}
}
}
clearSharedPreferences();
}
+ /** Reset the POLICY_REJECT_METERED uids when device is boot completed. */
+ public void onBootComplete() {
+ resetDenylistIfNeeded(/* packageName= */ null, /* force= */ true);
+ syncPolicyIfNeeded();
+ }
+
+ /** Dump the data stored in the {@link SharedPreferences}. */
+ public void dump(PrintWriter writer) {
+ writer.println("Dump of DynamicDenylistManager:");
+ writer.println("\tManualDenylist: " + getPackageNames(mContext,
+ getDenylistAllUids(getManualDenylistPref())));
+ writer.println("\tDynamicDenylist: " + getPackageNames(mContext,
+ getDenylistAllUids(getDynamicDenylistPref())));
+ }
+
private Set<Integer> getDenylistAllUids(SharedPreferences sharedPreferences) {
final ArraySet<Integer> uids = new ArraySet<>();
for (String key : sharedPreferences.getAll().keySet()) {
@@ -186,4 +206,14 @@
SharedPreferences getDynamicDenylistPref() {
return mContext.getSharedPreferences(PREF_KEY_DYNAMIC_DENY, Context.MODE_PRIVATE);
}
+
+ private static String getPackageNames(Context context, Set<Integer> uids) {
+ if (uids == null || uids.isEmpty()) {
+ return null;
+ }
+ final PackageManager pm = context.getPackageManager();
+ final StringBuilder builder = new StringBuilder();
+ uids.forEach(uid -> builder.append(pm.getNameForUid(uid) + " "));
+ return builder.toString();
+ }
}
diff --git a/src/com/android/settings/users/MultiUserPreferenceController.java b/src/com/android/settings/users/MultiUserPreferenceController.java
new file mode 100644
index 0000000..2eb57ec
--- /dev/null
+++ b/src/com/android/settings/users/MultiUserPreferenceController.java
@@ -0,0 +1,46 @@
+/*
+ * 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.users;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+
+public class MultiUserPreferenceController extends BasePreferenceController {
+
+ public MultiUserPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return (UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
+ && !Utils.isMonkeyRunning()) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ UserManager um = mContext.getSystemService(UserManager.class);
+ UserInfo info = um.getUserInfo(UserHandle.myUserId());
+ return mContext.getString(R.string.users_summary, info.name);
+ }
+}
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
index aa77dc1..010607f 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorAuthActivity.java
@@ -37,6 +37,8 @@
* Sharing a Wi-Fi network by QR code after unlocking. Used by {@code InternetDialog} in QS.
*/
public class WifiDppConfiguratorAuthActivity extends InstrumentedActivity {
+ private static final String WIFI_SHARING_KEY_ALIAS = "wifi_sharing_auth_key";
+ private static final int MAX_UNLOCK_SECONDS = 60;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -46,7 +48,9 @@
Intent authIntent = getSystemService(KeyguardManager.class)
.createConfirmDeviceCredentialIntent(
getText(R.string.wifi_dpp_lockscreen_title), null, getUserId());
- if (authIntent == null) {
+ if (authIntent == null
+ || WifiDppUtils.isUnlockedWithinSeconds(
+ WIFI_SHARING_KEY_ALIAS, MAX_UNLOCK_SECONDS)) {
startQrCodeActivity();
finish();
} else {
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 83a1571..23a6a54 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -30,6 +30,8 @@
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import com.android.settings.R;
@@ -37,9 +39,19 @@
import com.android.settingslib.wifi.AccessPoint;
import com.android.wifitrackerlib.WifiEntry;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.List;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
/**
* Here are the items shared by both WifiDppConfiguratorActivity & WifiDppEnrolleeActivity
*
@@ -97,6 +109,8 @@
private static final Duration VIBRATE_DURATION_QR_CODE_RECOGNITION = Duration.ofMillis(3);
+ private static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
+
/**
* Returns whether the device support WiFi DPP.
*/
@@ -367,6 +381,48 @@
}
/**
+ * Checks whether the device is unlocked recently.
+ *
+ * @param keyStoreAlias key
+ * @param seconds how many seconds since the device is unlocked
+ * @return whether the device is unlocked within the time
+ */
+ public static boolean isUnlockedWithinSeconds(String keyStoreAlias, int seconds) {
+ try {
+ Cipher cipher = Cipher.getInstance(AES_CBC_PKCS7_PADDING);
+ cipher.init(Cipher.ENCRYPT_MODE, generateSecretKey(keyStoreAlias, seconds));
+ cipher.doFinal();
+ return true;
+ } catch (NoSuchPaddingException
+ | IllegalBlockSizeException
+ | NoSuchAlgorithmException
+ | BadPaddingException
+ | InvalidKeyException e) {
+ return false;
+ }
+ }
+
+ private static SecretKey generateSecretKey(String keyStoreAlias, int seconds) {
+ KeyGenParameterSpec spec = new KeyGenParameterSpec
+ .Builder(keyStoreAlias, KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationParameters(
+ seconds,
+ KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG)
+ .build();
+ try {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
+ keyGenerator.init(spec);
+ return keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException
+ | InvalidAlgorithmParameterException e) {
+ return null;
+ }
+ }
+
+ /**
* Shows authentication screen to confirm credentials (pin, pattern or password) for the current
* user of the device.
*
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
index c31a2b6..d89e61b 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
@@ -92,17 +92,6 @@
}
@Test
- public void isWorkProfile_defaultValue_returnFalse() {
- assertThat(DatabaseUtils.isWorkProfile(mContext)).isFalse();
- }
-
- @Test
- public void isWorkProfile_withManagedUser_returnTrue() {
- BatteryTestUtils.setWorkProfile(mContext);
- assertThat(DatabaseUtils.isWorkProfile(mContext)).isTrue();
- }
-
- @Test
public void sendAppUsageEventData_returnsExpectedList() {
// Configures the testing AppUsageEvent data.
final List<AppUsageEvent> appUsageEventList = new ArrayList<>();
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 3202610..c29541c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/datasaver/DynamicDenylistManagerTest.java
@@ -36,6 +36,7 @@
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.NetworkPolicyManager;
import android.util.ArraySet;
@@ -48,6 +49,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -67,6 +70,8 @@
@Mock
private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
+ private PackageManager mPackageManager;
@Before
public void setUp() {
@@ -357,12 +362,66 @@
assertThat(mDynamicDenyListPref.getAll()).isEmpty();
}
+ @Test
+ public void dump_dumpExpectedResult() {
+ initDynamicDenylistManager(EMPTY_ARRAY);
+ setupPreference(mManualDenyListPref, FAKE_UID_1);
+ setupPreference(mDynamicDenyListPref, FAKE_UID_2);
+ final StringWriter stringWriter = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(stringWriter);
+ when(mPackageManager.getNameForUid(FAKE_UID_1_INT)).thenReturn("app1");
+ when(mPackageManager.getNameForUid(FAKE_UID_2_INT)).thenReturn("app2");
+
+ mDynamicDenylistManager.dump(printWriter);
+
+ final String dumpResults = stringWriter.toString();
+ assertThat(dumpResults.contains("ManualDenylist: app1")).isTrue();
+ assertThat(dumpResults.contains("DynamicDenylist: app2")).isTrue();
+ }
+
+ @Test
+ public void dump_withEmptySharedPreferences_dumpExpectedResult() {
+ initDynamicDenylistManager(EMPTY_ARRAY, EMPTY_ARRAY);
+ mDynamicDenylistManager.clearSharedPreferences();
+ final StringWriter stringWriter = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ mDynamicDenylistManager.dump(printWriter);
+
+ final String dumpResults = stringWriter.toString();
+ assertThat(dumpResults.contains("ManualDenylist: null")).isTrue();
+ assertThat(dumpResults.contains("DynamicDenylist: null")).isTrue();
+ }
+
+ @Test
+ public void onBootComplete_resetIntoManualMode() {
+ initDynamicDenylistManager(new int[] {FAKE_UID_1_INT});
+ setDenylist(new ArraySet<>(List.of(FAKE_UID_2_INT)));
+ // Ensure the testing environment for manual denylist.
+ assertThat(mManualDenyListPref.getAll()).hasSize(2);
+ assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ // Ensure the testing environment for dynamic denylist.
+ assertThat(mDynamicDenyListPref.getAll()).hasSize(1);
+
+ mDynamicDenylistManager.onBootComplete();
+
+ // Keep the users set uids in the manual denylist.
+ assertThat(mManualDenyListPref.getAll()).hasSize(2);
+ assertTrue(mManualDenyListPref.contains(PREF_KEY_MANUAL_DENYLIST_SYNCED));
+ assertTrue(mManualDenyListPref.contains(FAKE_UID_1));
+ // Clear the uids in the dynamic denylist.
+ 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);
diff --git a/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java
new file mode 100644
index 0000000..fd73eab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/users/MultiUserPreferenceControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.users;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class})
+public class MultiUserPreferenceControllerTest {
+
+ private Context mContext;
+ private ShadowUserManager mUserManager;
+ private MultiUserPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mUserManager = ShadowUserManager.getShadow();
+ mController = new MultiUserPreferenceController(mContext, "test_key");
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUserManager.reset();
+ }
+
+ @Test
+ public void getAvailabilityStatus_multiuserEnabled_shouldReturnAvailable() {
+ mUserManager.setSupportsMultipleUsers(true);
+
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_multiuserDisabled_shouldReturnNotAvailable() {
+ mUserManager.setSupportsMultipleUsers(false);
+
+ assertThat(mController.getAvailabilityStatus())
+ .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+}