Merge "Fix failure in BiometricsSafetySourceTest" into main
diff --git a/res/drawable/ic_android_satellite_24px.xml b/res/drawable/ic_android_satellite_24px.xml
new file mode 100644
index 0000000..15f2884
--- /dev/null
+++ b/res/drawable/ic_android_satellite_24px.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M487,600L392,505L346,551L376,580Q399,603 399,637Q399,671 376,694L333,737Q310,760 276.5,760Q243,760 220,737L103,620Q80,597 80,563.5Q80,530 103,507L146,464Q169,441 203,441Q237,441 260,464L289,494L335,448L160,273L273,160L448,335L493,290L464,260Q441,237 441,203Q441,169 464,146L507,103Q530,80 563.5,80Q597,80 620,103L737,220Q760,243 760,276.5Q760,310 737,333L694,376Q671,399 637,399Q603,399 580,376L550,347L505,392L600,487L487,600ZM520,880L520,800Q637,800 718.5,718.5Q800,637 800,520L880,520Q880,595 851.5,660.5Q823,726 774.5,774.5Q726,823 660.5,851.5Q595,880 520,880ZM520,720L520,640Q570,640 605,605Q640,570 640,520L720,520Q720,603 661.5,661.5Q603,720 520,720ZM520,203L550,233L593,190L563,160Q563,160 563,160Q563,160 563,160L520,203Q520,203 520,203Q520,203 520,203ZM160,563L190,593L233,550L203,520Q203,520 203,520Q203,520 203,520L160,563Q160,563 160,563Q160,563 160,563ZM637,320L680,277Q680,277 680,277Q680,277 680,277L650,247L607,290L637,320Q637,320 637,320Q637,320 637,320ZM277,680L320,637Q320,637 320,637Q320,637 320,637L290,607L247,650L277,680Q277,680 277,680Q277,680 277,680Z"/>
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 33f1864..710ee5d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -13182,4 +13182,7 @@
 
     <!-- Provider Model: summary of converted in SIM category. [CHAR LIMIT=50] -->
     <string name="sim_category_converted_sim">Converted to eSIM. Remove and discard.</string>
+
+    <!--Title for Sync Across Devices category-->
+    <string name="sync_across_devices_title">Sync across devices</string>
 </resources>
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index d9fccc4..7fcf85e 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -109,6 +109,11 @@
     </PreferenceCategory>
 
     <PreferenceCategory
+        android:key="sync_across_devices"
+        android:title="@string/sync_across_devices_title"
+        settings:controller="com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesPreferenceController"/>
+
+    <PreferenceCategory
         android:key="advanced_section_header"
         android:title="@string/advanced_section_header">
         <com.android.settingslib.RestrictedPreference
diff --git a/res/xml/satellite_setting.xml b/res/xml/satellite_setting.xml
index 9eb1763..09fbbd6 100644
--- a/res/xml/satellite_setting.xml
+++ b/res/xml/satellite_setting.xml
@@ -53,7 +53,7 @@
             android:key="key_supported_service"
             android:title="@string/title_supported_service"
             android:summary="@string/summary_supported_service"
-            android:icon="@drawable/ic_satellite_alt_24px"/>
+            android:icon="@drawable/ic_android_satellite_24px"/>
     </PreferenceCategory>
 
     <com.android.settingslib.widget.FooterPreference
diff --git a/src/com/android/settings/accessibility/OWNERS b/src/com/android/settings/accessibility/OWNERS
index 3bd156b..633e9c7 100644
--- a/src/com/android/settings/accessibility/OWNERS
+++ b/src/com/android/settings/accessibility/OWNERS
@@ -1,3 +1,6 @@
+# The Android Accessibility team should approve all changes to Settings > Accessibility content.
+set noparent
+
 # Default reviewers for this and subdirectories.
 chunkulin@google.com
 danielnorman@google.com
@@ -8,5 +11,9 @@
 # Legacy owner(s).
 menghanli@google.com #{LAST_RESORT_SUGGESTION}
 
