diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 90b42f41cbe..7daf136a40b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2832,17 +2832,20 @@
@@ -2852,16 +2855,19 @@
@@ -2907,24 +2913,29 @@
- \n\n"Ne možete da upućujete hitne pozive pomoću pozivanja preko Wi-Fi-ja. Ako probate da uputite hitan poziv, uređaj će automatski koristiti mobilnu mrežu. Hitni pozivi mogu da se upućuju samo u oblastima pokrivenim mobilnom mrežom."
+ \n\n"Ne možete da upućujete hitne pozive pomoću pozivanja preko WiFi-a. Ako probate da uputite hitan poziv, uređaj će automatski koristiti mobilnu mrežu. Hitni pozivi mogu da se upućuju samo u oblastima pokrivenim mobilnom mrežom."
diff --git a/res/values-mcc262-mnc02-mn/strings.xml b/res/values-mcc262-mnc02-mn/strings.xml
index ac2a7a436ef..9a96f74e126 100644
--- a/res/values-mcc262-mnc02-mn/strings.xml
+++ b/res/values-mcc262-mnc02-mn/strings.xml
@@ -16,5 +16,5 @@
- \n\n"Та Wi-Fi дуудлагаар яаралтай дуудлага хийх боломжгүй. Хэрэв та яаралтай дуудлага хийвэл таны төхөөрөмж мобайл сүлжээг автоматаар ашиглана. Яаралтай дуудлагыг зөвхөн мобайл сүлжээтэй газар хийх боломжтой."
+ \n\n"Та Wi-Fi дуудлагаар яаралтай дуудлага хийх боломжгүй. Хэрэв та яаралтай дуудлага хийвэл таны төхөөрөмж хөдөлгөөнт холбооны сүлжээг автоматаар ашиглана. Яаралтай дуудлагыг зөвхөн хөдөлгөөнт холбооны сүлжээтэй газар хийх боломжтой."
diff --git a/res/values-mcc262-mnc02-nb/strings.xml b/res/values-mcc262-mnc02-nb/strings.xml
index f77da800baa..68999e87006 100644
--- a/res/values-mcc262-mnc02-nb/strings.xml
+++ b/res/values-mcc262-mnc02-nb/strings.xml
@@ -16,5 +16,5 @@
- \n\n"Du kan ikke ringe nødnumre via Wi-Fi-anrop. Hvis du prøver å ringe et nødnummer, bruker enheten automatisk mobilnettverket. Du kan bare ringe nødnumre i områder med mobildekning."
+ \n\n"Du kan ikke ringe nødnumre via wifi-anrop. Hvis du prøver å ringe et nødnummer, bruker enheten automatisk mobilnettverket. Du kan bare ringe nødnumre i områder med mobildekning."
diff --git a/res/values-mcc262-mnc02-ne/strings.xml b/res/values-mcc262-mnc02-ne/strings.xml
index a850fff5df7..c7da875d490 100644
--- a/res/values-mcc262-mnc02-ne/strings.xml
+++ b/res/values-mcc262-mnc02-ne/strings.xml
@@ -16,5 +16,5 @@
- \n\n"तपाईं Wi-Fi को कल गर्ने सुविधामार्फत आपतकालीन कलहरू गर्न सक्नुहुन्न। तपाईंले आपतकालीन कल गर्न खोज्नुभयो भने तपाईंको यन्त्रले स्वतः मोबाइल नेटवर्कको प्रयोग गर्ने छ। मोबाइल नेटवर्क उपलब्ध भएका क्षेत्रहरूबाट मात्र आपतकालीन कलहरू गर्न सकिन्छ।"
+ \n\n"तपाईं Wi-Fi को कल गर्ने सुविधामार्फत आपत्कालीन कलहरू गर्न सक्नुहुन्न। तपाईंले आपत्कालीन कल गर्न खोज्नुभयो भने तपाईंको यन्त्रले स्वतः मोबाइल नेटवर्कको प्रयोग गर्ने छ। मोबाइल नेटवर्क उपलब्ध भएका क्षेत्रहरूबाट मात्र आपत्कालीन कलहरू गर्न सकिन्छ।"
diff --git a/res/values-mcc262-mnc02-sr/strings.xml b/res/values-mcc262-mnc02-sr/strings.xml
index abb56b6880b..f20f3f4a9b1 100644
--- a/res/values-mcc262-mnc02-sr/strings.xml
+++ b/res/values-mcc262-mnc02-sr/strings.xml
@@ -16,5 +16,5 @@
- \n\n"Не можете да упућујете хитне позиве помоћу позивања преко Wi-Fi-ја. Ако пробате да упутите хитан позив, уређај ће аутоматски користити мобилну мрежу. Хитни позиви могу да се упућују само у областима покривеним мобилном мрежом."
+ \n\n"Не можете да упућујете хитне позиве помоћу позивања преко WiFi-а. Ако пробате да упутите хитан позив, уређај ће аутоматски користити мобилну мрежу. Хитни позиви могу да се упућују само у областима покривеним мобилном мрежом."
diff --git a/res/values-mcc262-mnc02-sv/strings.xml b/res/values-mcc262-mnc02-sv/strings.xml
index fea75e9e472..b37dbb8f1f9 100644
--- a/res/values-mcc262-mnc02-sv/strings.xml
+++ b/res/values-mcc262-mnc02-sv/strings.xml
@@ -16,5 +16,5 @@
- \n\n"Det går inte att ringa nödsamtal via Wi-Fi. Om du ringer ett nödnummer på enheten används mobilnätverket automatiskt. Det går bara att ringa nödsamtal där mobilnätet har täckning."
+ \n\n"Det går inte att ringa nödsamtal via wifi. Om du ringer ett nödnummer på enheten används mobilnätverket automatiskt. Det går bara att ringa nödsamtal där mobilnätet har täckning."
diff --git a/res/values-mcc262-mnc02-te/strings.xml b/res/values-mcc262-mnc02-te/strings.xml
index 9d9a240ce76..e230bc5d229 100644
--- a/res/values-mcc262-mnc02-te/strings.xml
+++ b/res/values-mcc262-mnc02-te/strings.xml
@@ -16,5 +16,5 @@
- \n\n"మీరు Wi-Fi కాలింగ్ ద్వారా అత్యవసర కాల్లను చేయలేరు. అత్యవసర కాల్ చేయడానికి ప్రయత్నిస్తే, మీ పరికరం స్వయంచాలకంగా మొబైల్ నెట్వర్క్ని ఉపయోగిస్తుంది. మొబైల్ నెట్వర్క్ కవరేజ్ ఉన్న ప్రాంతాల్లో మాత్రమే అత్యవసర కాల్లు చేయబడతాయి."
+ \n\n"మీరు Wi-Fi కాలింగ్ ద్వారా అత్యవసర కాల్స్ను చేయలేరు. అత్యవసర కాల్ చేయడానికి ప్రయత్నిస్తే, మీ పరికరం ఆటోమేటిక్గా మొబైల్ నెట్వర్క్ని ఉపయోగిస్తుంది. మొబైల్ నెట్వర్క్ కవరేజ్ ఉన్న ప్రాంతాల్లో మాత్రమే అత్యవసర కాల్స్ చేయబడతాయి."
diff --git a/res/values-mcc262-mnc02-zh-rHK/strings.xml b/res/values-mcc262-mnc02-zh-rHK/strings.xml
index b713374a760..c04326ac937 100644
--- a/res/values-mcc262-mnc02-zh-rHK/strings.xml
+++ b/res/values-mcc262-mnc02-zh-rHK/strings.xml
@@ -16,5 +16,5 @@
- \n\n"您不可透過 Wi-Fi 通話撥打緊急電話。如果您嘗試撥打緊急電話,裝置會自動使用流動網絡。您只可在有流動網絡覆蓋的地方撥打緊急電話。"
+ \n\n"你不可透過 Wi-Fi 通話撥打緊急電話。如果你嘗試撥打緊急電話,裝置會自動使用流動網絡。你只可在有流動網絡覆蓋的地方撥打緊急電話。"
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index adaea1bd921..4680936b99b 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1552,4 +1552,8 @@
- Theater
- Flower
+
+
+
+
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index db87c0facac..46461340c10 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -356,6 +356,11 @@ public class FingerprintSettings extends SubSettings {
*/
protected void handleError(int errMsgId, CharSequence msg) {
switch (errMsgId) {
+ case FingerprintManager.FINGERPRINT_ERROR_CANCELED:
+ case FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED:
+ // Only happens if we get preempted by another activity, or canceled by the
+ // user (e.g. swipe up to home). Ignored.
+ return;
case FingerprintManager.FINGERPRINT_ERROR_LOCKOUT:
mInFingerprintLockout = true;
// We've been locked out. Reset after 30s.
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
index 30e86fe9b2f..4ff71360a49 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
@@ -19,13 +19,16 @@ package com.android.settings.bluetooth;
import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
import android.media.Spatializer;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -37,9 +40,14 @@ import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -53,22 +61,27 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
private static final String KEY_HEAD_TRACKING = "head_tracking";
+ private final AudioManager mAudioManager;
private final Spatializer mSpatializer;
@VisibleForTesting
PreferenceCategory mProfilesContainer;
- @VisibleForTesting
- AudioDeviceAttributes mAudioDevice = null;
+ @VisibleForTesting @Nullable AudioDeviceAttributes mAudioDevice = null;
AtomicBoolean mHasHeadTracker = new AtomicBoolean(false);
AtomicBoolean mInitialRefresh = new AtomicBoolean(true);
+ public static final Set SA_PROFILES =
+ ImmutableSet.of(
+ BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID);
+
public BluetoothDetailsSpatialAudioController(
Context context,
PreferenceFragmentCompat fragment,
CachedBluetoothDevice device,
Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
+ mAudioManager = context.getSystemService(AudioManager.class);
mSpatializer = FeatureFactory.getFeatureFactory().getBluetoothFeatureProvider()
.getSpatializer(context);
}
@@ -142,8 +155,12 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
@Override
protected void refresh() {
- if (mAudioDevice == null) {
- getAvailableDevice();
+ if (Flags.enableDeterminingSpatialAudioAttributesByProfile()) {
+ getAvailableDeviceByProfileState();
+ } else {
+ if (mAudioDevice == null) {
+ getAvailableDevice();
+ }
}
ThreadUtils.postOnBackgroundThread(
() -> {
@@ -274,6 +291,77 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
+ ", type : " + (mAudioDevice == null ? "no type" : mAudioDevice.getType()));
}
+ private void getAvailableDeviceByProfileState() {
+ Log.i(
+ TAG,
+ "getAvailableDevice() mCachedDevice: "
+ + mCachedDevice
+ + " profiles: "
+ + mCachedDevice.getProfiles());
+
+ AudioDeviceAttributes saDevice = null;
+ for (LocalBluetoothProfile profile : mCachedDevice.getProfiles()) {
+ // pick first enabled profile that is compatible with spatial audio
+ if (SA_PROFILES.contains(profile.getProfileId())
+ && profile.isEnabled(mCachedDevice.getDevice())) {
+ switch (profile.getProfileId()) {
+ case BluetoothProfile.A2DP:
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ mCachedDevice.getAddress());
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ if (mAudioManager.getBluetoothAudioDeviceCategory(
+ mCachedDevice.getAddress())
+ == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ mCachedDevice.getAddress());
+ } else {
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ mCachedDevice.getAddress());
+ }
+
+ break;
+ case BluetoothProfile.HEARING_AID:
+ saDevice =
+ new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ mCachedDevice.getAddress());
+ break;
+ default:
+ Log.i(
+ TAG,
+ "unrecognized profile for spatial audio: "
+ + profile.getProfileId());
+ break;
+ }
+ break;
+ }
+ }
+ mAudioDevice = null;
+ if (saDevice != null && mSpatializer.isAvailableForDevice(saDevice)) {
+ mAudioDevice = saDevice;
+ }
+
+ Log.d(
+ TAG,
+ "getAvailableDevice() device : "
+ + mCachedDevice.getDevice().getAnonymizedAddress()
+ + ", is available : "
+ + (mAudioDevice != null)
+ + ", type : "
+ + (mAudioDevice == null ? "no type" : mAudioDevice.getType()));
+ }
+
@VisibleForTesting
void setAvailableDevice(AudioDeviceAttributes audioDevice) {
mAudioDevice = audioDevice;
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index f63b64993fb..570a863dd4d 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -23,11 +23,16 @@ import android.os.Bundle;
import android.util.ArrayMap;
import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+
+import com.android.settings.fuelgauge.batteryusage.BatteryDiffData;
+import com.android.settings.fuelgauge.batteryusage.BatteryEvent;
import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settingslib.fuelgauge.Estimate;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/** Feature Provider used in power usage */
@@ -154,4 +159,14 @@ public interface PowerUsageFeatureProvider {
/** Whether the device is under the battery defender mode */
boolean isBatteryDefend(BatteryInfo info);
+
+ /** Whether the battery usage reattribute is eabled or not. */
+ boolean isBatteryUsageReattributeEnabled();
+
+ /** Collect and process battery reattribute data if needed. */
+ boolean processBatteryReattributeData(
+ @NonNull Context context,
+ @NonNull Map batteryDiffDataMap,
+ @NonNull List batteryEventList,
+ final boolean isFromPeriodJob);
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index cc6659a5be5..12262677939 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -18,7 +18,6 @@ package com.android.settings.fuelgauge;
import static com.android.settings.Utils.SYSTEMUI_PACKAGE_NAME;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -27,13 +26,19 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.util.ArrayUtils;
+import com.android.settings.fuelgauge.batteryusage.BatteryDiffData;
+import com.android.settings.fuelgauge.batteryusage.BatteryEvent;
import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settingslib.fuelgauge.Estimate;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/** Implementation of {@code PowerUsageFeatureProvider} */
@@ -240,4 +245,18 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
public boolean isBatteryDefend(BatteryInfo info) {
return info.isBatteryDefender && !isExtraDefend();
}
+
+ @Override
+ public boolean isBatteryUsageReattributeEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean processBatteryReattributeData(
+ @NonNull Context context,
+ @NonNull Map batteryDiffDataMap,
+ @NonNull List batteryEventList,
+ final boolean isFromPeriodJob) {
+ return false;
+ }
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
index 7ea7203a332..b5d50994ed7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
@@ -77,11 +77,13 @@ public class BatteryDiffData {
processAndSortEntries(mSystemEntries);
}
- long getStartTimestamp() {
+ /** Gets the start timestamp. */
+ public long getStartTimestamp() {
return mStartTimestamp;
}
- long getEndTimestamp() {
+ /** Gets the end timestamp. */
+ public long getEndTimestamp() {
return mEndTimestamp;
}
@@ -97,7 +99,8 @@ public class BatteryDiffData {
return mScreenOnTime;
}
- List getAppDiffEntryList() {
+ /** Gets the {@link BatteryDiffEntry} list for apps. */
+ public List getAppDiffEntryList() {
return mAppEntries;
}
@@ -296,8 +299,7 @@ public class BatteryDiffData {
* Sets total consume power, and adjusts the percentages to ensure the total round percentage
* could be 100%, and then sorts entries based on the sorting key.
*/
- @VisibleForTesting
- static void processAndSortEntries(final List batteryDiffEntries) {
+ public static void processAndSortEntries(final List batteryDiffEntries) {
if (batteryDiffEntries.isEmpty()) {
return;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index 1c6ff54f818..8f4d4dd3bc8 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -201,6 +201,20 @@ public final class ConvertUtils {
return defaultInstance;
}
+ /** Gets the encoded string from {@link BatteryReattribute} instance. */
+ @NonNull
+ public static String encodeBatteryReattribute(
+ @NonNull BatteryReattribute batteryReattribute) {
+ return Base64.encodeToString(batteryReattribute.toByteArray(), Base64.DEFAULT);
+ }
+
+ /** Gets the decoded {@link BatteryReattribute} instance from string. */
+ @NonNull
+ public static BatteryReattribute decodeBatteryReattribute(@NonNull String content) {
+ return BatteryUtils.parseProtoFromString(
+ content, BatteryReattribute.getDefaultInstance());
+ }
+
/** Converts to {@link BatteryHistEntry} */
public static BatteryHistEntry convertToBatteryHistEntry(
BatteryEntry entry, BatteryUsageStats batteryUsageStats) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index 719d3bd5362..0a1a547ecc2 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -28,6 +28,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
import java.util.ArrayList;
import java.util.Calendar;
@@ -78,6 +80,7 @@ public class DataProcessManager {
// Raw start timestamp with round to the nearest hour.
private final long mRawStartTimestamp;
private final long mLastFullChargeTimestamp;
+ private final boolean mIsFromPeriodJob;
private final Context mContext;
private final Handler mHandler;
private final UserIdsSeries mUserIdsSeries;
@@ -122,6 +125,7 @@ public class DataProcessManager {
Context context,
Handler handler,
final UserIdsSeries userIdsSeries,
+ final boolean isFromPeriodJob,
final long rawStartTimestamp,
final long lastFullChargeTimestamp,
@NonNull final OnBatteryDiffDataMapLoadedListener callbackFunction,
@@ -130,6 +134,7 @@ public class DataProcessManager {
mContext = context.getApplicationContext();
mHandler = handler;
mUserIdsSeries = userIdsSeries;
+ mIsFromPeriodJob = isFromPeriodJob;
mRawStartTimestamp = rawStartTimestamp;
mLastFullChargeTimestamp = lastFullChargeTimestamp;
mCallbackFunction = callbackFunction;
@@ -147,6 +152,7 @@ public class DataProcessManager {
mHandler = handler;
mUserIdsSeries = userIdsSeries;
mCallbackFunction = callbackFunction;
+ mIsFromPeriodJob = false;
mRawStartTimestamp = 0L;
mLastFullChargeTimestamp = 0L;
mHourlyBatteryLevelsPerDay = null;
@@ -158,14 +164,9 @@ public class DataProcessManager {
/** Starts the async tasks to load battery history data and app usage data. */
public void start() {
- start(/* isFromPeriodJob= */ false);
- }
-
- /** Starts the async tasks to load battery history data and app usage data. */
- public void start(boolean isFromPeriodJob) {
// If we have battery level data, load the battery history map and app usage simultaneously.
if (mHourlyBatteryLevelsPerDay != null) {
- if (isFromPeriodJob) {
+ if (mIsFromPeriodJob) {
mIsCurrentBatteryHistoryLoaded = true;
mIsCurrentAppUsageLoaded = true;
mIsBatteryUsageSlotLoaded = true;
@@ -514,6 +515,14 @@ public class DataProcessManager {
mAppUsagePeriodMap,
getSystemAppsPackageNames(),
getSystemAppsUids()));
+ // Process the reattributate data for the following two cases:
+ // 1) the latest slot for the timestamp "until now"
+ // 2) walkthrough all BatteryDiffData again to handle "re-compute" case
+ final PowerUsageFeatureProvider featureProvider =
+ FeatureFactory.getFeatureFactory()
+ .getPowerUsageFeatureProvider();
+ featureProvider.processBatteryReattributeData(
+ mContext, batteryDiffDataMap, mBatteryEventList, mIsFromPeriodJob);
Log.d(
TAG,
@@ -683,12 +692,13 @@ public class DataProcessManager {
context,
handler,
userIdsSeries,
+ isFromPeriodJob,
startTimestamp,
lastFullChargeTime,
onBatteryDiffDataMapLoadedListener,
batteryLevelData.getHourlyBatteryLevelsPerDay(),
processedBatteryHistoryMap)
- .start(isFromPeriodJob);
+ .start();
return batteryLevelData;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index 0bb6286171c..dcdd0607107 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -429,6 +429,7 @@ public final class DatabaseUtils {
database.batteryEventDao().clearAll();
database.batteryStateDao().clearAll();
database.batteryUsageSlotDao().clearAll();
+ database.batteryReattributeDao().clearAll();
} catch (RuntimeException e) {
Log.e(TAG, "clearAll() failed", e);
}
@@ -466,6 +467,7 @@ public final class DatabaseUtils {
database.batteryEventDao().clearAllBefore(earliestTimestamp);
database.batteryStateDao().clearAllBefore(earliestTimestamp);
database.batteryUsageSlotDao().clearAllBefore(earliestTimestamp);
+ database.batteryReattributeDao().clearAllBefore(earliestTimestamp);
} catch (RuntimeException e) {
Log.e(TAG, "clearAllBefore() failed", e);
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
index 7613d9ab097..7e759ee3f26 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java
@@ -55,6 +55,7 @@ public final class BugReportContentProvider extends ContentProvider {
}
writer.println("dump BatteryUsage and AppUsage states:");
LogUtils.dumpAppOptimizationModeEventHist(context, writer);
+ LogUtils.dumpBatteryReattributeDatabaseHist(context, writer);
LogUtils.dumpBatteryUsageDatabaseHist(context, writer);
LogUtils.dumpAppUsageDatabaseHist(context, writer);
LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
index b2300308fd4..45871a35b92 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
@@ -19,6 +19,8 @@ package com.android.settings.fuelgauge.batteryusage.bugreport;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils;
import com.android.settings.fuelgauge.batteryusage.AppOptimizationModeEvent;
@@ -29,11 +31,14 @@ import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeDao;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
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.settings.overlay.FeatureFactory;
import java.io.PrintWriter;
import java.time.Clock;
@@ -127,6 +132,33 @@ public final class LogUtils {
dumpListItems(writer, entities, entity -> entity);
}
+ static void dumpBatteryReattributeDatabaseHist(Context context, PrintWriter writer) {
+ try {
+ dumpBatteryReattributeDatabaseHist(
+ BatteryStateDatabase.getInstance(context).batteryReattributeDao(),
+ writer);
+ } catch (Exception e) {
+ Log.e(TAG, "failed to run dumpBatteryReattributeDatabaseHist()", e);
+ }
+ }
+
+ @VisibleForTesting
+ static void dumpBatteryReattributeDatabaseHist(
+ BatteryReattributeDao batteryReattributeDao, PrintWriter writer) {
+ if (!FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider()
+ .isBatteryUsageReattributeEnabled()) {
+ writer.println("\n\tBatteryReattribute is disabled!");
+ return;
+ }
+ writer.println("\n\tBatteryReattribute DatabaseHistory:");
+ final List entities =
+ batteryReattributeDao.getAllAfter(
+ Clock.systemUTC().millis() - DUMP_TIME_OFFSET.toMillis());
+ if (entities != null && !entities.isEmpty()) {
+ dumpListItems(writer, entities, entity -> entity);
+ }
+ }
+
private static void dumpListItems(
PrintWriter writer, List itemList, Function itemConverter) {
final AtomicInteger counter = new AtomicInteger(0);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDao.kt b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDao.kt
new file mode 100644
index 00000000000..79c9d0039a5
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDao.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.OnConflictStrategy;
+import androidx.room.Query;
+
+import java.util.List;
+
+/** DAO for accessing {@link BatteryReattributeEntity} in the database. */
+@Dao
+public interface BatteryReattributeDao {
+
+ /** Inserts a {@link BatteryReattributeEntity} data into the database. */
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insert(event: BatteryReattributeEntity)
+
+ /** Gets all recorded data after a specific timestamp. */
+ @Query(
+ "SELECT * FROM BatteryReattributeEntity WHERE "
+ + "timestampStart >= :timestampStart ORDER BY timestampStart DESC")
+ fun getAllAfter(timestampStart: Long): List
+
+ /** Deletes all recorded data before a specific timestamp. */
+ @Query("DELETE FROM BatteryReattributeEntity WHERE timestampStart <= :timestampStart")
+ fun clearAllBefore(timestampStart: Long)
+
+ /** Deletes all recorded data after a specific timestamp. */
+ @Query("DELETE FROM BatteryReattributeEntity WHERE timestampStart >= :timestampStart")
+ fun clearAllAfter(timestampStart: Long)
+
+ /** Clears all recorded data in the database. */
+ @Query("DELETE FROM BatteryReattributeEntity") fun clearAll()
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java
new file mode 100644
index 00000000000..1a0c0872d24
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTimeForLogging;
+
+import com.android.settings.fuelgauge.batteryusage.BatteryReattribute;
+import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+/** A {@link Entity} for battery usage reattribution data in the database. */
+@Entity
+public class BatteryReattributeEntity {
+
+ /** The start timestamp of this record data. */
+ @PrimaryKey
+ public final long timestampStart;
+
+ /** The end timestamp of this record data. */
+ public final long timestampEnd;
+
+ /** The battery usage reattribution data for corresponding uids. */
+ public final String reattributeData;
+
+ public BatteryReattributeEntity(@NonNull BatteryReattribute batteryReattribute) {
+ this(
+ batteryReattribute.getTimestampStart(),
+ batteryReattribute.getTimestampEnd(),
+ ConvertUtils.encodeBatteryReattribute(batteryReattribute));
+ }
+
+ @VisibleForTesting
+ BatteryReattributeEntity(
+ long timestampStart, long timestampEnd, @NonNull String reattributeData) {
+ this.timestampStart = timestampStart;
+ this.timestampEnd = timestampEnd;
+ this.reattributeData = reattributeData;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ final BatteryReattribute batteryReattribute =
+ ConvertUtils.decodeBatteryReattribute(reattributeData);
+ final StringBuilder builder = new StringBuilder()
+ .append("\nBatteryReattributeEntity{")
+ .append("\n\t" + utcToLocalTimeForLogging(timestampStart))
+ .append("\n\t" + utcToLocalTimeForLogging(timestampEnd))
+ .append("\n\t" + batteryReattribute);
+ if (batteryReattribute != null) {
+ builder.append("\n\t" + batteryReattribute.getReattributeDataMap());
+ }
+ return builder.append("\n}").toString();
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
index 7504775d74e..8e3d6e37e82 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
@@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batteryusage.db;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
@@ -29,11 +30,13 @@ import androidx.room.RoomDatabase;
AppUsageEventEntity.class,
BatteryEventEntity.class,
BatteryState.class,
- BatteryUsageSlotEntity.class
+ BatteryUsageSlotEntity.class,
+ BatteryReattributeEntity.class
},
- version = 1)
+ version = 2)
public abstract class BatteryStateDatabase extends RoomDatabase {
private static final String TAG = "BatteryStateDatabase";
+ private static final String DB_FILE_NAME = "battery-usage-db-v10";
private static BatteryStateDatabase sBatteryStateDatabase;
@@ -49,11 +52,15 @@ public abstract class BatteryStateDatabase extends RoomDatabase {
/** Provides DAO for battery usage slot table. */
public abstract BatteryUsageSlotDao batteryUsageSlotDao();
+ /** Provides DAO for battery reattribution table. */
+ @NonNull
+ public abstract BatteryReattributeDao batteryReattributeDao();
+
/** Gets or creates an instance of {@link RoomDatabase}. */
public static BatteryStateDatabase getInstance(Context context) {
if (sBatteryStateDatabase == null) {
sBatteryStateDatabase =
- Room.databaseBuilder(context, BatteryStateDatabase.class, "battery-usage-db-v9")
+ Room.databaseBuilder(context, BatteryStateDatabase.class, DB_FILE_NAME)
// Allows accessing data in the main thread for dumping bugreport.
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
diff --git a/src/com/android/settings/fuelgauge/protos/battery_reattribute.proto b/src/com/android/settings/fuelgauge/protos/battery_reattribute.proto
new file mode 100644
index 00000000000..8185a22c68d
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/protos/battery_reattribute.proto
@@ -0,0 +1,13 @@
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "com.android.settings.fuelgauge.batteryusage";
+option java_outer_classname = "BatteryReaatributeProto";
+
+// Battery usage reattribute data for a specific timestamp slot.
+message BatteryReattribute {
+ optional int64 timestamp_start = 1;
+ optional int64 timestamp_end = 2;
+ // Battery reattribute data for uid and its corresponding ratio.
+ map reattribute_data = 3;
+}
diff --git a/src/com/android/settings/spa/app/specialaccess/DisplayOverOtherApps.kt b/src/com/android/settings/spa/app/specialaccess/DisplayOverOtherApps.kt
index 904f0afd3d6..55e842fbc06 100644
--- a/src/com/android/settings/spa/app/specialaccess/DisplayOverOtherApps.kt
+++ b/src/com/android/settings/spa/app/specialaccess/DisplayOverOtherApps.kt
@@ -44,6 +44,16 @@ class DisplayOverOtherAppsListModel(context: Context) : AppOpPermissionListModel
logPermissionChange(newAllowed)
}
+ // TODO (b/349195999)
+ override fun isChangeable(record: AppOpPermissionRecord): Boolean {
+ if (record.app.packageName in
+ context.resources.getStringArray(R.array.display_over_apps_permission_change_exempt)
+ && record.app.isSystemApp()) {
+ return false
+ }
+ return super.isChangeable(record)
+ }
+
private fun logPermissionChange(newAllowed: Boolean) {
val category = when {
newAllowed -> SettingsEnums.APP_SPECIAL_PERMISSION_APPDRAW_ALLOW
diff --git a/src/com/android/settings/system/DeviceDiagnosticsPreferenceController.kt b/src/com/android/settings/system/DeviceDiagnosticsPreferenceController.kt
index 695bb5bdea9..d68380446d4 100644
--- a/src/com/android/settings/system/DeviceDiagnosticsPreferenceController.kt
+++ b/src/com/android/settings/system/DeviceDiagnosticsPreferenceController.kt
@@ -26,7 +26,7 @@ import com.android.settings.R
import com.android.settings.core.BasePreferenceController
import com.android.settings.flags.Flags
-class DeviceDiagnosticsPreferenceController(context: Context, preferenceKey: String) :
+open class DeviceDiagnosticsPreferenceController(context: Context, preferenceKey: String) :
BasePreferenceController(context, preferenceKey) {
override fun getAvailabilityStatus(): Int {
@@ -59,6 +59,12 @@ class DeviceDiagnosticsPreferenceController(context: Context, preferenceKey: Str
val packageName = mContext.getResources().getString(
R.string.config_device_diagnostics_package_name)
intent.setPackage(packageName)
+
+ val info = mContext.getPackageManager().resolveActivity(intent, 0)
+ if (info == null) {
+ return null
+ }
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
return intent
}
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
index 6407f648acb..58e7e2d4003 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
@@ -17,7 +17,6 @@
package com.android.settings.biometrics.fingerprint;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
-import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment;
@@ -34,16 +33,13 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
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.Intent;
import android.content.pm.UserInfo;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
-import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -84,7 +80,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
-import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSettingsPreferenceFragment.class, ShadowUtils.class, ShadowFragment.class,
@@ -152,6 +147,7 @@ public class FingerprintSettingsFragmentTest {
public void testCancellationSignalLifeCycle() {
setUpFragment(false);
+ mFingerprintAuthenticateSidecar.setFingerprintManager(mFingerprintManager);
doNothing().when(mFingerprintManager).authenticate(any(),
mCancellationSignalArgumentCaptor.capture(),
@@ -217,7 +213,6 @@ public class FingerprintSettingsFragmentTest {
doReturn(fragmentManager).when(mActivity).getSupportFragmentManager();
mFingerprintAuthenticateSidecar = new FingerprintAuthenticateSidecar();
- mFingerprintAuthenticateSidecar.setFingerprintManager(mFingerprintManager);
doReturn(mFingerprintAuthenticateSidecar).when(fragmentManager).findFragmentByTag(
"authenticate_sidecar");
@@ -251,27 +246,4 @@ public class FingerprintSettingsFragmentTest {
true /* resetLockoutRequiresHardwareAuthToken */));
doReturn(props).when(mFingerprintManager).getSensorPropertiesInternal();
}
-
- @Test
- public void testAuthOnFragmentSetup() {
- doReturn(List.of(new Fingerprint("Finger 1", 1, 2, 3)))
- .when(mFingerprintManager).getEnrolledFingerprints(anyInt());
- setUpFragment(false, 1, TYPE_REAR);
-
- verify(mFingerprintManager).authenticate(any(), any(),
- any(), any(), anyInt());
- }
-
- @Test
- public void testErrorCancelledRestartsAuth() {
- doReturn(List.of(new Fingerprint("Finger 1", 1, 2, 3)))
- .when(mFingerprintManager).getEnrolledFingerprints(anyInt());
- setUpFragment(false, 1, TYPE_REAR);
-
- // When we receive a cancel, we should restart auth.
- mFragment.handleError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, "blah");
-
- verify(mFingerprintManager, times(2)).authenticate(any(), any(),
- any(), any(), anyInt());
- }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
index d9a917b0cdd..24528ae4dcc 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
@@ -21,26 +21,35 @@ import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.Spatializer;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference;
import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.flags.Flags;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -54,7 +63,8 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetailsControllerTestBase {
-
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
private static final String KEY_HEAD_TRACKING = "head_tracking";
@@ -64,6 +74,9 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
@Mock private Lifecycle mSpatialAudioLifecycle;
@Mock private PreferenceCategory mProfilesContainer;
@Mock private BluetoothDevice mBluetoothDevice;
+ @Mock private A2dpProfile mA2dpProfile;
+ @Mock private LeAudioProfile mLeAudioProfile;
+ @Mock private HearingAidProfile mHearingAidProfile;
private AudioDeviceAttributes mAvailableDevice;
@@ -83,6 +96,12 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
when(mAudioManager.getSpatializer()).thenReturn(mSpatializer);
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedDevice.getProfiles())
+ .thenReturn(List.of(mA2dpProfile, mLeAudioProfile, mHearingAidProfile));
+ when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+ when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP);
+ when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+ when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
when(mFeatureFactory.getBluetoothFeatureProvider().getSpatializer(mContext))
.thenReturn(mSpatializer);
@@ -272,6 +291,52 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
false);
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+ public void refresh_leAudioProfileEnabledForHeadset_useLeAudioHeadsetAttributes() {
+ when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+ when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+ when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+ when(mAudioManager.getBluetoothAudioDeviceCategory(MAC_ADDRESS))
+ .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES);
+ when(mSpatializer.isAvailableForDevice(any())).thenReturn(true);
+
+ mController.refresh();
+ ShadowLooper.idleMainLooper();
+
+ assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_BLE_HEADSET);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+ public void refresh_leAudioProfileEnabledForSpeaker_useLeAudioSpeakerAttributes() {
+ when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+ when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+ when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+ when(mAudioManager.getBluetoothAudioDeviceCategory(MAC_ADDRESS))
+ .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER);
+ when(mSpatializer.isAvailableForDevice(any())).thenReturn(true);
+
+ mController.refresh();
+ ShadowLooper.idleMainLooper();
+
+ assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_BLE_SPEAKER);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE)
+ public void refresh_hearingAidProfileEnabled_useHearingAidAttributes() {
+ when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+ when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false);
+ when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+ when(mSpatializer.isAvailableForDevice(any())).thenReturn(true);
+
+ mController.refresh();
+ ShadowLooper.idleMainLooper();
+
+ assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID);
+ }
+
@Test
public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() {
mController.setAvailableDevice(mAvailableDevice);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index b5cb4462aec..a3b35be60e6 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -703,4 +703,26 @@ public final class ConvertUtilsTest {
/* taskRootPackageName= */ ""))
.isEqualTo(packageName);
}
+
+ @Test
+ public void decodeBatteryReattribute_returnExpectedResult() {
+ final BatteryReattribute batteryReattribute =
+ BatteryReattribute.newBuilder()
+ .setTimestampStart(100L)
+ .setTimestampEnd(200L)
+ .putReattributeData(1001, 0.2f)
+ .putReattributeData(2001, 0.8f)
+ .build();
+
+ final BatteryReattribute decodeResult = ConvertUtils.decodeBatteryReattribute(
+ ConvertUtils.encodeBatteryReattribute(batteryReattribute));
+
+ assertThat(decodeResult.getTimestampStart()).isEqualTo(100L);
+ assertThat(decodeResult.getTimestampEnd()).isEqualTo(200L);
+ final Map reattributeDataMap = decodeResult.getReattributeDataMap();
+ // Verify the reattribute data in the map.
+ assertThat(reattributeDataMap).hasSize(2);
+ assertThat(reattributeDataMap.get(1001)).isEqualTo(0.2f);
+ assertThat(reattributeDataMap.get(2001)).isEqualTo(0.8f);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
index 60428014048..2f20b425788 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -112,6 +112,7 @@ public final class DataProcessManagerTest {
mContext,
/* handler= */ null,
mUserIdsSeries,
+ /* isFromPeriodJob= */ false,
/* rawStartTimestamp= */ 0L,
/* lastFullChargeTimestamp= */ 0L,
/* callbackFunction= */ null,
@@ -258,6 +259,7 @@ public final class DataProcessManagerTest {
mContext,
/* handler= */ null,
mUserIdsSeries,
+ /* isFromPeriodJob= */ false,
/* rawStartTimestamp= */ 2L,
/* lastFullChargeTimestamp= */ 1L,
/* callbackFunction= */ null,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtilsTest.java
new file mode 100644
index 00000000000..9b459c57131
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtilsTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.bugreport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.fuelgauge.batteryusage.BatteryReattribute;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeDao;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeEntity;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
+import com.android.settings.testutils.BatteryTestUtils;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(RobolectricTestRunner.class)
+public final class LogUtilsTest {
+
+ private StringWriter mTestStringWriter;
+ private PrintWriter mTestPrintWriter;
+ private Context mContext;
+ private BatteryStateDatabase mDatabase;
+ private BatteryReattributeDao mBatteryReattributeDao;
+ private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mTestStringWriter = new StringWriter();
+ mTestPrintWriter = new PrintWriter(mTestStringWriter);
+ mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
+ mBatteryReattributeDao = mDatabase.batteryReattributeDao();
+ mPowerUsageFeatureProvider = FakeFeatureFactory.setupForTest().powerUsageFeatureProvider;
+ when(mPowerUsageFeatureProvider.isBatteryUsageReattributeEnabled()).thenReturn(true);
+ }
+
+ @After
+ public void cleanUp() {
+ mBatteryReattributeDao.clearAll();
+ }
+
+ @Test
+ public void dumpBatteryReattributeDatabaseHist_noData_printExpectedResult() {
+ LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter);
+
+ assertThat(mTestStringWriter.toString())
+ .contains("BatteryReattribute DatabaseHistory:");
+ }
+
+ @Test
+ public void dumpBatteryReattributeDatabaseHist_printExpectedResult() {
+ final long currentTimeMillis = System.currentTimeMillis();
+ // Insert the first testing data.
+ final BatteryReattribute batteryReattribute1 =
+ BatteryReattribute.newBuilder()
+ .setTimestampStart(currentTimeMillis - 20000)
+ .setTimestampEnd(currentTimeMillis - 10000)
+ .putReattributeData(1001, 0.1f)
+ .putReattributeData(1002, 0.99f)
+ .build();
+ mBatteryReattributeDao.insert(new BatteryReattributeEntity(batteryReattribute1));
+ // Insert the second testing data.
+ final BatteryReattribute batteryReattribute2 =
+ BatteryReattribute.newBuilder()
+ .setTimestampStart(currentTimeMillis - 40000)
+ .setTimestampEnd(currentTimeMillis - 20000)
+ .putReattributeData(1003, 1f)
+ .build();
+ mBatteryReattributeDao.insert(new BatteryReattributeEntity(batteryReattribute2));
+
+ LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter);
+
+ final String result = mTestStringWriter.toString();
+ assertThat(result).contains("BatteryReattribute DatabaseHistory:");
+ assertThat(result).contains(batteryReattribute1.toString());
+ assertThat(result).contains(batteryReattribute2.toString());
+ }
+
+ @Test
+ public void dumpBatteryReattributeDatabaseHist_featureDisable_notPrintData() {
+ mBatteryReattributeDao.insert(new BatteryReattributeEntity(
+ BatteryReattribute.getDefaultInstance()));
+ when(mPowerUsageFeatureProvider.isBatteryUsageReattributeEnabled()).thenReturn(false);
+
+ LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter);
+
+ final String result = mTestStringWriter.toString();
+ assertThat(result).contains("BatteryReattribute is disabled!");
+ assertThat(result.contains("BatteryReattribute DatabaseHistory:")).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDaoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDaoTest.java
new file mode 100644
index 00000000000..8cb0e120a3f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeDaoTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.BatteryTestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+/** Tests for {@link BatteryReattributeDao}. */
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryReattributeDaoTest {
+
+ private Context mContext;
+ private BatteryStateDatabase mDatabase;
+ private BatteryReattributeDao mBatteryReattributeDao;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
+ mBatteryReattributeDao = mDatabase.batteryReattributeDao();
+ insert(100L, 200L, "reattributeData1");
+ insert(300L, 400L, "reattributeData3");
+ insert(200L, 300L, "reattributeData2");
+ insert(400L, 500L, "reattributeData4");
+ // Ensure there was data inserted into the database.
+ assertThat(getAllEntityData()).isNotEmpty();
+ }
+
+ @Test
+ public void getAllAfter_returnExpectedEntityData() {
+ final List entityDataList =
+ mBatteryReattributeDao.getAllAfter(/* timestampStart= */ 300L);
+
+ assertThat(entityDataList).hasSize(2);
+ assertEntity(entityDataList.get(0), 400L, 500L, "reattributeData4");
+ assertEntity(entityDataList.get(1), 300L, 400L, "reattributeData3");
+ }
+
+ @Test
+ public void clearAll_clearAllData() {
+ mBatteryReattributeDao.clearAll();
+
+ assertThat(getAllEntityData()).isEmpty();
+ }
+
+ @Test
+ public void clearAllBefore_clearAllExpectedData() {
+ mBatteryReattributeDao.clearAllBefore(/* timestampStart= */ 300L);
+
+ final List entityDataList = getAllEntityData();
+ assertThat(entityDataList).hasSize(1);
+ assertEntity(entityDataList.get(0), 400L, 500L, "reattributeData4");
+ }
+
+ @Test
+ public void clearAllAfter_clearAllExpectedData() {
+ mBatteryReattributeDao.clearAllAfter(/* timestampStart= */ 300L);
+
+ final List entityDataList = getAllEntityData();
+ assertThat(entityDataList).hasSize(2);
+ assertEntity(entityDataList.get(0), 200L, 300L, "reattributeData2");
+ assertEntity(entityDataList.get(1), 100L, 200L, "reattributeData1");
+ }
+
+ @Test
+ public void insert_samePrimaryKeyEntityData_replaceIntoNewEntityData() {
+ // Verify the original data before update.
+ assertEntity(getAllEntityData().get(0), 400L, 500L, "reattributeData4");
+
+ insert(400L, 600L, "reattribute4Update");
+
+ // Verify the new update entity data.
+ assertEntity(getAllEntityData().get(0), 400L, 600L, "reattribute4Update");
+ }
+
+ private void insert(long timestampStart, long timestampEnd, String reattributeData) {
+ mBatteryReattributeDao.insert(
+ new BatteryReattributeEntity(
+ timestampStart, timestampEnd, reattributeData));
+ }
+
+ private List getAllEntityData() {
+ return mBatteryReattributeDao.getAllAfter(/* timestampStart= */ 0L);
+ }
+
+ private static void assertEntity(BatteryReattributeEntity entity, long timestampStart,
+ long timestampEnd, String reattributeData) {
+ assertThat(entity.timestampStart).isEqualTo(timestampStart);
+ assertThat(entity.timestampEnd).isEqualTo(timestampEnd);
+ assertThat(entity.reattributeData).isEqualTo(reattributeData);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntityTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntityTest.java
new file mode 100644
index 00000000000..04912aacbdb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntityTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage.db;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.fuelgauge.batteryusage.BatteryReattribute;
+import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link BatteryReattributeEntity}. */
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryReattributeEntityTest {
+
+ @Test
+ public void constructor_createExpectedData() {
+ final BatteryReattribute batteryReattribute =
+ BatteryReattribute.newBuilder()
+ .setTimestampStart(100L)
+ .setTimestampEnd(200L)
+ .putReattributeData(1001, 0.2f)
+ .putReattributeData(2001, 0.8f)
+ .build();
+
+ final BatteryReattributeEntity batteryReattributeEntity =
+ new BatteryReattributeEntity(batteryReattribute);
+
+ assertThat(batteryReattributeEntity.timestampStart)
+ .isEqualTo(batteryReattribute.getTimestampStart());
+ assertThat(batteryReattributeEntity.timestampEnd)
+ .isEqualTo(batteryReattribute.getTimestampEnd());
+ // Verify the BatteryReattribute data.
+ final BatteryReattribute decodeResult =
+ ConvertUtils.decodeBatteryReattribute(batteryReattributeEntity.reattributeData);
+ assertThat(decodeResult).isEqualTo(batteryReattribute);
+ }
+}