+# Core Settings owner for emergency changes.
+cipson@google.com #{LAST_RESORT_SUGGESTION}
+
+# Partner-team files
 per-file HapticFeedbackIntensityPreferenceController.java = michaelwr@google.com
 per-file *Vibration* = michaelwr@google.com
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
index 5e395b2..564e138 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
@@ -199,10 +199,8 @@
     @Override
     public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
         if (device.equals(mCachedDevice.getDevice())) {
-            if (DEBUG) {
-                Log.d(TAG, "onPresetSelectionFailed, device: " + device.getAddress()
-                        + ", reason: " + reason);
-            }
+            Log.w(TAG, "onPresetSelectionFailed, device: " + device.getAddress()
+                    + ", reason: " + reason);
             mContext.getMainExecutor().execute(() -> {
                 refresh();
                 showErrorToast();
@@ -213,10 +211,8 @@
     @Override
     public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
         if (hapGroupId == mHapClientProfile.getHapGroup(mCachedDevice.getDevice())) {
-            if (DEBUG) {
-                Log.d(TAG, "onPresetSelectionForGroupFailed, group: " + hapGroupId
-                        + ", reason: " + reason);
-            }
+            Log.w(TAG, "onPresetSelectionForGroupFailed, group: " + hapGroupId
+                    + ", reason: " + reason);
             // Try to set the preset independently if group operation failed
             if (mPreference != null) {
                 selectPresetIndependently(Integer.parseInt(mPreference.getValue()));
@@ -242,11 +238,8 @@
     @Override
     public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
         if (device.equals(mCachedDevice.getDevice())) {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "onSetPresetNameFailed, device: " + device.getAddress()
-                                + ", reason: " + reason);
-            }
+            Log.w(TAG, "onSetPresetNameFailed, device: " + device.getAddress()
+                    + ", reason: " + reason);
             mContext.getMainExecutor().execute(() -> {
                 refresh();
                 showErrorToast();
@@ -257,10 +250,8 @@
     @Override
     public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
         if (hapGroupId == mHapClientProfile.getHapGroup(mCachedDevice.getDevice())) {
-            if (DEBUG) {
-                Log.d(TAG, "onSetPresetNameForGroupFailed, group: " + hapGroupId
-                        + ", reason: " + reason);
-            }
+            Log.w(TAG, "onSetPresetNameForGroupFailed, group: " + hapGroupId
+                    + ", reason: " + reason);
             mContext.getMainExecutor().execute(() -> {
                 refresh();
                 showErrorToast();
@@ -288,9 +279,6 @@
         for (int i = 0; i < infoList.size(); i++) {
             presetNames[i] = infoList.get(i).getName();
             presetIndexes[i] = Integer.toString(infoList.get(i).getIndex());
-            if (DEBUG) {
-                Log.d(TAG, "loadAllPresetInfo, preset " + presetIndexes[i] + ": " + presetNames[i]);
-            }
         }
         mPreference.setEntries(presetNames);
         mPreference.setEntryValues(presetIndexes);
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 6df80f1..9abc6c2 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -545,9 +545,13 @@
                         getActivity(), this, forceRoundedIcons, pref, tile, key,
                         mPlaceholderPreferenceController.getOrder());
                 if (Flags.dynamicInjectionCategory()) {
-                    Preference group = screen.findPreference(tile.getGroupKey());
-                    if (tile.hasGroupKey() && group instanceof PreferenceCategory) {
-                        ((PreferenceCategory) group).addPreference(pref);
+                    if (tile.hasGroupKey()) {
+                        Preference group = screen.findPreference(tile.getGroupKey());
+                        if (group instanceof PreferenceCategory) {
+                            ((PreferenceCategory) group).addPreference(pref);
+                        } else {
+                            screen.addPreference(pref);
+                        }
                     } else {
                         screen.addPreference(pref);
                     }
diff --git a/src/com/android/settings/development/OemUnlockPreferenceController.java b/src/com/android/settings/development/OemUnlockPreferenceController.java
index 9076f9a..2542a11 100644
--- a/src/com/android/settings/development/OemUnlockPreferenceController.java
+++ b/src/com/android/settings/development/OemUnlockPreferenceController.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -176,6 +177,13 @@
 
     /** Returns {@code true} if the device is SIM-locked. Otherwise, returns {@code false}. */
     private boolean isSimLockedDevice() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
+            Log.w(TAG,
+                    "getAllowedCarriers is unsupported without "
+                            + "PackageManager#FEATURE_TELEPHONY_CARRIERLOCK");
+            return false;
+        }
         int phoneCount = mTelephonyManager.getPhoneCount();
         for (int i = 0; i < phoneCount; i++) {
             if (mTelephonyManager.getAllowedCarriers(i).size() > 0) {
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index 904923a..66ce8b8 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -396,7 +396,11 @@
                     chargeTimeMs <= 0
                             ? null
                             : getPowerRemainingChargingLabel(
-                                    context, chargeTimeMs, info.isFastCharging, currentTimeMs);
+                                    context,
+                                    chargeTimeMs,
+                                    info.isFastCharging,
+                                    info.pluggedStatus,
+                                    currentTimeMs);
 
             info.chargeLabel =
                     chargeTimeMs <= 0
@@ -428,20 +432,35 @@
     }
 
     private static CharSequence getPowerRemainingChargingLabel(
-            Context context, long remainingTimeMs, boolean isFastCharging, long currentTimeMs) {
+            Context context,
+            long chargeRemainingTimeMs,
+            boolean isFastCharging,
+            int pluggedStatus,
+            long currentTimeMs) {
+        if (pluggedStatus == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+            BatterySettingsFeatureProvider featureProvider =
+                    FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider();
+            final CharSequence wirelessChargingRemainingLabel =
+                    featureProvider.getWirelessChargingRemainingLabel(
+                            context, chargeRemainingTimeMs, currentTimeMs);
+            if (wirelessChargingRemainingLabel != null) {
+                return wirelessChargingRemainingLabel;
+            }
+        }
         if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
             int chargeLabelResId =
                     isFastCharging
                             ? R.string.power_remaining_fast_charging_duration_only_v2
                             : R.string.power_remaining_charging_duration_only_v2;
             String timeString =
-                    PowerUtil.getTargetTimeShortString(context, remainingTimeMs, currentTimeMs);
+                    PowerUtil.getTargetTimeShortString(
+                            context, chargeRemainingTimeMs, currentTimeMs);
             return context.getString(chargeLabelResId, timeString);
         }
         final CharSequence timeString =
                 StringUtil.formatElapsedTime(
                         context,
-                        remainingTimeMs,
+                        chargeRemainingTimeMs,
                         /* withSeconds= */ false,
                         /* collapseTimeUnit= */ true);
         return context.getString(R.string.power_remaining_charging_duration_only, timeString);
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
index c6fd6d4..a1a220d 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
@@ -48,4 +48,9 @@
     /** Return a label for the bottom summary during wireless charging. */
     @Nullable
     CharSequence getWirelessChargingLabel(@NonNull Context context, @NonNull BatteryInfo info);
+
+    /** Return a charging remaining time label for wireless charging. */
+    @Nullable
+    CharSequence getWirelessChargingRemainingLabel(
+            @NonNull Context context, long remainingTimeMs, long currentTimeMs);
 }
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
index a4900fb..49db450 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
@@ -60,4 +60,11 @@
             @NonNull Context context, @NonNull BatteryInfo info) {
         return null;
     }
+
+    @Nullable
+    @Override
+    public CharSequence getWirelessChargingRemainingLabel(
+            @NonNull Context context, long remainingTimeMs, long currentTimeMs) {
+        return null;
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index 1cbe6da..1c6ff54 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -321,8 +321,17 @@
         final List<BatteryEvent> batteryEventList = new ArrayList<>();
         final List<BatteryLevelData.PeriodBatteryLevelData> levelDataList =
                 batteryLevelData.getHourlyBatteryLevelsPerDay();
-        for (BatteryLevelData.PeriodBatteryLevelData oneDayData : levelDataList) {
-            for (int hourIndex = 0; hourIndex < oneDayData.getLevels().size() - 1; hourIndex++) {
+        final int dailyDataSize = levelDataList.size();
+        for (int dailyIndex = 0; dailyIndex < dailyDataSize; dailyIndex++) {
+            final BatteryLevelData.PeriodBatteryLevelData oneDayData =
+                    levelDataList.get(dailyIndex);
+            final int hourDataSize = oneDayData.getLevels().size();
+            for (int hourIndex = 0; hourIndex < hourDataSize; hourIndex++) {
+                // For timestamp data on adjacent days, the last data (24:00) of the previous day is
+                // equal to the first data (00:00) of the next day, so skip sending EVEN_HOUR event.
+                if (dailyIndex < dailyDataSize - 1 && hourIndex == hourDataSize - 1) {
+                    continue;
+                }
                 batteryEventList.add(
                         convertToBatteryEvent(
                                 oneDayData.getTimestamps().get(hourIndex),
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index afb385d..719d3bd 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -31,6 +31,7 @@
 
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -649,6 +650,22 @@
         // Process raw history map data into hourly timestamps.
         final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
                 DataProcessor.getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
+        if (isFromPeriodJob && !processedBatteryHistoryMap.isEmpty()) {
+            // For periodic job, only generate battery usage data between even-hour timestamps.
+            // Remove the timestamps:
+            // 1) earlier than the latest completed period job (startTimestamp)
+            // 2) later than current scheduled even-hour job (lastEvenHourTimestamp).
+            final long lastEvenHourTimestamp = TimestampUtils.getLastEvenHourTimestamp(currentTime);
+            final Set<Long> batteryHistMapKeySet = processedBatteryHistoryMap.keySet();
+            final long minTimestamp = Collections.min(batteryHistMapKeySet);
+            final long maxTimestamp = Collections.max(batteryHistMapKeySet);
+            if (minTimestamp < startTimestamp) {
+                processedBatteryHistoryMap.remove(minTimestamp);
+            }
+            if (maxTimestamp > lastEvenHourTimestamp) {
+                processedBatteryHistoryMap.remove(maxTimestamp);
+            }
+        }
         // Wrap and processed history map into easy-to-use format for UI rendering.
         final BatteryLevelData batteryLevelData =
                 DataProcessor.getLevelDataThroughProcessedHistoryMap(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
index 0ac8cca..88bd4ad 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
@@ -37,6 +37,7 @@
 import java.time.Clock;
 import java.time.Duration;
 import java.util.List;
+import java.util.TimeZone;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Function;
 
@@ -97,6 +98,7 @@
     static void dumpBatteryUsageSlotDatabaseHist(Context context, PrintWriter writer) {
         final BatteryUsageSlotDao dao =
                 BatteryStateDatabase.getInstance(context).batteryUsageSlotDao();
+        writer.println("\n\tBattery Usage Slot TimeZone ID: " + TimeZone.getDefault().getID());
         writer.println("\n\tBattery Usage Slot DatabaseHistory:");
         final List<BatteryUsageSlotEntity> entities =
                 dao.getAllAfterForLog(getLastFullChargeTimestamp(context));
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 48d918c..a2ca9ae 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -204,7 +204,7 @@
         if (mIsEmbeddingActivityEnabled) {
             final UserManager um = getSystemService(UserManager.class);
             final UserInfo userInfo = um.getUserInfo(getUserId());
-            if (userInfo.isManagedProfile()) {
+            if (!userInfo.isMain()) {
                 final Intent intent = new Intent(getIntent())
                         .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
                         .putExtra(EXTRA_USER_HANDLE, getUser())
diff --git a/src/com/android/settings/network/WepNetworkDialogActivity.kt b/src/com/android/settings/network/WepNetworkDialogActivity.kt
index d6fa795..3233916 100644
--- a/src/com/android/settings/network/WepNetworkDialogActivity.kt
+++ b/src/com/android/settings/network/WepNetworkDialogActivity.kt
@@ -18,6 +18,7 @@
 
 import android.app.settings.SettingsEnums
 import android.net.wifi.WifiManager
+import android.view.WindowManager
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -30,13 +31,11 @@
 import com.android.settingslib.spa.SpaDialogWindowTypeActivity
 import com.android.settingslib.spa.widget.dialog.AlertDialogButton
 import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogContent
+import com.android.settingslib.wifi.WifiUtils.Companion.DIALOG_WINDOW_TYPE
 import com.android.settingslib.wifi.WifiUtils.Companion.SSID
 
 class WepNetworkDialogActivity : SpaDialogWindowTypeActivity() {
 
-    // TODO: Set different window type when called from Quick Settings.
-    override val dialogWindowType = null
-
     @Composable
     override fun Content() {
         val context = LocalContext.current
@@ -70,4 +69,11 @@
                 )
             })
     }
+
+    override fun getDialogWindowType(): Int {
+        return intent.getIntExtra(
+            DIALOG_WINDOW_TYPE,
+            WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+        )
+    }
 }
diff --git a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
index cf1350c..1acfaf8 100644
--- a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
@@ -125,16 +125,21 @@
         }
         EuiccManager euiccManager = (EuiccManager)
                 mContext.getSystemService(Context.EUICC_SERVICE);
-        if (!euiccManager.isPsimConversionSupported(subInfo.getCarrierId())) {
-            Log.i(TAG, "subId is not matched with pSIM conversion"
-                    + " supported carriers:" + subInfo.getCarrierId());
-            return CONDITIONALLY_UNAVAILABLE;
-        }
-        if (findConversionSupportComponent()) {
-            return mSubscriptionInfoEntity != null && mSubscriptionInfoEntity.isActiveSubscriptionId
-                    && !mSubscriptionInfoEntity.isEmbedded && isActiveSubscription(subId)
-                    ? AVAILABLE
-                    : CONDITIONALLY_UNAVAILABLE;
+        try {
+            if (!euiccManager.isPsimConversionSupported(subInfo.getCarrierId())) {
+                Log.i(TAG, "subId is not matched with pSIM conversion"
+                        + " supported carriers:" + subInfo.getCarrierId());
+                return CONDITIONALLY_UNAVAILABLE;
+            }
+            if (findConversionSupportComponent()) {
+                return mSubscriptionInfoEntity != null
+                        && mSubscriptionInfoEntity.isActiveSubscriptionId
+                        && !mSubscriptionInfoEntity.isEmbedded && isActiveSubscription(subId)
+                        ? AVAILABLE
+                        : CONDITIONALLY_UNAVAILABLE;
+            }
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Fail to check pSIM conversion supported carrier: " + e.getMessage());
         }
         return CONDITIONALLY_UNAVAILABLE;
     }
diff --git a/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt b/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt
index c696ef1..7f42400 100644
--- a/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt
+++ b/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt
@@ -19,6 +19,12 @@
 import android.content.Context
 import android.telephony.AccessNetworkConstants.AccessNetworkType
 import android.telephony.CellIdentity
+import android.telephony.CellIdentityCdma
+import android.telephony.CellIdentityGsm
+import android.telephony.CellIdentityLte
+import android.telephony.CellIdentityNr
+import android.telephony.CellIdentityTdscdma
+import android.telephony.CellIdentityWcdma
 import android.telephony.CellInfo
 import android.telephony.CellInfoCdma
 import android.telephony.CellInfoGsm
@@ -120,17 +126,17 @@
         getAccessNetworkTypeFromCellInfo(),
     )
 
-    private fun getIconIdForCell(): Int = when (cellInfo) {
-        is CellInfoGsm -> R.drawable.signal_strength_g
-        is CellInfoCdma -> R.drawable.signal_strength_1x
-        is CellInfoWcdma, is CellInfoTdscdma -> R.drawable.signal_strength_3g
+    private fun getIconIdForCell(): Int = when (cellId) {
+        is CellIdentityGsm -> R.drawable.signal_strength_g
+        is CellIdentityCdma -> R.drawable.signal_strength_1x
+        is CellIdentityWcdma, is CellIdentityTdscdma -> R.drawable.signal_strength_3g
 
-        is CellInfoLte -> {
+        is CellIdentityLte -> {
             if (show4GForLTE) R.drawable.ic_signal_strength_4g
             else R.drawable.signal_strength_lte
         }
 
-        is CellInfoNr -> R.drawable.signal_strength_5g
+        is CellIdentityNr -> R.drawable.signal_strength_5g
         else -> MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON
     }
 
diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
index 510bfd6..f29a5ef 100644
--- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java
+++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
@@ -490,6 +490,7 @@
      */
     private void forceUpdateConnectedPreferenceCategory(
             NetworkSelectRepository.NetworkRegistrationAndForbiddenInfo info) {
+        mPreferenceCategory.removeAll();
         for (NetworkRegistrationInfo regInfo : info.getNetworkList()) {
             final CellIdentity cellIdentity = regInfo.getCellIdentity();
             if (cellIdentity == null) {
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureCallback.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureCallback.java
new file mode 100644
index 0000000..6dfe183
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureCallback.java
@@ -0,0 +1,38 @@
+/*
+ * 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.notification.syncacrossdevices;
+
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+/** Callback to add or remove {@link Preference} in Sync Across Devices feature. */
+public interface SyncAcrossDevicesFeatureCallback {
+
+    /**
+     * Called when a sync across devices feature is added
+     *
+     * @param preference present the feature
+     */
+    void onFeatureAdded(@Nullable Preference preference);
+
+    /**
+     * Called when a sync across devices feature is removed
+     *
+     * @param preference present the feature
+     */
+    void onFeatureRemoved(@Nullable Preference preference);
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProvider.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProvider.java
new file mode 100644
index 0000000..d575d59
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProvider.java
@@ -0,0 +1,32 @@
+/*
+ * 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.notification.syncacrossdevices;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/** Feature provider for the Sync Across Devices. */
+public interface SyncAcrossDevicesFeatureProvider {
+
+    /** Returns the SyncAcrossDevicesFeatureUpdater of the Sync Across Devices feature */
+    @Nullable
+    SyncAcrossDevicesFeatureUpdater getSyncAcrossDevicesFeatureUpdater(
+            @NonNull Context context,
+            @NonNull SyncAcrossDevicesFeatureCallback featurePreferenceCallback);
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProviderImpl.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProviderImpl.java
new file mode 100644
index 0000000..090bf63
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProviderImpl.java
@@ -0,0 +1,34 @@
+/*
+ * 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.notification.syncacrossdevices;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/** Default implementation for {@link SyncAcrossDevicesFeatureProvider} */
+public class SyncAcrossDevicesFeatureProviderImpl implements SyncAcrossDevicesFeatureProvider {
+
+    @Override
+    @Nullable
+    public SyncAcrossDevicesFeatureUpdater getSyncAcrossDevicesFeatureUpdater(
+            @NonNull Context context,
+            @NonNull SyncAcrossDevicesFeatureCallback featurePreferenceCallback) {
+        return new SyncAcrossDevicesFeatureUpdater() {};
+    }
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureUpdater.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureUpdater.java
new file mode 100644
index 0000000..f9407b0
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureUpdater.java
@@ -0,0 +1,34 @@
+/*
+ * 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.notification.syncacrossdevices;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Updates the sync across devices feature state. It notifies the upper level whether to add/remove
+ * the preference through {@link SyncAcrossDevicesFeatureCallback}
+ */
+public interface SyncAcrossDevicesFeatureUpdater {
+
+    /** Forces to update the list of the Sync Across Devices feature. */
+    default void forceUpdate() {}
+
+    /** Sets the context to generate the {@link Preference}, so it could get the correct theme. */
+    default void setPreferenceContext(@Nullable Context preferenceContext) {}
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceController.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceController.java
new file mode 100644
index 0000000..ccea671
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceController.java
@@ -0,0 +1,100 @@
+/*
+ * 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.notification.syncacrossdevices;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.overlay.FeatureFactory;
+
+public class SyncAcrossDevicesPreferenceController extends BasePreferenceController
+        implements PreferenceControllerMixin, SyncAcrossDevicesFeatureCallback {
+
+    private static final String TAG = "SyncXDevicesPrefCtr";
+
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private PreferenceGroup mPreferenceGroup;
+    private SyncAcrossDevicesFeatureUpdater mSyncAcrossDevicesFeatureUpdater;
+
+    public SyncAcrossDevicesPreferenceController(@NonNull Context context, @NonNull String key) {
+        super(context, key);
+        SyncAcrossDevicesFeatureProvider syncAcrossDevicesFeatureProvider =
+                FeatureFactory.getFeatureFactory().getSyncAcrossDevicesFeatureProvider();
+        mSyncAcrossDevicesFeatureUpdater =
+                syncAcrossDevicesFeatureProvider.getSyncAcrossDevicesFeatureUpdater(context, this);
+    }
+
+    @Override
+    public void displayPreference(@NonNull PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreferenceGroup = screen.findPreference(getPreferenceKey());
+        mPreferenceGroup.setVisible(false);
+        if (isAvailable()) {
+            final Context context = screen.getContext();
+            mSyncAcrossDevicesFeatureUpdater.setPreferenceContext(context);
+            mSyncAcrossDevicesFeatureUpdater.forceUpdate();
+        }
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mSyncAcrossDevicesFeatureUpdater != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public void onFeatureAdded(@Nullable Preference preference) {
+        if (preference == null) {
+            if (DEBUG) {
+                Log.d(TAG, "onFeatureAdded receives null preference. Ignore.");
+            }
+            return;
+        }
+        mPreferenceGroup.addPreference(preference);
+        updatePreferenceVisibility();
+    }
+
+    @Override
+    public void onFeatureRemoved(@Nullable Preference preference) {
+        if (preference == null) {
+            if (DEBUG) {
+                Log.d(TAG, "onFeatureRemoved receives null preference. Ignore.");
+            }
+            return;
+        }
+        mPreferenceGroup.removePreference(preference);
+        updatePreferenceVisibility();
+    }
+
+    private void updatePreferenceVisibility() {
+        mPreferenceGroup.setVisible(mPreferenceGroup.getPreferenceCount() > 0);
+    }
+
+    @VisibleForTesting
+    public void setPreferenceGroup(@NonNull PreferenceGroup preferenceGroup) {
+        mPreferenceGroup = preferenceGroup;
+    }
+}
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 53ad8ba..ef63f19 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -38,6 +38,7 @@
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider
 import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
 import com.android.settings.localepicker.LocaleFeatureProvider
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider
 import com.android.settings.onboarding.OnboardingFeatureProvider
 import com.android.settings.overlay.FeatureFactory.Companion.setFactory
 import com.android.settings.panel.PanelFeatureProvider
@@ -188,6 +189,11 @@
      */
     abstract val audioSharingFeatureProvider: AudioSharingFeatureProvider
 
+    /**
+     * Gets implementation for sync across devices related feature.
+     */
+    abstract val syncAcrossDevicesFeatureProvider: SyncAcrossDevicesFeatureProvider
+
     companion object {
         private var _factory: FeatureFactory? = null
 
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index 1770209..c74260c 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -55,6 +55,8 @@
 import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
 import com.android.settings.inputmethod.KeyboardSettingsFeatureProviderImpl
 import com.android.settings.localepicker.LocaleFeatureProviderImpl
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProviderImpl
 import com.android.settings.panel.PanelFeatureProviderImpl
 import com.android.settings.search.SearchFeatureProvider
 import com.android.settings.search.SearchFeatureProviderImpl
@@ -197,4 +199,8 @@
     override val audioSharingFeatureProvider: AudioSharingFeatureProvider by lazy {
         AudioSharingFeatureProviderImpl()
     }
+
+    override val syncAcrossDevicesFeatureProvider: SyncAcrossDevicesFeatureProvider by lazy {
+        SyncAcrossDevicesFeatureProviderImpl()
+    }
 }
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index f61f7bb..623816a 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -36,11 +36,11 @@
 import androidx.activity.result.ActivityResultLauncher;
 import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.Nullable;
-import androidx.fragment.app.FragmentActivity;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.SetScreenLockDialogActivity;
 import com.android.settings.R;
+import com.android.settings.SettingsActivity;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settingslib.transition.SettingsTransitionHelper;
 
@@ -52,7 +52,7 @@
  * user to set a device lock if not set with an alert dialog. This can be launched using the intent
  * com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS.
  */
-public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
+public class PrivateSpaceAuthenticationActivity extends SettingsActivity {
     private static final String TAG = "PrivateSpaceAuthCheck";
     public static final String EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED =
             "extra_show_private_space_unlocked";
@@ -76,6 +76,9 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        if (isFinishing()) {
+            return;
+        }
 
         if (Flags.allowPrivateProfile()
                 && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
diff --git a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
index 4e904d3..ed70030 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
@@ -58,7 +58,8 @@
     public void onStart() {
         super.onStart();
         if (PrivateSpaceMaintainer.getInstance(getContext()).isPrivateSpaceLocked()) {
-            finish();
+            // To make sure the task is removed if it is the last activity in that stack.
+            getActivity().finishAndRemoveTask();
         }
     }
 
diff --git a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java
index b0b65f6..6f3c824 100644
--- a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java
+++ b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java
@@ -114,7 +114,7 @@
         return getSubscriptionManager().getDefaultDataSubscriptionInfo();
     }
 
-    private void updateDialog(AlertDialog dialog) {
+    private void updateDialog(@Nullable AlertDialog dialog) {
         Log.d(TAG, "Dialog updated, dismiss status: " + mWasDismissed);
         if (mWasDismissed) {
             return;
@@ -123,6 +123,7 @@
         if (dialog == null) {
             Log.d(TAG, "Dialog is null.");
             dismiss();
+            return;
         }
 
         SubscriptionInfo currentDataSubInfo = getDefaultDataSubInfo();
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 2b40a91..03cd743 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -111,7 +111,7 @@
                 placeholder = {Text(text = originalSimCarrierName)},
                 modifier = Modifier.fillMaxWidth()
             ) {
-                titleSimName = it
+                titleSimName = if (it.isEmpty()) originalSimCarrierName else it
             }
         },
     )
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
index d5887e6..ff5c6c5 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
@@ -21,6 +21,7 @@
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
@@ -634,6 +635,79 @@
                 expectedChargeLabel);
     }
 
+    @Test
+    public void getBatteryInfo_customizedWLCLabel_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
+                /* chargingStringV2Enabled= */ true);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
+        var expectedLabel = "Full by 8:00 AM";
+        when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
+                        eq(mContext), anyLong(), anyLong()))
+                .thenReturn(expectedLabel);
+        var currentTimeMillis = Instant.parse("2021-02-09T13:00:00.00Z").toEpochMilli();
+        var info =
+                BatteryInfo.getBatteryInfo(
+                        mContext,
+                        batteryIntent,
+                        mBatteryUsageStats,
+                        MOCK_ESTIMATE,
+                        /* elapsedRealtimeUs= */ UNUSED_TIME_MS,
+                        /* shortString= */ false,
+                        /* currentTimeMillis= */ currentTimeMillis);
+
+        assertThat(info.remainingLabel).isEqualTo(expectedLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_noCustomizedWLCLabel_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
+                /* chargingStringV2Enabled= */ true);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
+        when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
+                        eq(mContext), anyLong(), anyLong()))
+                .thenReturn(null);
+        var expectedStatusLabel = "Charging";
+        var expectedRemainingLabel = "Fully charged by";
+        var expectedChargeLabel = "45% - " + expectedRemainingLabel;
+        var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                currentTimeMillis,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_noCustomWirelessChargingLabelWithV1_updateRemainingAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
+                /* chargingStringV2Enabled= */ false);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 10);
+        when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
+                        eq(mContext), anyLong(), anyLong()))
+                .thenReturn(null);
+        var expectedStatusLabel = "Charging wirelessly";
+        var expectedRemainingLabel = "2 hr, 10 min left until full";
+        var expectedChargeLabel = "10% - " + expectedRemainingLabel;
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                /* currentTimeMillis= */ UNUSED_TIME_MS,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
     private enum ChargingSpeed {
         FAST,
         REGULAR,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
index 158756a..c6241ab 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
@@ -70,7 +70,13 @@
         assertThat(expectedResult).isTrue();
     }
 
-    @Test void getWirelessChargingLabel_returnNull() {
+    @Test
+    public void getWirelessChargingLabel_returnNull() {
         assertThat(mImpl.getWirelessChargingLabel(mContext, new BatteryInfo())).isNull();
     }
+
+    @Test
+    public void getWirelessChargingRemainingLabel_returnNull() {
+        assertThat(mImpl.getWirelessChargingRemainingLabel(mContext, 1000L, 1000L)).isNull();
+    }
 }
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 3ed2dd1..b5cb446 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -565,7 +565,9 @@
     public void convertToBatteryEventList_normalCase_returnsExpectedResult() {
         final BatteryLevelData batteryLevelData =
                 new BatteryLevelData(
-                        Map.of(1691589600000L, 98, 1691596800000L, 90, 1691596812345L, 80));
+                        // 2023-08-09 14:00:00 UTC
+                        // 2023-08-09 16:00:00 UTC
+                        Map.of(1691589600000L, 98, 1691596800000L, 90));
 
         final List<BatteryEvent> batteryEventList =
                 ConvertUtils.convertToBatteryEventList(batteryLevelData);
@@ -580,6 +582,31 @@
     }
 
     @Test
+    public void convertToBatteryEventList_multipleDays_returnsExpectedResult() {
+        final BatteryLevelData batteryLevelData =
+                new BatteryLevelData(
+                        // 2024-04-23 22:00:00 UTC
+                        // 2024-04-24 00:00:00 UTC
+                        // 2024-04-24 02:00:00 UTC
+                        Map.of(1713909600000L, 98, 1713916800000L, 90, 1713924000000L, 83));
+
+        final List<BatteryEvent> batteryEventList =
+                ConvertUtils.convertToBatteryEventList(batteryLevelData);
+
+        assertThat(batteryEventList).hasSize(3);
+        assertThat(batteryEventList.get(0).getTimestamp()).isEqualTo(1713909600000L);
+        assertThat(batteryEventList.get(0).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
+        assertThat(batteryEventList.get(0).getBatteryLevel()).isEqualTo(98);
+        assertThat(batteryEventList.get(1).getTimestamp()).isEqualTo(1713916800000L);
+        assertThat(batteryEventList.get(1).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
+        assertThat(batteryEventList.get(1).getBatteryLevel()).isEqualTo(90);
+        assertThat(batteryEventList.get(2).getTimestamp()).isEqualTo(1713924000000L);
+        assertThat(batteryEventList.get(2).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
+        assertThat(batteryEventList.get(2).getBatteryLevel()).isEqualTo(83);
+    }
+
+
+    @Test
     public void convertToBatteryUsageSlotList_normalCase_returnsExpectedResult() {
         BatteryDiffData batteryDiffData1 =
                 new BatteryDiffData(
diff --git a/tests/robotests/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceControllerTest.java
new file mode 100644
index 0000000..7bfde1b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.notification.syncacrossdevices;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SyncAcrossDevicesPreferenceControllerTest {
+
+    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private SyncAcrossDevicesFeatureUpdater mSyncAcrossDevicesFeatureUpdater;
+    @Mock private PreferenceManager mPreferenceManager;
+
+    private static final String PREFERENCE_KEY = "preference_key";
+
+    private Context mContext;
+    private SyncAcrossDevicesPreferenceController mSyncAcrossDevicesPreferenceController;
+    private PreferenceGroup mPreferenceGroup;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+        SyncAcrossDevicesFeatureProvider provider =
+                FakeFeatureFactory.setupForTest().getSyncAcrossDevicesFeatureProvider();
+        doReturn(mSyncAcrossDevicesFeatureUpdater)
+                .when(provider)
+                .getSyncAcrossDevicesFeatureUpdater(any(), any());
+
+        mSyncAcrossDevicesPreferenceController =
+                new SyncAcrossDevicesPreferenceController(mContext, PREFERENCE_KEY);
+
+        mPreferenceGroup = spy(new PreferenceCategory(mContext));
+        doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager();
+        mPreferenceGroup.setVisible(false);
+
+        mPreferenceGroup.setKey(mSyncAcrossDevicesPreferenceController.getPreferenceKey());
+        mSyncAcrossDevicesPreferenceController.setPreferenceGroup(mPreferenceGroup);
+    }
+
+    @Test
+    public void testGetAvailabilityStatus_noFeatureUpdater_returnUnSupported() {
+        SyncAcrossDevicesFeatureProvider provider =
+                FakeFeatureFactory.setupForTest().getSyncAcrossDevicesFeatureProvider();
+        doReturn(null).when(provider).getSyncAcrossDevicesFeatureUpdater(any(), any());
+
+        SyncAcrossDevicesPreferenceController syncAcrossDevicesPreferenceController =
+                new SyncAcrossDevicesPreferenceController(mContext, PREFERENCE_KEY);
+
+        assertThat(syncAcrossDevicesPreferenceController.getAvailabilityStatus())
+                .isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void testGetAvailabilityStatus_withFeatureUpdater_returnSupported() {
+        assertThat(mSyncAcrossDevicesPreferenceController.getAvailabilityStatus())
+                .isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void testUpdatePreferenceVisibility_addFeaturePreference_shouldShowPreference() {
+        Preference preference = new Preference(mContext);
+
+        mSyncAcrossDevicesPreferenceController.onFeatureAdded(preference);
+
+        assertThat(mPreferenceGroup.isVisible()).isTrue();
+    }
+
+    @Test
+    public void testUpdatePreferenceVisibility_removeFeaturePreference_shouldHidePreference() {
+        Preference preference = new Preference(mContext);
+
+        mSyncAcrossDevicesPreferenceController.onFeatureAdded(preference);
+        mSyncAcrossDevicesPreferenceController.onFeatureRemoved(preference);
+
+        assertThat(mPreferenceGroup.isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDisplayPreference_availabilityStatusIsAvailable_shouldForceUpdated() {
+        PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
+        preferenceScreen.addPreference(mPreferenceGroup);
+
+        mSyncAcrossDevicesPreferenceController.displayPreference(preferenceScreen);
+
+        verify(mSyncAcrossDevicesFeatureUpdater, times(1)).setPreferenceContext(any());
+        verify(mSyncAcrossDevicesFeatureUpdater, times(1)).forceUpdate();
+    }
+}
diff --git a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
index 38683d0..71f8e58 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
@@ -40,6 +40,7 @@
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
 import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProvider;
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider;
 import com.android.settings.onboarding.OnboardingFeatureProvider;
 import com.android.settings.overlay.DockUpdaterFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
@@ -103,6 +104,7 @@
     public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
     public DisplayFeatureProvider mDisplayFeatureProvider;
     public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
+    public SyncAcrossDevicesFeatureProvider mSyncAcrossDevicesFeatureProvider;
 
     /**
      * Call this in {@code @Before} method of the test class to use fake factory.
@@ -150,9 +152,10 @@
         mStylusFeatureProvider = mock(StylusFeatureProvider.class);
         mOnboardingFeatureProvider = mock(OnboardingFeatureProvider.class);
         mFastPairFeatureProvider = mock(FastPairFeatureProvider.class);
-        mPrivateSpaceLoginFeatureProvider =  mock(PrivateSpaceLoginFeatureProvider.class);
+        mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
         mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
         mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
+        mSyncAcrossDevicesFeatureProvider = mock(SyncAcrossDevicesFeatureProvider.class);
     }
 
     @Override
@@ -340,5 +343,10 @@
     public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
         return mAudioSharingFeatureProvider;
     }
+
+    @Override
+    public SyncAcrossDevicesFeatureProvider getSyncAcrossDevicesFeatureProvider() {
+        return mSyncAcrossDevicesFeatureProvider;
+    }
 }
 
diff --git a/tests/spa_unit/AndroidManifest.xml b/tests/spa_unit/AndroidManifest.xml
index 118060a..1950f20 100644
--- a/tests/spa_unit/AndroidManifest.xml
+++ b/tests/spa_unit/AndroidManifest.xml
@@ -24,6 +24,7 @@
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="com.android.settings.BATTERY_DATA" />
 
     <application android:debuggable="true">
         <provider android:name="com.android.settings.slices.SettingsSliceProvider"
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
index b7b96b0..a724a8c 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
@@ -20,13 +20,14 @@
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEnabled
 import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.hasTextExactly
+import androidx.compose.ui.test.isEnabled
 import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performClick
 import androidx.test.core.app.ApplicationProvider
@@ -36,18 +37,23 @@
 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
 import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController
 import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry
+import com.android.settingslib.spa.testutils.delay
 import com.android.settingslib.spaprivileged.model.app.userId
+import kotlinx.coroutines.runBlocking
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
 import org.mockito.MockitoSession
-import org.mockito.Spy
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
-import org.mockito.Mockito.`when` as whenever
 
+@OptIn(ExperimentalTestApi::class)
 @RunWith(AndroidJUnit4::class)
 class AppBatteryPreferenceTest {
     @get:Rule
@@ -55,29 +61,28 @@
 
     private lateinit var mockSession: MockitoSession
 
-    @Spy
-    private val context: Context = ApplicationProvider.getApplicationContext()
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
 
-    @Spy
-    private val resources = context.resources
+    private val resources = spy(context.resources) {
+        on { getBoolean(R.bool.config_show_app_info_settings_battery) } doReturn true
+    }
 
     @Before
     fun setUp() {
         mockSession = ExtendedMockito.mockitoSession()
-            .initMocks(this)
             .mockStatic(BatteryChartPreferenceController::class.java)
             .mockStatic(AdvancedPowerUsageDetail::class.java)
             .strictness(Strictness.LENIENT)
             .startMocking()
         whenever(context.resources).thenReturn(resources)
-        whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
-            .thenReturn(true)
     }
 
     private fun mockBatteryDiffEntry(batteryDiffEntry: BatteryDiffEntry?) {
-        whenever(BatteryChartPreferenceController.getAppBatteryUsageData(
-            context, PACKAGE_NAME, APP.userId
-        )).thenReturn(batteryDiffEntry)
+        whenever(
+            BatteryChartPreferenceController.getAppBatteryUsageData(
+                context, PACKAGE_NAME, APP.userId
+            )
+        ).thenReturn(batteryDiffEntry)
     }
 
     @After
@@ -87,8 +92,9 @@
 
     @Test
     fun whenConfigIsFalse_notDisplayed() {
-        whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
-            .thenReturn(false)
+        resources.stub {
+            on { getBoolean(R.bool.config_show_app_info_settings_battery) } doReturn false
+        }
 
         setContent()
 
@@ -112,47 +118,47 @@
 
         setContent()
 
-        composeTestRule.onNode(
+        composeTestRule.waitUntilExactlyOneExists(
             hasTextExactly(
                 context.getString(R.string.battery_details_title),
                 context.getString(R.string.no_battery_summary),
-            ),
-        ).assertIsDisplayed().assertIsEnabled()
+            ) and isEnabled(),
+        )
     }
 
     @Test
     fun noConsumePower() {
-        val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
-            mConsumePower = 0.0
-        }
+        val batteryDiffEntry = mock<BatteryDiffEntry>().apply { mConsumePower = 0.0 }
         mockBatteryDiffEntry(batteryDiffEntry)
 
         setContent()
 
-        composeTestRule.onNodeWithText(context.getString(R.string.no_battery_summary))
-            .assertIsDisplayed()
+        composeTestRule.waitUntilExactlyOneExists(
+            hasText(context.getString(R.string.no_battery_summary))
+        )
     }
 
     @Test
     fun hasConsumePower() {
-        val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
-            mConsumePower = 12.3
-        }
-        whenever(batteryDiffEntry.percentage).thenReturn(45.6)
+        val batteryDiffEntry = mock<BatteryDiffEntry> {
+            on { percentage } doReturn 45.6
+        }.apply { mConsumePower = 12.3 }
         mockBatteryDiffEntry(batteryDiffEntry)
 
         setContent()
 
-        composeTestRule.onNodeWithText("46% use since last full charge").assertIsDisplayed()
+        composeTestRule.waitUntilExactlyOneExists(hasText("46% use since last full charge"))
     }
 
     @Test
     fun whenClick_openDetailsPage() {
-        val batteryDiffEntry = mock(BatteryDiffEntry::class.java)
-        whenever(batteryDiffEntry.percentage).thenReturn(10.0)
+        val batteryDiffEntry = mock<BatteryDiffEntry> {
+            on { percentage } doReturn 10.0
+        }.apply { mConsumePower = 12.3 }
         mockBatteryDiffEntry(batteryDiffEntry)
 
         setContent()
+        composeTestRule.waitUntilExactlyOneExists(hasText("10% use since last full charge"))
         composeTestRule.onRoot().performClick()
 
         ExtendedMockito.verify {
@@ -178,7 +184,7 @@
     }
 
     private companion object {
-        const val PACKAGE_NAME = "packageName"
+        const val PACKAGE_NAME = "package.name"
         const val UID = 123
         val APP = ApplicationInfo().apply {
             packageName = PACKAGE_NAME
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index 606db8e..e1dcda2 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -39,6 +39,7 @@
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider
 import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
 import com.android.settings.localepicker.LocaleFeatureProvider
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider
 import com.android.settings.overlay.DockUpdaterFeatureProvider
 import com.android.settings.overlay.FeatureFactory
 import com.android.settings.overlay.SurveyFeatureProvider
@@ -152,4 +153,6 @@
         get() = TODO("Not yet implemented")
     override val audioSharingFeatureProvider: AudioSharingFeatureProvider
         get() = TODO("Not yet implemented")
+    override val syncAcrossDevicesFeatureProvider: SyncAcrossDevicesFeatureProvider
+        get() = TODO("Not yet implemented")
 }
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 29758de..cc129fd 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -40,6 +40,7 @@
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
 import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
 import com.android.settings.localepicker.LocaleFeatureProvider;
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider;
 import com.android.settings.onboarding.OnboardingFeatureProvider;
 import com.android.settings.overlay.DockUpdaterFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
@@ -102,6 +103,7 @@
     public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
     public DisplayFeatureProvider mDisplayFeatureProvider;
     public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
+    public SyncAcrossDevicesFeatureProvider mSyncAcrossDevicesFeatureProvider;
 
     /** Call this in {@code @Before} method of the test class to use fake factory. */
     public static FakeFeatureFactory setupForTest() {
@@ -154,6 +156,7 @@
         mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
         mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
         mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
+        mSyncAcrossDevicesFeatureProvider = mock(SyncAcrossDevicesFeatureProvider.class);
     }
 
     @Override
@@ -341,4 +344,9 @@
     public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
         return mAudioSharingFeatureProvider;
     }
+
+    @Override
+    public SyncAcrossDevicesFeatureProvider getSyncAcrossDevicesFeatureProvider() {
+        return mSyncAcrossDevicesFeatureProvider;
+    }
 }