Merge "[A11y] Add battery chart slot selected status in content description." into main
diff --git a/res/layout/accessibility_magnification_mode_header.xml b/res/layout/accessibility_dialog_header.xml
similarity index 93%
rename from res/layout/accessibility_magnification_mode_header.xml
rename to res/layout/accessibility_dialog_header.xml
index e476553..ace8b23 100644
--- a/res/layout/accessibility_magnification_mode_header.xml
+++ b/res/layout/accessibility_dialog_header.xml
@@ -21,9 +21,9 @@
     android:padding="?android:attr/dialogPreferredPadding">
 
     <TextView
+        android:id="@+id/accessibility_dialog_header_text_view"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/accessibility_magnification_area_settings_message"
         android:textSize="16sp"
         style="?android:attr/textAppearanceMedium"
         android:textColor="?android:attr/textColorAlertDialogListItem"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6193263..0f51ddd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1054,6 +1054,7 @@
     <!-- Watch unlock enrollment and settings --><skip />
     <!-- Title shown for menu item that launches watch unlock settings. [CHAR LIMIT=40] -->
     <string name ="security_settings_activeunlock_preference_title">Watch Unlock</string>
+    <string name="security_settings_activeunlock">Watch</string>
     <!-- Introduction shown in face and fingerprint page to introduce the biometric feature. [CHAR LIMIT=NONE]-->
     <string name="biometric_settings_intro_with_activeunlock">When you set up Face Unlock and Fingerprint Unlock, your phone will ask for your fingerprint when you wear a mask or are in a dark area.\n\nWatch Unlock is another convenient way to unlock your phone, for example, when your fingers are wet or face isn\u2019t recognized.</string>
     <!-- Introduction shown in fingerprint page to explain that watch unlock can be used if fingerprint isn't recognized. [CHAR LIMIT=NONE]-->
@@ -5219,6 +5220,16 @@
     <string name="accessibility_screen_magnification_title">Magnification</string>
     <!-- Title for accessibility shortcut preference for magnification. [CHAR LIMIT=60] -->
     <string name="accessibility_screen_magnification_shortcut_title">Magnification shortcut</string>
+    <!-- Title of cursor following mode preference for magnification. [CHAR LIMIT=60] -->
+    <string name="accessibility_magnification_cursor_following_title">Cursor following</string>
+    <!-- Header message of cursor following mode dialog for magnification. [CHAR LIMIT=none] -->
+    <string name="accessibility_magnification_cursor_following_header">Choose how Magnification follows your cursor.</string>
+    <!-- Option title of cursor following continuous mode in the mode selection dialog. [CHAR LIMIT=none] -->
+    <string name="accessibility_magnification_cursor_following_continuous">Move screen continuously as mouse moves</string>
+    <!-- Option title of cursor following center mode in the mode selection dialog. [CHAR LIMIT=none] -->
+    <string name="accessibility_magnification_cursor_following_center">Move screen keeping mouse at center of screen</string>
+    <!-- Option title of cursor following edge mode in the mode selection dialog. [CHAR LIMIT=none] -->
+    <string name="accessibility_magnification_cursor_following_edge">Move screen when mouse touches edges of screen</string>
     <!-- Title for accessibility follow typing preference for magnification. [CHAR LIMIT=35] -->
     <string name="accessibility_screen_magnification_follow_typing_title">Magnify typing</string>
     <!-- Summary for accessibility follow typing preference for magnification. [CHAR LIMIT=none] -->
diff --git a/res/xml/mouse_settings.xml b/res/xml/mouse_settings.xml
index e4b3f1c..ec1c39d 100644
--- a/res/xml/mouse_settings.xml
+++ b/res/xml/mouse_settings.xml
@@ -28,11 +28,11 @@
         settings:controller="com.android.settings.inputmethod.MousePointerAccelerationPreferenceController" />
 
     <com.android.settings.widget.SeekBarPreference
-        android:key="trackpad_pointer_speed"
-        android:title="@string/trackpad_pointer_speed"
+        android:key="pointer_speed"
+        android:title="@string/pointer_speed"
         android:order="20"
         android:selectable="false"
-        settings:controller="com.android.settings.inputmethod.TouchpadPointerSpeedPreferenceController"/>
+        settings:controller="com.android.settings.inputmethod.MousePointerSpeedPreferenceController"/>
 
     <SwitchPreferenceCompat
         android:key="mouse_swap_primary_button"
diff --git a/res/xml/night_display_settings.xml b/res/xml/night_display_settings.xml
index 95d5034..d75619d 100644
--- a/res/xml/night_display_settings.xml
+++ b/res/xml/night_display_settings.xml
@@ -52,7 +52,7 @@
         android:title="@string/night_display_end_time_title"
         settings:controller="com.android.settings.display.NightDisplayCustomEndTimePreferenceController"/>
 
-    <com.android.settings.widget.SeekBarPreference
+    <com.android.settingslib.widget.SliderPreference
         android:key="night_display_temperature"
         android:title="@string/night_display_temperature_title"
         settings:keywords="@string/keywords_display_night_display"
diff --git a/src/com/android/settings/accessibility/AccessibilityActivityPreference.java b/src/com/android/settings/accessibility/AccessibilityActivityPreference.java
index a8e456d..3ae64fc 100644
--- a/src/com/android/settings/accessibility/AccessibilityActivityPreference.java
+++ b/src/com/android/settings/accessibility/AccessibilityActivityPreference.java
@@ -127,14 +127,13 @@
         final String htmlDescription = mA11yShortcutInfo.loadHtmlDescription(mPm);
         final String settingsClassName = mA11yShortcutInfo.getSettingsActivityName();
         final String tileServiceClassName = mA11yShortcutInfo.getTileServiceName();
-        final int metricsCategory = FeatureFactory.getFeatureFactory()
-                .getAccessibilityMetricsFeatureProvider()
-                .getDownloadedFeatureMetricsCategory(mComponentName);
+        final int pageIdCategory = FeatureFactory.getFeatureFactory()
+                .getAccessibilityPageIdFeatureProvider().getCategory(mComponentName);
 
         ThreadUtils.getUiThreadHandler().post(() -> {
             RestrictedPreferenceHelper.putBasicExtras(
                     this, prefKey, getTitle(), intro, description, imageRes,
-                    htmlDescription, mComponentName, metricsCategory);
+                    htmlDescription, mComponentName, pageIdCategory);
             RestrictedPreferenceHelper.putSettingsExtras(this, getPackageName(), settingsClassName);
             RestrictedPreferenceHelper.putTileServiceExtras(
                     this, getPackageName(), tileServiceClassName);
diff --git a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
index 4e9cd92..347c769 100644
--- a/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityDetailsSettingsFragment.java
@@ -228,10 +228,10 @@
                     new ComponentName(packageName, tileServiceClassName).flattenToString());
         }
 
-        final int metricsCategory = FeatureFactory.getFeatureFactory()
-                .getAccessibilityMetricsFeatureProvider()
-                .getDownloadedFeatureMetricsCategory(componentName);
-        extras.putInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY, metricsCategory);
+        final int pageIdCategory = FeatureFactory.getFeatureFactory()
+                .getAccessibilityPageIdFeatureProvider().getCategory(componentName);
+        extras.putInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY, pageIdCategory);
+        extras.putInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY, pageIdCategory);
         extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName);
         extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, info.getAnimatedImageRes());
 
diff --git a/src/com/android/settings/accessibility/AccessibilityDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
index c89b8d7..dc49008 100644
--- a/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
@@ -104,6 +104,11 @@
          * screen / Switch between full and partial screen > Save.
          */
         int DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING = 1011;
+
+        /**
+         * OPEN: Settings > Accessibility > Magnification > Cursor following.
+         */
+        int DIALOG_MAGNIFICATION_CURSOR_FOLLOWING_MODE = 1012;
     }
 
     /**
diff --git a/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProvider.java b/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProvider.java
index 018bd2e..7d03230 100644
--- a/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProvider.java
+++ b/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProvider.java
@@ -15,8 +15,6 @@
  */
 package com.android.settings.accessibility;
 
-import android.content.ComponentName;
-
 import androidx.annotation.Nullable;
 
 /**
@@ -25,11 +23,11 @@
 public interface AccessibilityFeedbackFeatureProvider {
 
     /**
-     * Returns value according to the {@code componentName}.
+     * Returns value according to the {@code pageId}.
      *
-     * @param componentName the component name of the downloaded service or activity
-     * @return Feedback bucket ID
+     * @param pageId The unique identifier of the page.
+     * @return Feedback bucket ID associated with the page, or {@code null} if is not found.
      */
     @Nullable
-    String getCategory(@Nullable ComponentName componentName);
+    String getCategory(int pageId);
 }
diff --git a/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProviderImpl.java b/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProviderImpl.java
index 917c5f6..2381887 100644
--- a/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProviderImpl.java
+++ b/src/com/android/settings/accessibility/AccessibilityFeedbackFeatureProviderImpl.java
@@ -15,8 +15,6 @@
  */
 package com.android.settings.accessibility;
 
-import android.content.ComponentName;
-
 import androidx.annotation.Nullable;
 
 /** Default implementation of {@link AccessibilityFeedbackFeatureProvider}. */
@@ -25,7 +23,7 @@
 
     @Override
     @Nullable
-    public String getCategory(@Nullable ComponentName componentName) {
-        return "";
+    public String getCategory(int pageId) {
+        return null;
     }
 }
diff --git a/src/com/android/settings/accessibility/AccessibilityMetricsFeatureProvider.java b/src/com/android/settings/accessibility/AccessibilityPageIdFeatureProvider.java
similarity index 84%
rename from src/com/android/settings/accessibility/AccessibilityMetricsFeatureProvider.java
rename to src/com/android/settings/accessibility/AccessibilityPageIdFeatureProvider.java
index a9d7c05..698efbe 100644
--- a/src/com/android/settings/accessibility/AccessibilityMetricsFeatureProvider.java
+++ b/src/com/android/settings/accessibility/AccessibilityPageIdFeatureProvider.java
@@ -21,9 +21,9 @@
 import androidx.annotation.Nullable;
 
 /**
- * Provider for Accessibility metrics related features.
+ * Provider for Accessibility page id related features.
  */
-public interface AccessibilityMetricsFeatureProvider {
+public interface AccessibilityPageIdFeatureProvider {
 
     /**
      * Returns {@link android.app.settings.SettingsEnums} value according to the {@code
@@ -32,5 +32,5 @@
      * @param componentName the component name of the downloaded service or activity
      * @return value in {@link android.app.settings.SettingsEnums}
      */
-    int getDownloadedFeatureMetricsCategory(@Nullable ComponentName componentName);
+    int getCategory(@Nullable ComponentName componentName);
 }
diff --git a/src/com/android/settings/accessibility/AccessibilityMetricsFeatureProviderImpl.java b/src/com/android/settings/accessibility/AccessibilityPageIdFeatureProviderImpl.java
similarity index 75%
rename from src/com/android/settings/accessibility/AccessibilityMetricsFeatureProviderImpl.java
rename to src/com/android/settings/accessibility/AccessibilityPageIdFeatureProviderImpl.java
index 0f85f38..acd8aab 100644
--- a/src/com/android/settings/accessibility/AccessibilityMetricsFeatureProviderImpl.java
+++ b/src/com/android/settings/accessibility/AccessibilityPageIdFeatureProviderImpl.java
@@ -19,14 +19,16 @@
 import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
 
+import androidx.annotation.Nullable;
+
 /**
- * Provider implementation for Accessibility metrics related features.
+ * Provider implementation for Accessibility page id related features.
  */
-public class AccessibilityMetricsFeatureProviderImpl implements
-        AccessibilityMetricsFeatureProvider {
+public class AccessibilityPageIdFeatureProviderImpl implements
+        AccessibilityPageIdFeatureProvider {
 
     @Override
-    public int getDownloadedFeatureMetricsCategory(ComponentName componentName) {
+    public int getCategory(@Nullable ComponentName componentName) {
         return SettingsEnums.ACCESSIBILITY_SERVICE;
     }
 }
diff --git a/src/com/android/settings/accessibility/AccessibilityServicePreference.java b/src/com/android/settings/accessibility/AccessibilityServicePreference.java
index 8a22d82..7032774 100644
--- a/src/com/android/settings/accessibility/AccessibilityServicePreference.java
+++ b/src/com/android/settings/accessibility/AccessibilityServicePreference.java
@@ -123,13 +123,12 @@
         final String settingsClassName = mA11yServiceInfo.getSettingsActivityName();
         final String tileServiceClassName = mA11yServiceInfo.getTileServiceName();
         final ResolveInfo resolveInfo = mA11yServiceInfo.getResolveInfo();
-        final int metricsCategory = FeatureFactory.getFeatureFactory()
-                .getAccessibilityMetricsFeatureProvider()
-                .getDownloadedFeatureMetricsCategory(mComponentName);
+        final int pageIdCategory = FeatureFactory.getFeatureFactory()
+                .getAccessibilityPageIdFeatureProvider().getCategory(mComponentName);
         ThreadUtils.getUiThreadHandler().post(() -> {
             RestrictedPreferenceHelper.putBasicExtras(
                     this, prefKey, getTitle(), intro, description, imageRes,
-                    htmlDescription, mComponentName, metricsCategory);
+                    htmlDescription, mComponentName, pageIdCategory);
             RestrictedPreferenceHelper.putServiceExtras(this, resolveInfo, mServiceEnabled);
             RestrictedPreferenceHelper.putSettingsExtras(this, getPackageName(), settingsClassName);
             RestrictedPreferenceHelper.putTileServiceExtras(
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index 57eb4d5..2c8247f 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -97,6 +97,7 @@
     static final String EXTRA_HTML_DESCRIPTION = "html_description";
     static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool";
     static final String EXTRA_METRICS_CATEGORY = "metrics_category";
+    static final String EXTRA_FEEDBACK_CATEGORY = "feedback_category";
 
     // Timeout before we update the services if packages are added/removed
     // since the AccessibilityManagerService has to do that processing first
@@ -255,7 +256,7 @@
     public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
         if (getFeedbackManager().isAvailable()) {
             menu.add(Menu.NONE, MENU_ID_SEND_FEEDBACK, Menu.NONE,
-                    getPrefContext().getText(R.string.accessibility_send_feedback_title));
+                    R.string.accessibility_send_feedback_title);
         }
         super.onCreateOptionsMenu(menu, inflater);
     }
@@ -286,7 +287,7 @@
 
     private FeedbackManager getFeedbackManager() {
         if (mFeedbackManager == null) {
-            mFeedbackManager = new FeedbackManager(getActivity());
+            mFeedbackManager = new FeedbackManager(getActivity(), SettingsEnums.ACCESSIBILITY);
         }
         return mFeedbackManager;
     }
diff --git a/src/com/android/settings/accessibility/FeedbackManager.java b/src/com/android/settings/accessibility/FeedbackManager.java
index 52aefd2..dc4baa7 100644
--- a/src/com/android/settings/accessibility/FeedbackManager.java
+++ b/src/com/android/settings/accessibility/FeedbackManager.java
@@ -16,7 +16,6 @@
 package com.android.settings.accessibility;
 
 import android.app.Activity;
-import android.content.ComponentName;
 import android.content.Intent;
 import android.text.TextUtils;
 
@@ -46,23 +45,14 @@
      * Constructs a new FeedbackManager.
      *
      * @param activity The activity context. A WeakReference is used to prevent memory leaks.
+     * @param pageId The unique identifier of the page associated with the feedback.
      */
-    public FeedbackManager(@Nullable Activity activity) {
-        this(activity, /* componentName= */ null);
-    }
-
-    /**
-     * Constructs a new FeedbackManager.
-     *
-     * @param activity The activity context. A WeakReference is used to prevent memory leaks.
-     * @param componentName The component name associated with the feedback.
-     */
-    public FeedbackManager(@Nullable Activity activity, @Nullable ComponentName componentName) {
+    public FeedbackManager(@Nullable Activity activity, int pageId) {
         this(activity,
                 DeviceInfoUtils.getFeedbackReporterPackage(activity),
                 FeatureFactory.getFeatureFactory()
                         .getAccessibilityFeedbackFeatureProvider()
-                        .getCategory(componentName));
+                        .getCategory(pageId));
     }
 
     /**
diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
index 013fdee..c6995b0 100644
--- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
@@ -31,8 +31,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
@@ -60,6 +58,11 @@
     }
 
     @Override
+    public int getFeedbackCategory() {
+        return getArguments().getInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY);
+    }
+
+    @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         // Init new preference to replace the switch preference instead.
@@ -115,13 +118,6 @@
         return mTileComponentName;
     }
 
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        // Do not call super. We don't want to see the "Help & feedback" option on this page so as
-        // not to confuse users who think they might be able to send feedback about a specific
-        // accessibility service from this page.
-    }
-
     // IMPORTANT: Refresh the info since there are dynamically changing capabilities.
     private AccessibilityShortcutInfo getAccessibilityShortcutInfo() {
         final List<AccessibilityShortcutInfo> infos = AccessibilityManager.getInstance(
diff --git a/src/com/android/settings/accessibility/MagnificationCursorFollowingModePreferenceController.java b/src/com/android/settings/accessibility/MagnificationCursorFollowingModePreferenceController.java
new file mode 100644
index 0000000..d217ead
--- /dev/null
+++ b/src/com/android/settings/accessibility/MagnificationCursorFollowingModePreferenceController.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2025 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.accessibility;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.provider.Settings;
+import android.provider.Settings.Secure.AccessibilityMagnificationCursorFollowingMode;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.util.Preconditions;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.DialogCreatable;
+import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controller that shows the magnification cursor following mode and the preference click behavior.
+ */
+public class MagnificationCursorFollowingModePreferenceController extends
+        BasePreferenceController implements DialogCreatable {
+    static final String PREF_KEY =
+            Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE;
+
+    private static final String TAG =
+            MagnificationCursorFollowingModePreferenceController.class.getSimpleName();
+
+    private final List<ModeInfo> mModeList = new ArrayList<>();
+    @Nullable
+    private DialogHelper mDialogHelper;
+    @VisibleForTesting
+    @Nullable
+    ListView mModeListView;
+    @Nullable
+    private Preference mModePreference;
+
+    public MagnificationCursorFollowingModePreferenceController(@NonNull Context context,
+            @NonNull String preferenceKey) {
+        super(context, preferenceKey);
+        initModeList();
+    }
+
+    public void setDialogHelper(@NonNull DialogHelper dialogHelper) {
+        mDialogHelper = dialogHelper;
+    }
+
+    private void initModeList() {
+        mModeList.add(new ModeInfo(mContext.getString(
+                R.string.accessibility_magnification_cursor_following_continuous),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CONTINUOUS));
+        mModeList.add(new ModeInfo(
+                mContext.getString(R.string.accessibility_magnification_cursor_following_center),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CENTER));
+        mModeList.add(new ModeInfo(
+                mContext.getString(R.string.accessibility_magnification_cursor_following_edge),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE));
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @NonNull
+    @Override
+    public CharSequence getSummary() {
+        return getCursorFollowingModeSummary(getCurrentMagnificationCursorFollowingMode());
+    }
+
+    @Override
+    public void displayPreference(@NonNull PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mModePreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(@NonNull Preference preference) {
+        if (!TextUtils.equals(preference.getKey(), getPreferenceKey()) || mModePreference == null) {
+            return super.handlePreferenceTreeClick(preference);
+        }
+
+        Preconditions.checkNotNull(mDialogHelper).showDialog(
+                    DialogEnums.DIALOG_MAGNIFICATION_CURSOR_FOLLOWING_MODE);
+        return true;
+    }
+
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(int dialogId) {
+        Preconditions.checkArgument(
+                dialogId == DialogEnums.DIALOG_MAGNIFICATION_CURSOR_FOLLOWING_MODE,
+                "This only handles cursor following mode dialog");
+        return createMagnificationCursorFollowingModeDialog();
+    }
+
+    @Override
+    public int getDialogMetricsCategory(int dialogId) {
+        Preconditions.checkArgument(
+                dialogId == DialogEnums.DIALOG_MAGNIFICATION_CURSOR_FOLLOWING_MODE,
+                "This only handles cursor following mode dialog");
+        return SettingsEnums.DIALOG_MAGNIFICATION_CURSOR_FOLLOWING;
+    }
+
+    @NonNull
+    private Dialog createMagnificationCursorFollowingModeDialog() {
+        mModeListView = AccessibilityDialogUtils.createSingleChoiceListView(mContext, mModeList,
+                /* itemListener= */null);
+        final View headerView = LayoutInflater.from(mContext).inflate(
+                R.layout.accessibility_dialog_header, mModeListView,
+                /* attachToRoot= */false);
+        final TextView textView = Preconditions.checkNotNull(headerView.findViewById(
+                R.id.accessibility_dialog_header_text_view));
+        textView.setText(
+                mContext.getString(R.string.accessibility_magnification_cursor_following_header));
+        textView.setVisibility(View.VISIBLE);
+        mModeListView.addHeaderView(headerView, /* data= */null, /* isSelectable= */false);
+        final int selectionIndex = computeSelectionIndex();
+        if (selectionIndex != AdapterView.INVALID_POSITION) {
+            mModeListView.setItemChecked(selectionIndex, /* value= */true);
+        }
+        final CharSequence title = mContext.getString(
+                R.string.accessibility_magnification_cursor_following_title);
+        final CharSequence positiveBtnText = mContext.getString(R.string.save);
+        final CharSequence negativeBtnText = mContext.getString(R.string.cancel);
+        return AccessibilityDialogUtils.createCustomDialog(mContext, title, mModeListView,
+                positiveBtnText,
+                this::onMagnificationCursorFollowingModeDialogPositiveButtonClicked,
+                negativeBtnText, /* negativeListener= */null);
+    }
+
+    void onMagnificationCursorFollowingModeDialogPositiveButtonClicked(
+            DialogInterface dialogInterface, int which) {
+        ListView listView = Preconditions.checkNotNull(mModeListView);
+        final int selectionIndex = listView.getCheckedItemPosition();
+        if (selectionIndex == AdapterView.INVALID_POSITION) {
+            Log.w(TAG, "Selected positive button with INVALID_POSITION index");
+            return;
+        }
+        ModeInfo cursorFollowingMode = (ModeInfo) listView.getItemAtPosition(selectionIndex);
+        if (cursorFollowingMode != null) {
+            Preconditions.checkNotNull(mModePreference).setSummary(
+                    getCursorFollowingModeSummary(cursorFollowingMode.mMode));
+            Settings.Secure.putInt(mContext.getContentResolver(), PREF_KEY,
+                    cursorFollowingMode.mMode);
+        }
+    }
+
+    private int computeSelectionIndex() {
+        ListView listView = Preconditions.checkNotNull(mModeListView);
+        @AccessibilityMagnificationCursorFollowingMode
+        final int currentMode = getCurrentMagnificationCursorFollowingMode();
+        for (int i = 0; i < listView.getCount(); i++) {
+            final ModeInfo mode = (ModeInfo) listView.getItemAtPosition(i);
+            if (mode != null && mode.mMode == currentMode) {
+                return i;
+            }
+        }
+        return AdapterView.INVALID_POSITION;
+    }
+
+    @NonNull
+    private CharSequence getCursorFollowingModeSummary(
+            @AccessibilityMagnificationCursorFollowingMode int cursorFollowingMode) {
+        int stringId = switch (cursorFollowingMode) {
+            case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CENTER ->
+                    R.string.accessibility_magnification_cursor_following_center;
+            case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE ->
+                    R.string.accessibility_magnification_cursor_following_edge;
+            default ->
+                    R.string.accessibility_magnification_cursor_following_continuous;
+        };
+        return mContext.getString(stringId);
+    }
+
+    private @AccessibilityMagnificationCursorFollowingMode int
+            getCurrentMagnificationCursorFollowingMode() {
+        return Settings.Secure.getInt(mContext.getContentResolver(), PREF_KEY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CONTINUOUS);
+    }
+
+    static class ModeInfo extends ItemInfoArrayAdapter.ItemInfo {
+        @AccessibilityMagnificationCursorFollowingMode
+        public final int mMode;
+
+        ModeInfo(@NonNull CharSequence title,
+                @AccessibilityMagnificationCursorFollowingMode int mode) {
+            super(title, /* summary= */null, /* drawableId= */null);
+            mMode = mode;
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/MagnificationModePreferenceController.java b/src/com/android/settings/accessibility/MagnificationModePreferenceController.java
index 71ea4c2..93cb23b 100644
--- a/src/com/android/settings/accessibility/MagnificationModePreferenceController.java
+++ b/src/com/android/settings/accessibility/MagnificationModePreferenceController.java
@@ -176,8 +176,12 @@
                 mContext, mModeInfos, this::onMagnificationModeSelected);
 
         final View headerView = LayoutInflater.from(mContext).inflate(
-                R.layout.accessibility_magnification_mode_header,
-                getMagnificationModesListView(), /* attachToRoot= */false);
+                R.layout.accessibility_dialog_header, getMagnificationModesListView(),
+                /* attachToRoot= */false);
+        final TextView textView = Preconditions.checkNotNull(headerView.findViewById(
+                R.id.accessibility_dialog_header_text_view));
+        textView.setText(
+                mContext.getString(R.string.accessibility_magnification_area_settings_message));
         getMagnificationModesListView().addHeaderView(headerView, /* data= */null,
                 /* isSelectable= */false);
 
diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
index 5c18be8..ae6239a 100644
--- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
+++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java
@@ -217,6 +217,7 @@
         extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, imageRes);
         extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
         extras.putInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY, metricsCategory);
+        extras.putInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY, metricsCategory);
     }
 
     /**
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index 06bcdb7..a11ad46 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -39,8 +39,6 @@
 import android.text.BidiFormatter;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.CompoundButton;
 
@@ -76,10 +74,8 @@
     }
 
     @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        // Do not call super. We don't want to see the "Help & feedback" option on this page so as
-        // not to confuse users who think they might be able to send feedback about a specific
-        // accessibility service from this page.
+    public int getFeedbackCategory() {
+        return getArguments().getInt(AccessibilitySettings.EXTRA_FEEDBACK_CATEGORY);
     }
 
     @Override
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 9367251..66c32df 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -40,6 +40,9 @@
 import android.text.Html;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
@@ -48,6 +51,7 @@
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -89,6 +93,7 @@
     // <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
     private static final String IMG_PREFIX = "R.drawable.";
     private static final String DRAWABLE_FOLDER = "drawable";
+    static final int MENU_ID_SEND_FEEDBACK = 0;
 
     protected TopIntroPreference mTopIntroPreference;
     protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
@@ -102,6 +107,7 @@
     protected Intent mSettingsIntent;
     // The mComponentName maybe null, such as Magnify
     protected ComponentName mComponentName;
+    @Nullable private FeedbackManager mFeedbackManager;
     protected CharSequence mFeatureName;
     protected Uri mImageUri;
     protected CharSequence mHtmlDescription;
@@ -241,6 +247,24 @@
     }
 
     @Override
+    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
+        if (getFeedbackManager().isAvailable()) {
+            menu.add(Menu.NONE, MENU_ID_SEND_FEEDBACK, Menu.NONE,
+                    R.string.accessibility_send_feedback_title);
+        }
+        super.onCreateOptionsMenu(menu, inflater);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+        if (item.getItemId() == MENU_ID_SEND_FEEDBACK) {
+            getFeedbackManager().sendFeedback();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
     public int getDialogMetricsCategory(int dialogId) {
         switch (dialogId) {
             case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
@@ -739,4 +763,28 @@
                 super.onCreateRecyclerView(inflater, parent, savedInstanceState);
         return AccessibilityFragmentUtils.addCollectionInfoToAccessibilityDelegate(recyclerView);
     }
+
+    @VisibleForTesting
+    void setFeedbackManager(FeedbackManager feedbackManager) {
+        this.mFeedbackManager = feedbackManager;
+    }
+
+    private FeedbackManager getFeedbackManager() {
+        if (mFeedbackManager == null) {
+            mFeedbackManager = new FeedbackManager(getActivity(), getFeedbackCategory());
+        }
+        return mFeedbackManager;
+    }
+
+    /**
+     * Returns the category of the feedback page.
+     *
+     * <p>By default, this method returns {@link SettingsEnums#PAGE_UNKNOWN}. This indicates that
+     * the feedback category is unknown, and the absence of a feedback menu.
+     *
+     * @return The feedback category, which is {@link SettingsEnums#PAGE_UNKNOWN} by default.
+     */
+    protected int getFeedbackCategory() {
+        return SettingsEnums.PAGE_UNKNOWN;
+    }
 }
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 8b52507..71c95c0 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -93,6 +93,8 @@
     private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
     @Nullable
     private DialogCreatable mMagnificationModeDialogDelegate;
+    @Nullable
+    private DialogCreatable mMagnificationCursorFollowingModeDialogDelegate;
 
     @Nullable
     MagnificationOneFingerPanningPreferenceController mOneFingerPanningPreferenceController;
@@ -104,6 +106,12 @@
         mMagnificationModeDialogDelegate = delegate;
     }
 
+    @VisibleForTesting
+    public void setMagnificationCursorFollowingModeDialogDelegate(
+            @NonNull DialogCreatable delegate) {
+        mMagnificationCursorFollowingModeDialogDelegate = delegate;
+    }
+
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -186,6 +194,9 @@
             case DialogEnums.DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING:
                 return Preconditions.checkNotNull(mMagnificationModeDialogDelegate)
                         .onCreateDialog(dialogId);
+            case DialogEnums.DIALOG_MAGNIFICATION_CURSOR_FOLLOWING_MODE:
+                return Preconditions.checkNotNull(mMagnificationCursorFollowingModeDialogDelegate)
+                        .onCreateDialog(dialogId);
             case DialogEnums.GESTURE_NAVIGATION_TUTORIAL:
                 return AccessibilityShortcutsTutorial
                         .showAccessibilityGestureTutorialDialog(getPrefContext());
@@ -201,6 +212,11 @@
                 PackageManager.FEATURE_WINDOW_MAGNIFICATION);
     }
 
+    private static boolean isMagnificationCursorFollowingModeDialogSupported() {
+        // TODO(b/398066000): Hide the setting when no pointer device exists for most form factors.
+        return com.android.settings.accessibility.Flags.enableMagnificationCursorFollowingDialog();
+    }
+
     @Override
     protected void initSettingsPreference() {
         final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
@@ -213,6 +229,7 @@
             addJoystickSetting(generalCategory);
             // LINT.ThenChange(:search_data)
         }
+        addCursorFollowingSetting(generalCategory);
         addFeedbackSetting(generalCategory);
     }
 
@@ -286,6 +303,31 @@
         addPreferenceController(magnificationModePreferenceController);
     }
 
+    private static Preference createCursorFollowingPreference(Context context) {
+        final Preference pref = new Preference(context);
+        pref.setTitle(R.string.accessibility_magnification_cursor_following_title);
+        pref.setKey(MagnificationCursorFollowingModePreferenceController.PREF_KEY);
+        pref.setPersistent(false);
+        return pref;
+    }
+
+    private void addCursorFollowingSetting(PreferenceCategory generalCategory) {
+        if (!isMagnificationCursorFollowingModeDialogSupported()) {
+            return;
+        }
+
+        generalCategory.addPreference(createCursorFollowingPreference(getPrefContext()));
+
+        final MagnificationCursorFollowingModePreferenceController controller =
+                new MagnificationCursorFollowingModePreferenceController(
+                        getContext(),
+                        MagnificationCursorFollowingModePreferenceController.PREF_KEY);
+        controller.setDialogHelper(/* dialogHelper= */this);
+        mMagnificationCursorFollowingModeDialogDelegate = controller;
+        controller.displayPreference(getPreferenceScreen());
+        addPreferenceController(controller);
+    }
+
     private static Preference createFollowTypingPreference(Context context) {
         final Preference pref = new SwitchPreferenceCompat(context);
         pref.setTitle(R.string.accessibility_screen_magnification_follow_typing_title);
@@ -510,6 +552,9 @@
             case DialogEnums.DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING:
                 return Preconditions.checkNotNull(mMagnificationModeDialogDelegate)
                         .getDialogMetricsCategory(dialogId);
+            case DialogEnums.DIALOG_MAGNIFICATION_CURSOR_FOLLOWING_MODE:
+                return Preconditions.checkNotNull(mMagnificationCursorFollowingModeDialogDelegate)
+                        .getDialogMetricsCategory(dialogId);
             case DialogEnums.GESTURE_NAVIGATION_TUTORIAL:
                 return SettingsEnums.DIALOG_TOGGLE_SCREEN_MAGNIFICATION_GESTURE_NAVIGATION;
             case DialogEnums.ACCESSIBILITY_BUTTON_TUTORIAL:
@@ -667,6 +712,11 @@
                         return rawData;
                     }
 
+                    // Add all preferences to search raw data so that they are included in
+                    // indexing, which happens infrequently. Irrelevant preferences should be
+                    // hidden from the live returned search results by `getNonIndexableKeys`,
+                    // which is called every time a search occurs. This allows for dynamic search
+                    // entries that hide or show depending on current device state.
                     rawData.add(createShortcutPreferenceSearchData(context));
                     Stream.of(
                                     createMagnificationModePreference(context),
@@ -674,6 +724,7 @@
                                     createOneFingerPanningPreference(context),
                                     createAlwaysOnPreference(context),
                                     createJoystickPreference(context),
+                                    createCursorFollowingPreference(context),
                                     createFeedbackPreference(context)
                             )
                             .forEach(pref ->
@@ -714,6 +765,10 @@
                         }
                     }
 
+                    if (!isMagnificationCursorFollowingModeDialogSupported()) {
+                        niks.add(MagnificationCursorFollowingModePreferenceController.PREF_KEY);
+                    }
+
                     if (!Flags.enableLowVisionHats()) {
                         niks.add(MagnificationFeedbackPreferenceController.PREF_KEY);
                     }
diff --git a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
index 10813a7..eb0c93b 100644
--- a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
@@ -80,6 +80,12 @@
     }
 
     @Override
+    public int getFeedbackCategory() {
+        // The feedback options should not be displayed on the setup wizard page.
+        return SettingsEnums.PAGE_UNKNOWN;
+    }
+
+    @Override
     public void onStop() {
         // Log the final choice in value if it's different from the previous value.
         if (mToggleServiceSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
diff --git a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
index 10796b5..14dc0bc 100644
--- a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
@@ -80,6 +80,12 @@
     }
 
     @Override
+    public int getFeedbackCategory() {
+        // The feedback options should not be displayed on the setup wizard page.
+        return SettingsEnums.PAGE_UNKNOWN;
+    }
+
+    @Override
     public void onStop() {
         // Log the final choice in value if it's different from the previous value.
         if (mToggleServiceSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
diff --git a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
index 826583d..ad1f823 100644
--- a/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
+++ b/src/com/android/settings/applications/appinfo/ExternalSourcesDetails.java
@@ -95,8 +95,7 @@
                     userHandle)) {
                 if (RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(context,
                         DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userHandle.getIdentifier())) {
-                    return context.getString(com.android.settingslib.widget.restricted
-                            .R.string.disabled_by_advanced_protection);
+                    return context.getString(com.android.settingslib.R.string.disabled);
                 } else {
                     return context.getString(
                             com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
index 8cc7d6a..42029ff 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListener.java
@@ -28,6 +28,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.settingslib.utils.ThreadUtils;
@@ -76,17 +77,22 @@
         mContentKey = contentKey;
         String authority = new ActiveUnlockStatusUtils(mContext).getAuthority();
         if (authority != null) {
-            mUri = new Uri.Builder()
-                    .scheme(ContentResolver.SCHEME_CONTENT)
-                    .authority(authority)
-                    .appendPath(CONTENT_PROVIDER_PATH)
-                    .build();
+            mUri = getUri(authority);
         } else {
             mUri = null;
         }
 
     }
 
+    /** Returns Active Unlock Uri. */
+    public static @NonNull Uri getUri(@NonNull String authority) {
+        return new Uri.Builder()
+                    .scheme(ContentResolver.SCHEME_CONTENT)
+                    .authority(authority)
+                    .appendPath(CONTENT_PROVIDER_PATH)
+                    .build();
+    }
+
     /** Returns true if start listening for updates from the ContentProvider, false otherwise. */
     public synchronized boolean subscribe() {
         if (mSubscribed || mUri == null) {
@@ -123,25 +129,40 @@
             Log.e(mLogTag, "Uri null when trying to fetch content");
             return;
         }
-        ContentResolver contentResolver = mContext.getContentResolver();
-        ContentProviderClient client = contentResolver.acquireContentProviderClient(mUri);
-        Bundle bundle;
-        try {
-            bundle = client.call(mMethodName, null /* arg */, null /* extras */);
-        } catch (RemoteException e) {
-            Log.e(mLogTag, "Failed to call contentProvider", e);
-            return;
-        } finally {
-            client.close();
-        }
-        if (bundle == null) {
-            Log.e(mLogTag, "Null bundle returned from contentProvider");
-            return;
-        }
-        String newValue = bundle.getString(mContentKey);
+
+        @Nullable String newValue = getContentFromUri(
+            mContext, mUri, mLogTag, mMethodName, mContentKey);
         if (!TextUtils.equals(mContent, newValue)) {
             mContent = newValue;
             mContentChangedListener.onContentChanged(mContent);
         }
     }
+
+    /** Get the content from Uri. */
+    public static @Nullable String getContentFromUri(
+            @NonNull Context context,
+            @NonNull Uri uri,
+            @NonNull String logTag,
+            @NonNull String methodName,
+            @NonNull String contentKey) {
+        ContentResolver contentResolver = context.getContentResolver();
+        ContentProviderClient client = contentResolver.acquireContentProviderClient(uri);
+
+        @Nullable Bundle bundle = null;
+
+        try {
+            bundle = client.call(methodName, /* arg= */ null, /* extras = */ null);
+        } catch (RemoteException e) {
+            Log.e(logTag, "Failed to call contentProvider", e);
+        } finally {
+            client.close();
+        }
+
+        if (bundle == null) {
+            Log.e(logTag, "Null bundle returned from contentProvider");
+            return null;
+        }
+
+        return bundle.getString(contentKey);
+    }
 }
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
index 1badb0f..9e81762 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockDeviceNameListener.java
@@ -21,8 +21,8 @@
 /** Listens to device name updates from the content provider and fetches the latest value. */
 public class ActiveUnlockDeviceNameListener  {
     private static final String TAG = "ActiveUnlockDeviceNameListener";
-    private static final String METHOD_NAME = "getDeviceName";
-    private static final String DEVICE_NAME_KEY = "com.android.settings.active_unlock.device_name";
+    static final String METHOD_NAME = "getDeviceName";
+    static final String DEVICE_NAME_KEY = "com.android.settings.active_unlock.device_name";
 
     private final ActiveUnlockContentListener mActiveUnlockContentListener;
     public ActiveUnlockDeviceNameListener(
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
index 4ff2e90..66485d3 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
@@ -156,9 +156,16 @@
     }
 
     /**
+     * Returns the title of active unlock only.
+     */
+    public @NonNull String getTitleForActiveUnlockOnly() {
+        return mContext.getString(R.string.security_settings_activeunlock);
+    }
+
+    /**
      * Returns the title of the combined biometric settings entity when active unlock is enabled.
      */
-    public String getTitleForActiveUnlock() {
+    public @NonNull String getTitleForActiveUnlock() {
         final boolean faceAllowed = Utils.hasFaceHardware(mContext);
         final boolean fingerprintAllowed = Utils.hasFingerprintHardware(mContext);
         return mContext.getString(getTitleRes(faceAllowed, fingerprintAllowed));
@@ -264,6 +271,30 @@
         return mContext.getString(getUseBiometricTitleRes(faceAllowed, fingerprintAllowed));
     }
 
+    /**
+     * Returns the summary from content provider.
+     */
+    @Nullable
+    public static String getSummaryFromContentProvider(
+            @NonNull Context context, @NonNull String authority, @NonNull String logTag) {
+        return ActiveUnlockContentListener.getContentFromUri(
+            context, ActiveUnlockContentListener.getUri(authority), logTag,
+            ActiveUnlockSummaryListener.METHOD_NAME,
+            ActiveUnlockSummaryListener.SUMMARY_KEY);
+    }
+
+    /**
+     * Returns the device name from content provider.
+     */
+    @Nullable
+    public static String getDeviceNameFromContentProvider(
+            @NonNull Context context, @NonNull String authority, @NonNull String logTag) {
+        return ActiveUnlockContentListener.getContentFromUri(
+            context, ActiveUnlockContentListener.getUri(authority), logTag,
+            ActiveUnlockDeviceNameListener.METHOD_NAME,
+            ActiveUnlockDeviceNameListener.DEVICE_NAME_KEY);
+    }
+
     @StringRes
     private static int getUseBiometricTitleRes(
             boolean isFaceAllowed, boolean isFingerprintAllowed) {
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java
index bcffe62..38e137b 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockSummaryListener.java
@@ -21,8 +21,8 @@
 /** Listens to summary updates from the content provider and fetches the latest value. */
 public class ActiveUnlockSummaryListener {
     private static final String TAG = "ActiveUnlockSummaryListener";
-    private static final String METHOD_NAME = "getSummary";
-    private static final String SUMMARY_KEY = "com.android.settings.summary";
+    static final String METHOD_NAME = "getSummary";
+    static final String SUMMARY_KEY = "com.android.settings.summary";
 
     private final ActiveUnlockContentListener mContentListener;
     public ActiveUnlockSummaryListener(
diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
index 1d8b7a1..ed4b713 100644
--- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -66,7 +66,7 @@
     @VisibleForTesting
     static final int CONFIRM_REQUEST = 2001;
     private static final int CHOOSE_LOCK_REQUEST = 2002;
-    protected static final int ACTIVE_UNLOCK_REQUEST = 2003;
+    public static final int ACTIVE_UNLOCK_REQUEST = 2003;
     @VisibleForTesting
     static final int BIOMETRIC_AUTH_REQUEST = 2004;
 
diff --git a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
index 700b601..a145913 100644
--- a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
+++ b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java
@@ -25,11 +25,11 @@
 
 import com.android.settings.R;
 import com.android.settings.core.SliderPreferenceController;
-import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.widget.SliderPreference;
 
 public class NightDisplayIntensityPreferenceController extends SliderPreferenceController {
 
-    private ColorDisplayManager mColorDisplayManager;
+    private final ColorDisplayManager mColorDisplayManager;
 
     public NightDisplayIntensityPreferenceController(Context context, String key) {
         super(context, key);
@@ -64,11 +64,11 @@
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
-        final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
-        preference.setContinuousUpdates(true);
+        SliderPreference preference = screen.findPreference(getPreferenceKey());
+        preference.setUpdatesContinuously(true);
         preference.setMax(getMax());
         preference.setMin(getMin());
-        preference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
+        // TODO(b/394828723) add haptic feedback
     }
 
     @Override
diff --git a/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceController.java b/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceController.java
new file mode 100644
index 0000000..bb91b3c
--- /dev/null
+++ b/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceController.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025 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.inputmethod;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.hardware.input.InputSettings;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.SliderPreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+public class MousePointerSpeedPreferenceController extends SliderPreferenceController {
+
+    private final MetricsFeatureProvider mMetricsFeatureProvider;
+
+    public MousePointerSpeedPreferenceController(@NonNull Context context, @NonNull String key) {
+        super(context, key);
+        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        SeekBarPreference preference = screen.findPreference(getPreferenceKey());
+        preference.setMax(getMax());
+        preference.setMin(getMin());
+        preference.setProgress(getSliderPosition());
+        updateState(preference);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public boolean setSliderPosition(int position) {
+        if (position < getMin() || position > getMax()) {
+            return false;
+        }
+        InputSettings.setPointerSpeed(mContext, position);
+        mMetricsFeatureProvider.action(
+                mContext, SettingsEnums.ACTION_GESTURE_POINTER_SPEED_CHANGED, position);
+        return true;
+    }
+
+    @Override
+    public int getSliderPosition() {
+        return InputSettings.getPointerSpeed(mContext);
+    }
+
+    @Override
+    public int getMin() {
+        return InputSettings.MIN_POINTER_SPEED;
+    }
+
+    @Override
+    public int getMax() {
+        return InputSettings.MAX_POINTER_SPEED;
+    }
+}
diff --git a/src/com/android/settings/localepicker/LocaleDialogFragment.java b/src/com/android/settings/localepicker/LocaleDialogFragment.java
index a3a4b8f..5c7958a 100644
--- a/src/com/android/settings/localepicker/LocaleDialogFragment.java
+++ b/src/com/android/settings/localepicker/LocaleDialogFragment.java
@@ -26,11 +26,6 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
 import android.window.OnBackInvokedCallback;
 import android.window.OnBackInvokedDispatcher;
 
@@ -57,6 +52,7 @@
     static final String ARG_DIALOG_TYPE = "arg_dialog_type";
     static final String ARG_TARGET_LOCALE = "arg_target_locale";
     static final String ARG_SHOW_DIALOG = "arg_show_dialog";
+    static final String ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED = "arg_show_dialog_for_not_translated";
 
     private boolean mShouldKeepDialog;
     private OnBackInvokedDispatcher mBackDispatcher;
@@ -185,6 +181,7 @@
         private final int mDialogType;
         private final LocaleStore.LocaleInfo mLocaleInfo;
         private final MetricsFeatureProvider mMetricsFeatureProvider;
+        private final boolean mShowDialogForNotTranslated;
 
         private LocaleListEditor mParent;
 
@@ -194,6 +191,7 @@
             mContext = context;
             Bundle arguments = dialogFragment.getArguments();
             mDialogType = arguments.getInt(ARG_DIALOG_TYPE);
+            mShowDialogForNotTranslated = arguments.getBoolean(ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED);
             mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(ARG_TARGET_LOCALE);
             mMetricsFeatureProvider =
                     FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
@@ -215,6 +213,7 @@
                 bundle.putInt(ARG_DIALOG_TYPE, mDialogType);
                 bundle.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, mLocaleInfo);
                 intent.putExtras(bundle);
+                intent.putExtra(ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED, mShowDialogForNotTranslated);
                 mParent.onActivityResult(mDialogType, result, intent);
                 mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CHANGE_LANGUAGE,
                         changed);
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index 907fe7b..af8b668 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -364,12 +364,25 @@
     }
 
     public void notifyListChanged(LocaleStore.LocaleInfo localeInfo) {
-        if (!localeInfo.getLocale().equals(mCacheItemList.get(0).getLocale())) {
+        if (listChanged()) {
             mFeedItemList = new ArrayList<>(mCacheItemList);
             notifyDataSetChanged();
         }
     }
 
+    private boolean listChanged() {
+        if (mFeedItemList.size() == mCacheItemList.size()) {
+            for (int i = 0; i < mFeedItemList.size(); i++) {
+                if (!mFeedItemList.get(i).getLocale().equals(mCacheItemList.get(i).getLocale())) {
+                    return true;
+                }
+            }
+            return false;
+        } else {
+            return true;
+        }
+    }
+
     public void setCacheItemList() {
         mCacheItemList = new ArrayList<>(mFeedItemList);
     }
diff --git a/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
index a7ebe32..df0af63 100644
--- a/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
+++ b/src/com/android/settings/localepicker/LocaleLinearLayoutManager.java
@@ -151,7 +151,7 @@
         }
 
         if (result) {
-            mLocaleListEditor.showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
+            mLocaleListEditor.showConfirmDialog(mAdapter.getFeedItemList().get(0), null);
         }
         return result;
     }
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index b1f005a..e2da851 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -44,6 +44,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.FragmentManager;
 import androidx.preference.Preference;
@@ -235,7 +236,9 @@
             localeInfo = mAdapter.getFeedItemList().get(0);
             if (resultCode == Activity.RESULT_OK) {
                 mAdapter.doTheUpdate();
-                if (!localeInfo.isTranslated()) {
+                boolean showNotTranslatedDialog = data.getBooleanExtra(
+                        LocaleDialogFragment.ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED, true);
+                if (showNotTranslatedDialog && !localeInfo.isTranslated()) {
                     Bundle args = new Bundle();
                     args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE,
                             LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE);
@@ -428,13 +431,10 @@
                             // to remove.
                             mRemoveMode = false;
                             mShowingRemoveDialog = false;
-                            LocaleStore.LocaleInfo firstLocale =
-                                    mAdapter.getFeedItemList().get(0);
+                            Locale defaultBeforeRemoval = Locale.getDefault();
                             mAdapter.removeChecked();
-                            boolean isFirstRemoved =
-                                    firstLocale != mAdapter.getFeedItemList().get(0);
-                            showConfirmDialog(isFirstRemoved, isFirstRemoved ? firstLocale
-                                    : mAdapter.getFeedItemList().get(0));
+                            showConfirmDialog(mAdapter.getFeedItemList().get(0),
+                                    defaultBeforeRemoval);
                             setRemoveMode(false);
                             dialogHelper.getDialog().dismiss();
                         })
@@ -520,27 +520,73 @@
     public boolean onTouch(View v, MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_UP
                 || event.getAction() == MotionEvent.ACTION_CANCEL) {
-            showConfirmDialog(false, mAdapter.getFeedItemList().get(0));
+            showConfirmDialog(mAdapter.getFeedItemList().get(0), null);
         }
         return false;
     }
 
-    public void showConfirmDialog(boolean isFirstRemoved, LocaleStore.LocaleInfo localeInfo) {
+    protected void showConfirmDialog(LocaleStore.LocaleInfo localeInfo,
+            @Nullable Locale defaultLocaleBeforeRemoval) {
         Locale currentSystemLocale = LocalePicker.getLocales().get(0);
         if (!localeInfo.getLocale().equals(currentSystemLocale)) {
-            final LocaleDialogFragment localeDialogFragment =
-                    LocaleDialogFragment.newInstance();
-            Bundle args = new Bundle();
-            args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
-            args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE,
-                    isFirstRemoved ? LocaleStore.getLocaleInfo(currentSystemLocale) : localeInfo);
-            localeDialogFragment.setArguments(args);
-            localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
+            displayDialogFragment(localeInfo, true);
         } else {
-            mAdapter.doTheUpdate();
+            if (!localeInfo.isTranslated()) {
+                if (defaultLocaleBeforeRemoval == null) {
+                    showDialogDueToDragAndDrop();
+                } else {
+                    showDialogDueToRemoval(defaultLocaleBeforeRemoval);
+                }
+            } else {
+                mAdapter.doTheUpdate();
+            }
         }
     }
 
+    private void showDialogDueToDragAndDrop() {
+        LocaleStore.LocaleInfo newLocale = mAdapter.getFeedItemList().stream().filter(
+                i -> i.isTranslated()).findFirst().orElse(null);
+        if (newLocale == null) {
+            return;
+        }
+        LocaleStore.LocaleInfo oldLocale = null;
+        final LocaleList localeList = LocalePicker.getLocales();
+        for (int i = 0; i < localeList.size(); i++) {
+            LocaleStore.LocaleInfo temp = LocaleStore.getLocaleInfo(localeList.get(i));
+            if (temp.isTranslated()) {
+                oldLocale = temp;
+                break;
+            }
+        }
+        if (oldLocale != null && !newLocale.getLocale().equals(
+                oldLocale.getLocale())) {
+            displayDialogFragment(newLocale, false);
+        }
+    }
+
+    private void showDialogDueToRemoval(Locale preDefault) {
+        if (preDefault == null) {
+            return;
+        }
+        LocaleStore.LocaleInfo currentDefault = mAdapter.getFeedItemList().stream().filter(
+                i -> i.isTranslated()).findFirst().orElse(null);
+        if (currentDefault != null && !preDefault.equals(currentDefault.getLocale())) {
+            displayDialogFragment(currentDefault, false);
+        }
+    }
+
+    private void displayDialogFragment(LocaleStore.LocaleInfo localeInfo,
+            boolean showDialogForNotTranslated) {
+        final LocaleDialogFragment localeDialogFragment = LocaleDialogFragment.newInstance();
+        Bundle args = new Bundle();
+        args.putBoolean(LocaleDialogFragment.ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED,
+                showDialogForNotTranslated);
+        args.putInt(LocaleDialogFragment.ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+        args.putSerializable(LocaleDialogFragment.ARG_TARGET_LOCALE, localeInfo);
+        localeDialogFragment.setArguments(args);
+        localeDialogFragment.show(mFragmentManager, TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT);
+    }
+
     // Hide the "Remove" menu if there is only one locale in the list, show it otherwise
     // This is called when the menu is first created, and then one add / remove locale
     private void updateVisibilityOfRemoveMenu() {
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 46aa19b..7e04f0d 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -17,7 +17,7 @@
 
 import android.content.Context
 import com.android.settings.accessibility.AccessibilityFeedbackFeatureProvider
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProvider
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProvider
 import com.android.settings.accessibility.AccessibilitySearchFeatureProvider
 import com.android.settings.accounts.AccountFeatureProvider
 import com.android.settings.applications.ApplicationFeatureProvider
@@ -145,9 +145,9 @@
     abstract val accessibilitySearchFeatureProvider: AccessibilitySearchFeatureProvider
 
     /**
-     * Retrieves implementation for Accessibility metrics category feature.
+     * Retrieves implementation for Accessibility page id category feature.
      */
-    abstract val accessibilityMetricsFeatureProvider: AccessibilityMetricsFeatureProvider
+    abstract val accessibilityPageIdFeatureProvider: AccessibilityPageIdFeatureProvider
 
     /**
      * Retrieves implementation for advanced vpn feature.
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index 08abf2b..4949c3f 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -22,8 +22,8 @@
 import android.os.UserManager
 import com.android.settings.accessibility.AccessibilityFeedbackFeatureProvider
 import com.android.settings.accessibility.AccessibilityFeedbackFeatureProviderImpl
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProvider
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProviderImpl
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProvider
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProviderImpl
 import com.android.settings.accessibility.AccessibilitySearchFeatureProvider
 import com.android.settings.accessibility.AccessibilitySearchFeatureProviderImpl
 import com.android.settings.accounts.AccountFeatureProvider
@@ -174,8 +174,8 @@
         AccessibilitySearchFeatureProviderImpl()
     }
 
-    override val accessibilityMetricsFeatureProvider: AccessibilityMetricsFeatureProvider by lazy {
-        AccessibilityMetricsFeatureProviderImpl()
+    override val accessibilityPageIdFeatureProvider: AccessibilityPageIdFeatureProvider by lazy {
+        AccessibilityPageIdFeatureProviderImpl()
     }
 
     override val advancedVpnFeatureProvider by lazy { AdvancedVpnFeatureProviderImpl() }
diff --git a/src/com/android/settings/safetycenter/LockScreenSafetySource.java b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
index 00a4c67..61f05f7 100644
--- a/src/com/android/settings/safetycenter/LockScreenSafetySource.java
+++ b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
@@ -131,6 +131,7 @@
         if (Flags.biometricsOnboardingEducation()) {
             FaceSafetySource.onBiometricsChanged(context);
             FingerprintSafetySource.onBiometricsChanged(context);
+            WearSafetySource.onBiometricsChanged(context);
         } else {
             BiometricsSafetySource.onBiometricsChanged(context);
         }
diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
index a49b7e0..4cf40dd 100644
--- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
+++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
@@ -86,6 +86,9 @@
         if (sourceIds.contains(FingerprintSafetySource.SAFETY_SOURCE_ID)) {
             FingerprintSafetySource.setSafetySourceData(context, safetyEvent);
         }
+        if (sourceIds.contains(WearSafetySource.SAFETY_SOURCE_ID)) {
+            WearSafetySource.setSafetySourceData(context, safetyEvent);
+        }
     }
 
     private static void refreshAllSafetySources(Context context, SafetyEvent safetyEvent) {
@@ -95,5 +98,6 @@
         PrivateSpaceSafetySource.setSafetySourceData(context, safetyEvent);
         FaceSafetySource.setSafetySourceData(context, safetyEvent);
         FingerprintSafetySource.setSafetySourceData(context, safetyEvent);
+        WearSafetySource.setSafetySourceData(context, safetyEvent);
     }
 }
diff --git a/src/com/android/settings/safetycenter/WearSafetySource.java b/src/com/android/settings/safetycenter/WearSafetySource.java
new file mode 100644
index 0000000..a345096
--- /dev/null
+++ b/src/com/android/settings/safetycenter/WearSafetySource.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2025 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.safetycenter;
+
+import static com.android.settings.biometrics.combination.BiometricsSettingsBase.ACTIVE_UNLOCK_REQUEST;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.UserManager;
+import android.safetycenter.SafetyEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
+import com.android.settings.flags.Flags;
+
+/** Wear Safety Source for Safety Center. */
+public final class WearSafetySource {
+
+    private static final String TAG = "WearSafetySource";
+    public static final String SAFETY_SOURCE_ID = "AndroidWearUnlock";
+    private static boolean sIsTestingEnv = false;
+    private static String sSummaryForTesting = "";
+    private static boolean sHasEnrolledForTesting;
+
+    private WearSafetySource() {}
+
+    /** Sets test value for summary. */
+    @VisibleForTesting
+    public static void setSummaryForTesting(@NonNull String summary) {
+        sIsTestingEnv = true;
+        sSummaryForTesting = summary;
+    }
+
+    /** Sets test value for hasEnrolled. */
+    @VisibleForTesting
+    public static void setHasEnrolledForTesting(boolean hasEnrolled) {
+        sIsTestingEnv = true;
+        sHasEnrolledForTesting = hasEnrolled;
+    }
+
+    /** Sets biometric safety data for Safety Center. */
+    public static void setSafetySourceData(
+            @NonNull Context context, @NonNull SafetyEvent safetyEvent) {
+        if (!SafetyCenterManagerWrapper.get().isEnabled(context)) {
+            return;
+        }
+        if (!Flags.biometricsOnboardingEducation()) { // this source is effectively turned off
+            sendNullData(context, safetyEvent);
+            return;
+        }
+
+        // Handle private profile case.
+        UserManager userManager = UserManager.get(context);
+        if (android.os.Flags.allowPrivateProfile()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures()
+                && userManager.isPrivateProfile()) {
+            // SC always expects a response from the source if the broadcast has been sent for this
+            // source, therefore, we need to send a null SafetySourceData.
+            sendNullData(context, safetyEvent);
+            return;
+        }
+
+        ActiveUnlockStatusUtils activeUnlockStatusUtils = new ActiveUnlockStatusUtils(context);
+        if (!userManager.isProfile() && activeUnlockStatusUtils.isAvailable()) {
+            boolean hasEnrolled = false;
+            String summary = "";
+
+            if (sIsTestingEnv) {
+                hasEnrolled = sHasEnrolledForTesting;
+                summary = sSummaryForTesting;
+            } else {
+                String authority = new ActiveUnlockStatusUtils(context).getAuthority();
+                hasEnrolled = getHasEnrolledFromContentProvider(context, authority);
+                summary = getSummaryFromContentProvider(context, authority);
+            }
+
+            BiometricSourcesUtils.setBiometricSafetySourceData(
+                    SAFETY_SOURCE_ID,
+                    context,
+                    activeUnlockStatusUtils.getTitleForActiveUnlockOnly(),
+                    summary,
+                    PendingIntent.getActivity(context, ACTIVE_UNLOCK_REQUEST,
+                            activeUnlockStatusUtils.getIntent(),
+                            PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT),
+                    /* enabled= */ true,
+                    hasEnrolled,
+                    safetyEvent);
+            return;
+        }
+
+        sendNullData(context, safetyEvent);
+    }
+
+    private static void sendNullData(Context context, SafetyEvent safetyEvent) {
+        SafetyCenterManagerWrapper.get()
+                .setSafetySourceData(
+                        context, SAFETY_SOURCE_ID, /* safetySourceData= */ null, safetyEvent);
+    }
+
+    /** Notifies Safety Center of a change in wear biometrics settings. */
+    public static void onBiometricsChanged(@NonNull Context context) {
+        setSafetySourceData(
+                context,
+                new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED)
+                        .build());
+    }
+
+    private static boolean getHasEnrolledFromContentProvider(
+            @NonNull Context context, @Nullable String authority) {
+        if (authority == null) {
+            return false;
+        }
+        return ActiveUnlockStatusUtils.getDeviceNameFromContentProvider(context, authority, TAG)
+            != null;
+    }
+
+    private static String getSummaryFromContentProvider(
+            @NonNull Context context, @Nullable String authority) {
+        if (authority == null) {
+            return "";
+        }
+        String summary = ActiveUnlockStatusUtils.getSummaryFromContentProvider(
+                context, authority, TAG);
+        if (summary == null) {
+            return "";
+        }
+        return summary;
+    }
+
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
index 7e16096..9bb3051 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
@@ -91,10 +91,15 @@
         }
     }.sharedFlow()
 
-    val isAvailableFlow = installerLabelFlow.map { installerLabel ->
-        withContext(Dispatchers.IO) {
-            !AppUtils.isMainlineModule(packageManager, app.packageName) &&
-                    installerLabel != null
+    val isAvailableFlow = installerLabelFlow.map() { installerLabel ->
+        // Do not show the install info for the special case of the Play Store app.
+        if (app.packageName == context.getString(R.string.config_mainline_module_update_package)) {
+            false
+        } else {
+            withContext(Dispatchers.IO) {
+                val isMainlineModule = AppUtils.isMainlineModule(packageManager, app.packageName)
+                !isMainlineModule && installerLabel != null
+            }
         }
     }
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
index e590a80..6710da9 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
@@ -22,7 +22,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -458,12 +457,10 @@
         setupFragment();
         mFragment.setFeedbackManager(
                 new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
-        when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem);
 
         mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
 
-        verify(mMenu).add(anyInt(), eq(AccessibilitySettings.MENU_ID_SEND_FEEDBACK),
-                anyInt(), eq(mContext.getText(R.string.accessibility_send_feedback_title)));
+        verify(mMenu).add(anyInt(), anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -472,12 +469,10 @@
         setupFragment();
         mFragment.setFeedbackManager(
                 new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
-        when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem);
 
         mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
 
-        verify(mMenu, never()).add(anyInt(), eq(AccessibilitySettings.MENU_ID_SEND_FEEDBACK),
-                anyInt(), eq(mContext.getText(R.string.accessibility_send_feedback_title)));
+        verify(mMenu, never()).add(anyInt(), anyInt(), anyInt(), anyInt());
     }
 
     @Test
@@ -486,8 +481,6 @@
         setupFragment();
         mFragment.setFeedbackManager(
                 new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
-        when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem);
-        mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
         when(mMenuItem.getItemId()).thenReturn(AccessibilitySettings.MENU_ID_SEND_FEEDBACK);
 
         mFragment.onOptionsItemSelected(mMenuItem);
@@ -502,8 +495,6 @@
         setupFragment();
         mFragment.setFeedbackManager(
                 new FeedbackManager(mFragment.getActivity(), PACKAGE_NAME, DEFAULT_CATEGORY));
-        when(mMenu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mMenuItem);
-        mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
         when(mMenuItem.getItemId()).thenReturn(AccessibilitySettings.MENU_ID_SEND_FEEDBACK);
 
         mFragment.onOptionsItemSelected(mMenuItem);
diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationCursorFollowingModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationCursorFollowingModePreferenceControllerTest.java
new file mode 100644
index 0000000..42efdfe
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationCursorFollowingModePreferenceControllerTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2025 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.provider.Settings;
+import android.provider.Settings.Secure.AccessibilityMagnificationCursorFollowingMode;
+import android.text.TextUtils;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.DialogCreatable;
+import com.android.settings.R;
+import com.android.settings.accessibility.MagnificationCursorFollowingModePreferenceController.ModeInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link MagnificationCursorFollowingModePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class MagnificationCursorFollowingModePreferenceControllerTest {
+    private static final String PREF_KEY =
+            Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE;
+
+    @Rule
+    public MockitoRule mocks = MockitoJUnit.rule();
+
+    @Spy
+    private TestDialogHelper mDialogHelper = new TestDialogHelper();
+
+    private PreferenceScreen mScreen;
+    private Context mContext;
+    private MagnificationCursorFollowingModePreferenceController mController;
+    private Preference mModePreference;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
+        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        mScreen = preferenceManager.createPreferenceScreen(mContext);
+        mModePreference = new Preference(mContext);
+        mModePreference.setKey(PREF_KEY);
+        mScreen.addPreference(mModePreference);
+        mController = new MagnificationCursorFollowingModePreferenceController(mContext, PREF_KEY);
+        mController.setDialogHelper(mDialogHelper);
+        mDialogHelper.setDialogDelegate(mController);
+        showPreferenceOnTheScreen();
+    }
+
+    private void showPreferenceOnTheScreen() {
+        mController.displayPreference(mScreen);
+    }
+
+    @AccessibilityMagnificationCursorFollowingMode
+    private int getCheckedModeFromDialog() {
+        final ListView listView = mController.mModeListView;
+        assertThat(listView).isNotNull();
+
+        final int checkedPosition = listView.getCheckedItemPosition();
+        assertWithMessage("No mode is checked").that(checkedPosition)
+                .isNotEqualTo(AdapterView.INVALID_POSITION);
+
+        final ModeInfo modeInfo = (ModeInfo) listView.getAdapter().getItem(checkedPosition);
+        return modeInfo.mMode;
+    }
+
+    private void performItemClickWith(@AccessibilityMagnificationCursorFollowingMode int mode) {
+        final ListView listView = mController.mModeListView;
+        assertThat(listView).isNotNull();
+
+        int modeIndex = AdapterView.NO_ID;
+        for (int i = 0; i < listView.getAdapter().getCount(); i++) {
+            final ModeInfo modeInfo = (ModeInfo) listView.getAdapter().getItem(i);
+            if (modeInfo != null && modeInfo.mMode == mode) {
+                modeIndex = i;
+                break;
+            }
+        }
+        assertWithMessage("The mode could not be found").that(modeIndex)
+                .isNotEqualTo(AdapterView.NO_ID);
+
+        listView.performItemClick(listView.getChildAt(modeIndex), modeIndex, modeIndex);
+    }
+
+    @Test
+    public void clickPreference_defaultMode_selectionIsDefault() {
+        mController.handlePreferenceTreeClick(mModePreference);
+
+        assertThat(getCheckedModeFromDialog()).isEqualTo(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CONTINUOUS);
+    }
+
+    @Test
+    public void clickPreference_nonDefaultMode_selectionIsExpected() {
+        Settings.Secure.putInt(mContext.getContentResolver(), PREF_KEY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CENTER);
+
+        mController.handlePreferenceTreeClick(mModePreference);
+
+        assertThat(getCheckedModeFromDialog()).isEqualTo(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CENTER);
+    }
+
+    @Test
+    public void selectItemInDialog_selectionIsExpected() {
+        mController.handlePreferenceTreeClick(mModePreference);
+
+        performItemClickWith(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE);
+
+        assertThat(getCheckedModeFromDialog()).isEqualTo(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE);
+    }
+
+    @Test
+    public void selectItemInDialog_dismissWithoutSave_selectionNotPersists() {
+        mController.handlePreferenceTreeClick(mModePreference);
+
+        performItemClickWith(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE);
+
+        showPreferenceOnTheScreen();
+
+        mController.handlePreferenceTreeClick(mModePreference);
+
+        assertThat(getCheckedModeFromDialog()).isEqualTo(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_CONTINUOUS);
+        assertThat(TextUtils.equals(mController.getSummary(), mContext.getString(
+                R.string.accessibility_magnification_cursor_following_continuous))).isTrue();
+    }
+
+    @Test
+    public void selectItemInDialog_saveAndDismiss_selectionPersists() {
+        mController.handlePreferenceTreeClick(mModePreference);
+
+        performItemClickWith(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE);
+        mController.onMagnificationCursorFollowingModeDialogPositiveButtonClicked(
+                mDialogHelper.getDialog(), DialogInterface.BUTTON_POSITIVE);
+
+        showPreferenceOnTheScreen();
+
+        mController.handlePreferenceTreeClick(mModePreference);
+
+        assertThat(getCheckedModeFromDialog()).isEqualTo(
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CURSOR_FOLLOWING_MODE_EDGE);
+        assertThat(TextUtils.equals(mController.getSummary(), mContext.getString(
+                R.string.accessibility_magnification_cursor_following_edge))).isTrue();
+    }
+
+    private static class TestDialogHelper implements DialogHelper {
+        private DialogCreatable mDialogDelegate;
+        private Dialog mDialog;
+
+        @Override
+        public void showDialog(int dialogId) {
+            mDialog = mDialogDelegate.onCreateDialog(dialogId);
+        }
+
+        public void setDialogDelegate(@NonNull DialogCreatable delegate) {
+            mDialogDelegate = delegate;
+        }
+
+        public Dialog getDialog() {
+            return mDialog;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 8f9d2e1..571075c 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -23,9 +23,12 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,10 +40,13 @@
 import android.content.pm.PackageManager;
 import android.icu.text.CaseMap;
 import android.os.Bundle;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
@@ -66,8 +72,9 @@
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
@@ -84,6 +91,8 @@
 })
 public class ToggleFeaturePreferenceFragmentTest {
     @Rule
+    public final MockitoRule mocks = MockitoJUnit.rule();
+    @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example";
@@ -96,6 +105,7 @@
             PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME);
     private static final String PLACEHOLDER_TILE_TOOLTIP_CONTENT =
             PLACEHOLDER_PACKAGE_NAME + "tooltip_content";
+    private static final String PLACEHOLDER_CATEGORY = "category";
     private static final String PLACEHOLDER_DIALOG_TITLE = "title";
     private static final String DEFAULT_SUMMARY = "default summary";
     private static final String DEFAULT_DESCRIPTION = "default description";
@@ -120,10 +130,13 @@
     private ContentResolver mContentResolver;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private Menu mMenu;
+    @Mock
+    private MenuItem mMenuItem;
 
     @Before
     public void setUpTestFragment() {
-        MockitoAnnotations.initMocks(this);
         mShadowAccessibilityManager = Shadow.extract(
                 mContext.getSystemService(AccessibilityManager.class));
 
@@ -170,6 +183,61 @@
     }
 
     @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
+    public void onCreateOptionsMenu_enableLowVisionGenericFeedback_shouldAddSendFeedbackMenu() {
+        mFragment.setFeedbackManager(
+                new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
+
+        mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
+
+        verify(mMenu).add(anyInt(), eq(ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK),
+                anyInt(), eq(R.string.accessibility_send_feedback_title));
+    }
+
+    @Test
+    @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
+    public void onCreateOptionsMenu_disableLowVisionGenericFeedback_shouldNotAddSendFeedbackMenu() {
+        mFragment.setFeedbackManager(
+                new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
+
+        mFragment.onCreateOptionsMenu(mMenu, /* inflater= */ null);
+
+        verify(mMenu, never()).add(anyInt(),
+                eq(ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK), anyInt(),
+                        eq(R.string.accessibility_send_feedback_title));
+    }
+
+    @Test
+    @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
+    public void onOptionsItemSelected_enableLowVisionGenericFeedback_shouldStartSendFeedback() {
+        mFragment.setFeedbackManager(
+                new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
+        when(mMenuItem.getItemId()).thenReturn(
+                ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK);
+
+        mFragment.onOptionsItemSelected(mMenuItem);
+
+        verify(mActivity).startActivityForResult(
+                argThat(intent -> intent != null
+                        && Intent.ACTION_BUG_REPORT.equals(intent.getAction())), anyInt());
+    }
+
+    @Test
+    @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_LOW_VISION_GENERIC_FEEDBACK)
+    public void onOptionsItemSelected_disableLowVisionGenericFeedback_shouldNotStartSendFeedback() {
+        mFragment.setFeedbackManager(
+                new FeedbackManager(mActivity, PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CATEGORY));
+        when(mMenuItem.getItemId()).thenReturn(
+                ToggleFeaturePreferenceFragment.MENU_ID_SEND_FEEDBACK);
+
+        mFragment.onOptionsItemSelected(mMenuItem);
+
+        verify(mActivity, never()).startActivityForResult(
+                argThat(intent -> intent != null
+                        && Intent.ACTION_BUG_REPORT.equals(intent.getAction())), anyInt());
+    }
+
+    @Test
     public void updateShortcutPreferenceData_assignDefaultValueToVariable() {
         mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
index 3c136f0..6407c08 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
@@ -614,6 +614,24 @@
     }
 
     @Test
+    @EnableFlags(com.android.settings.accessibility.Flags
+                .FLAG_ENABLE_MAGNIFICATION_CURSOR_FOLLOWING_DIALOG)
+    public void onCreateDialog_setCursorFollowingModeDialogDelegate_invokeDialogDelegate() {
+        ToggleScreenMagnificationPreferenceFragment fragment =
+                mFragController.create(
+                        R.id.main_content, /* bundle= */ null).start().resume().get();
+        final DialogCreatable dialogDelegate = mock(DialogCreatable.class, RETURNS_DEEP_STUBS);
+        final int dialogId = DialogEnums.DIALOG_MAGNIFICATION_CURSOR_FOLLOWING_MODE;
+        when(dialogDelegate.getDialogMetricsCategory(anyInt())).thenReturn(dialogId);
+        fragment.setMagnificationCursorFollowingModeDialogDelegate(dialogDelegate);
+
+        fragment.onCreateDialog(dialogId);
+        fragment.getDialogMetricsCategory(dialogId);
+        verify(dialogDelegate).onCreateDialog(dialogId);
+        verify(dialogDelegate).getDialogMetricsCategory(dialogId);
+    }
+
+    @Test
     public void getMetricsCategory_returnsCorrectCategory() {
         ToggleScreenMagnificationPreferenceFragment fragment =
                 mFragController.create(
@@ -826,6 +844,7 @@
                 MagnificationOneFingerPanningPreferenceController.PREF_KEY,
                 MagnificationAlwaysOnPreferenceController.PREF_KEY,
                 MagnificationJoystickPreferenceController.PREF_KEY,
+                MagnificationCursorFollowingModePreferenceController.PREF_KEY,
                 MagnificationFeedbackPreferenceController.PREF_KEY);
 
         final List<SearchIndexableRaw> rawData = ToggleScreenMagnificationPreferenceFragment
@@ -881,7 +900,9 @@
     @EnableFlags({
             com.android.settings.accessibility.Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH,
             Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE,
-            Flags.FLAG_ENABLE_LOW_VISION_HATS})
+            Flags.FLAG_ENABLE_LOW_VISION_HATS,
+            com.android.settings.accessibility.Flags
+                    .FLAG_ENABLE_MAGNIFICATION_CURSOR_FOLLOWING_DIALOG})
     public void getNonIndexableKeys_hasShortcutAndAllFeaturesEnabled_allItemsSearchable() {
         mShadowAccessibilityManager.setAccessibilityShortcutTargets(
                 TRIPLETAP, List.of(MAGNIFICATION_CONTROLLER_NAME));
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java
index 65c9caf..7149d4f 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/ExternalSourcesDetailsTest.java
@@ -333,7 +333,7 @@
 
     @RequiresFlagsEnabled(android.security.Flags.FLAG_AAPM_FEATURE_DISABLE_INSTALL_UNKNOWN_SOURCES)
     @Test
-    public void getPreferenceSummary_restrictedGlobally_adminString() {
+    public void getPreferenceSummary_restrictedGloballyByAdmin_adminString() {
         final EnforcingAdmin nonAdvancedProtectionEnforcingAdmin = new EnforcingAdmin("test.pkg",
                 UnknownAuthority.UNKNOWN_AUTHORITY, mUserHandle, new ComponentName("", ""));
 
@@ -353,7 +353,7 @@
 
     @RequiresFlagsEnabled(Flags.FLAG_AAPM_FEATURE_DISABLE_INSTALL_UNKNOWN_SOURCES)
     @Test
-    public void getPreferenceSummary_restrictedGlobally_advancedProtectionString() {
+    public void getPreferenceSummary_restrictedGloballyByAdvancedProtection_disabledString() {
         final EnforcingAdmin advancedProtectionEnforcingAdmin = new EnforcingAdmin("test.pkg",
                 new UnknownAuthority(ADVANCED_PROTECTION_SYSTEM_ENTITY), mUserHandle,
                 new ComponentName("", ""));
@@ -363,12 +363,10 @@
                         advancedProtectionEnforcingAdmin);
         when(mUserManager.hasUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
                 mUserHandle)).thenReturn(true);
-        when(mContext.getString(
-                com.android.settingslib.widget.restricted.R.string.disabled_by_advanced_protection))
-                .thenReturn("disabled_by_advanced_protection");
+        when(mContext.getString(com.android.settingslib.R.string.disabled)).thenReturn("disabled");
 
         CharSequence summary = ExternalSourcesDetails.getPreferenceSummary(mContext, mAppEntry);
 
-        assertEquals("disabled_by_advanced_protection", summary.toString());
+        assertEquals("disabled", summary.toString());
     }
 }
diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java
index 563974d..6da6aa7 100644
--- a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtilsTest.java
@@ -243,4 +243,11 @@
                 .isEqualTo(mApplicationContext.getString(
                         R.string.biometric_settings_use_watch_for));
     }
+
+    @Test
+    public void getTitleForActiveUnlockOnly_returnsTile() {
+        assertThat(mActiveUnlockStatusUtils.getTitleForActiveUnlockOnly())
+                .isEqualTo(mApplicationContext.getString(
+                        R.string.security_settings_activeunlock));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceControllerTest.java
new file mode 100644
index 0000000..82afec2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/inputmethod/MousePointerSpeedPreferenceControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2025 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.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.hardware.input.InputSettings;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+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.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+/** Tests for {@link MousePointerSpeedPreferenceController} */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+        com.android.settings.testutils.shadow.ShadowSystemSettings.class,
+})
+public class MousePointerSpeedPreferenceControllerTest {
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
+
+    private static final String PREFERENCE_KEY = "pointer_speed";
+    private static final String SETTING_KEY = Settings.System.POINTER_SPEED;
+
+    private MousePointerSpeedPreferenceController mController;
+    private int mDefaultSpeed;
+    private FakeFeatureFactory mFeatureFactory;
+
+    @Before
+    public void setUp() {
+        Context context = ApplicationProvider.getApplicationContext();
+        mFeatureFactory = FakeFeatureFactory.setupForTest();
+        mController = new MousePointerSpeedPreferenceController(context, PREFERENCE_KEY);
+        mDefaultSpeed = Settings.System.getIntForUser(
+                context.getContentResolver(),
+                SETTING_KEY,
+                InputSettings.DEFAULT_POINTER_SPEED,
+                UserHandle.USER_CURRENT);
+    }
+
+    @Test
+    public void setSliderPosition_speedValue1_shouldReturnTrue() {
+        int inputSpeed = 1;
+
+        boolean result = mController.setSliderPosition(inputSpeed);
+
+        assertThat(result).isTrue();
+        assertThat(mController.getSliderPosition()).isEqualTo(inputSpeed);
+        verify(mFeatureFactory.metricsFeatureProvider).action(
+                any(),
+                eq(SettingsEnums.ACTION_GESTURE_POINTER_SPEED_CHANGED),
+                eq(1));
+    }
+
+    @Test
+    public void setSliderPosition_speedValueOverMaxValue_shouldReturnFalse() {
+        int inputSpeed = InputSettings.MAX_POINTER_SPEED + 1;
+
+        boolean result = mController.setSliderPosition(inputSpeed);
+
+        assertThat(result).isFalse();
+        assertThat(mController.getSliderPosition()).isEqualTo(mDefaultSpeed);
+    }
+
+    @Test
+    public void setSliderPosition_speedValueOverMinValue_shouldReturnFalse() {
+        int inputSpeed = InputSettings.MIN_POINTER_SPEED - 1;
+
+        boolean result = mController.setSliderPosition(inputSpeed);
+
+        assertThat(result).isFalse();
+        assertThat(mController.getSliderPosition()).isEqualTo(mDefaultSpeed);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
index 4272afe..22d39e3 100644
--- a/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
+++ b/tests/robotests/src/com/android/settings/localepicker/LocaleListEditorTest.java
@@ -36,7 +36,6 @@
 import android.app.Dialog;
 import android.app.IActivityManager;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -100,6 +99,8 @@
     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     private static final String ARG_DIALOG_TYPE = "arg_dialog_type";
+    private static final String
+            ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED = "arg_show_dialog_for_not_translated";
     private static final String TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT = "dialog_confirm_system_default";
     private static final String TAG_DIALOG_NOT_AVAILABLE = "dialog_not_available_locale";
     private static final String TAG_DIALOG_ADD_SYSTEM_LOCALE = "dialog_add_system_locale";
@@ -123,6 +124,10 @@
     @Mock
     private LocaleStore.LocaleInfo mLocaleInfo;
     @Mock
+    private LocaleStore.LocaleInfo mLocaleInfo1;
+    @Mock
+    private LocaleStore.LocaleInfo mLocaleInfo2;
+    @Mock
     private FragmentManager mFragmentManager;
     @Mock
     private FragmentTransaction mFragmentTransaction;
@@ -270,7 +275,7 @@
     public void showConfirmDialog_systemLocaleSelected_shouldShowLocaleChangeDialog()
             throws Exception {
         //pre-condition
-        setUpLocaleConditions();
+        setUpLocaleConditions(true);
         final Configuration config = new Configuration();
         config.setLocales((LocaleList.forLanguageTags("zh-TW,en-US")));
         when(mActivityService.getConfiguration()).thenReturn(config);
@@ -300,6 +305,41 @@
     }
 
     @Test
+    public void showConfirmDialog_2ndLocaleSelected_shouldShowLocaleChangeDialog()
+            throws Exception {
+        //pre-condition
+        Locale.setDefault(Locale.forLanguageTag("en-US"));
+        setUpLocaleConditions2();
+        final Configuration config = new Configuration();
+        config.setLocales((LocaleList.forLanguageTags("blo-BJ,en-US,zh-TW")));
+        when(mActivityService.getConfiguration()).thenReturn(config);
+        when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
+        when(mAdapter.getCheckedCount()).thenReturn(1);
+        when(mAdapter.getItemCount()).thenReturn(3);
+        when(mAdapter.isFirstLocaleChecked()).thenReturn(false);
+        ReflectionHelpers.setField(mLocaleListEditor, "mRemoveMode", true);
+        ReflectionHelpers.setField(mLocaleListEditor, "mShowingRemoveDialog", true);
+
+        //launch the first dialog
+        mLocaleListEditor.showRemoveLocaleWarningDialog();
+
+        final Dialog dialog = ShadowDialog.getLatestDialog();
+
+        assertThat(dialog).isNotNull();
+
+        // click the remove button
+        dialog.findViewById(R.id.button_ok).performClick();
+        ShadowLooper.idleMainLooper();
+
+        assertThat(dialog.isShowing()).isFalse();
+
+        // check the second dialog is showing
+        verify(mFragmentTransaction).add(any(LocaleDialogFragment.class),
+                eq(TAG_DIALOG_CONFIRM_SYSTEM_DEFAULT));
+    }
+
+
+    @Test
     public void mayAppendUnicodeTags_appendUnicodeTags_success() {
         LocaleStore.LocaleInfo localeInfo = LocaleStore.fromLocale(Locale.forLanguageTag("en-US"));
 
@@ -315,7 +355,8 @@
         Bundle bundle = new Bundle();
         bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
         mIntent.putExtras(bundle);
-        setUpLocaleConditions();
+        mIntent.putExtra(ARG_SHOW_DIALOG_FOR_NOT_TRANSLATED, true);
+        setUpLocaleConditions(false);
         mLocaleListEditor.onActivityResult(REQUEST_CONFIRM_SYSTEM_DEFAULT, Activity.RESULT_OK,
                 mIntent);
 
@@ -328,7 +369,7 @@
         Bundle bundle = new Bundle();
         bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
         mIntent.putExtras(bundle);
-        setUpLocaleConditions();
+        setUpLocaleConditions(true);
         mLocaleListEditor.onActivityResult(REQUEST_CONFIRM_SYSTEM_DEFAULT, Activity.RESULT_CANCELED,
                 mIntent);
 
@@ -338,7 +379,7 @@
     @Test
     public void onTouch_dragDifferentLocaleToTop_showConfirmDialog() throws Exception {
         MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
-        setUpLocaleConditions();
+        setUpLocaleConditions(true);
         final Configuration config = new Configuration();
         config.setLocales((LocaleList.forLanguageTags("zh-TW,en-US")));
         when(mActivityService.getConfiguration()).thenReturn(config);
@@ -352,7 +393,7 @@
     @Test
     public void onTouch_dragSameLocaleToTop_updateAdapter() throws Exception {
         MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
-        setUpLocaleConditions();
+        setUpLocaleConditions(true);
         final Configuration config = new Configuration();
         config.setLocales((LocaleList.forLanguageTags("en-US,zh-TW")));
         when(mActivityService.getConfiguration()).thenReturn(config);
@@ -490,12 +531,26 @@
         verify(mAdapter).setCheckBoxDescription(any(LocaleDragCell.class), any(), anyBoolean());
     }
 
-    private void setUpLocaleConditions() {
+    private void setUpLocaleConditions(boolean isTranslated) {
         ShadowActivityManager.setService(mActivityService);
         mLocaleList = new ArrayList<>();
         mLocaleList.add(mLocaleInfo);
         when(mLocaleInfo.getFullNameNative()).thenReturn("English");
         when(mLocaleInfo.getLocale()).thenReturn(LocaleList.forLanguageTags("en-US").get(0));
+        when(mLocaleInfo.isTranslated()).thenReturn(isTranslated);
+        when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
+    }
+
+    private void setUpLocaleConditions2() {
+        ShadowActivityManager.setService(mActivityService);
+        mLocaleList = new ArrayList<>();
+        mLocaleList.add(mLocaleInfo);
+        mLocaleList.add(mLocaleInfo1);
+        mLocaleList.add(mLocaleInfo2);
+        when(mLocaleInfo.getLocale()).thenReturn(Locale.forLanguageTag("blo-BJ"));
+        when(mLocaleInfo.isTranslated()).thenReturn(false);
+        when(mLocaleInfo2.getLocale()).thenReturn(Locale.forLanguageTag("zh-TW"));
+        when(mLocaleInfo2.isTranslated()).thenReturn(true);
         when(mAdapter.getFeedItemList()).thenReturn(mLocaleList);
     }
 }
diff --git a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
index c5d4c36..e002de1 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 
 import com.android.settings.accessibility.AccessibilityFeedbackFeatureProvider;
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProvider;
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProvider;
 import com.android.settings.accessibility.AccessibilitySearchFeatureProvider;
 import com.android.settings.accounts.AccountFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProvider;
@@ -94,7 +94,7 @@
     public WifiTrackerLibProvider wifiTrackerLibProvider;
     public SecuritySettingsFeatureProvider securitySettingsFeatureProvider;
     public AccessibilitySearchFeatureProvider mAccessibilitySearchFeatureProvider;
-    public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
+    public AccessibilityPageIdFeatureProvider mAccessibilityPageIdFeatureProvider;
     public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
     public WifiFeatureProvider mWifiFeatureProvider;
     public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
@@ -145,7 +145,7 @@
         wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
         securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
         mAccessibilitySearchFeatureProvider = mock(AccessibilitySearchFeatureProvider.class);
-        mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class);
+        mAccessibilityPageIdFeatureProvider = mock(AccessibilityPageIdFeatureProvider.class);
         mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
         mWifiFeatureProvider = mock(WifiFeatureProvider.class);
         mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
@@ -294,8 +294,8 @@
     }
 
     @Override
-    public AccessibilityMetricsFeatureProvider getAccessibilityMetricsFeatureProvider() {
-        return mAccessibilityMetricsFeatureProvider;
+    public AccessibilityPageIdFeatureProvider getAccessibilityPageIdFeatureProvider() {
+        return mAccessibilityPageIdFeatureProvider;
     }
 
     @Override
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
index 6297c62..52ee077 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
@@ -122,6 +122,25 @@
     }
 
     @Test
+    fun whenIsPlayStoreApp_notDisplayed() {
+        val playStorePackageName = "com.android.vending"
+        whenever(
+            AppStoreUtil.getInstallerPackageNameAndInstallSourceInfo(
+                any(),
+                eq(playStorePackageName)
+            )
+        )
+            .thenReturn(Pair(INSTALLER_PACKAGE_NAME, INSTALL_SOURCE_INFO))
+        val playStoreApp = ApplicationInfo().apply {
+            packageName = playStorePackageName
+            uid = UID
+        }
+        setContent(playStoreApp)
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
     fun whenStoreLinkIsNull_disabled() {
         whenever(AppStoreUtil.getAppStoreLink(context, INSTALLER_PACKAGE_NAME, PACKAGE_NAME))
             .thenReturn(null)
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 56dd444..7b1bdc0 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -18,7 +18,7 @@
 
 import android.content.Context
 import com.android.settings.accessibility.AccessibilityFeedbackFeatureProvider
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProvider
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProvider
 import com.android.settings.accessibility.AccessibilitySearchFeatureProvider
 import com.android.settings.accounts.AccountFeatureProvider
 import com.android.settings.applications.ApplicationFeatureProvider
@@ -130,7 +130,7 @@
         get() = TODO("Not yet implemented")
     override val accessibilitySearchFeatureProvider: AccessibilitySearchFeatureProvider
         get() = TODO("Not yet implemented")
-    override val accessibilityMetricsFeatureProvider: AccessibilityMetricsFeatureProvider
+    override val accessibilityPageIdFeatureProvider: AccessibilityPageIdFeatureProvider
         get() = TODO("Not yet implemented")
     override val advancedVpnFeatureProvider: AdvancedVpnFeatureProvider
         get() = TODO("Not yet implemented")
diff --git a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
index f16113a..6e46d2b 100644
--- a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
@@ -527,6 +527,9 @@
         verify(mSafetyCenterManagerWrapper)
                 .setSafetySourceData(
                         any(), eq(FingerprintSafetySource.SAFETY_SOURCE_ID), any(), any());
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(), eq(WearSafetySource.SAFETY_SOURCE_ID), any(), any());
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
index e65d041..836247c 100644
--- a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
@@ -246,6 +246,25 @@
     }
 
     @Test
+    public void onReceive_onRefresh_withWearUnlockSourceId_setsWearUnlockData() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        Intent intent =
+                new Intent()
+                        .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+                        .putExtra(
+                                EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+                                new String[] {WearSafetySource.SAFETY_SOURCE_ID})
+                        .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, REFRESH_BROADCAST_ID);
+
+        new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mSafetyCenterManagerWrapper, times(1))
+                .setSafetySourceData(any(), captor.capture(), any(), any());
+
+        assertThat(captor.getValue()).isEqualTo(WearSafetySource.SAFETY_SOURCE_ID);
+    }
+
+    @Test
     public void onReceive_onRefresh_withFingerprintUnlockSourceId_setsFingerprintUnlockData() {
         when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
         Intent intent =
@@ -332,7 +351,7 @@
 
         new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
         ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
-        verify(mSafetyCenterManagerWrapper, times(5))
+        verify(mSafetyCenterManagerWrapper, times(6))
                 .setSafetySourceData(any(), captor.capture(), any(), any());
         List<String> safetySourceIdList = captor.getAllValues();
 
@@ -356,6 +375,11 @@
         assertThat(
                         safetySourceIdList.stream()
                                 .anyMatch(
+                                        id -> id.equals(WearSafetySource.SAFETY_SOURCE_ID)))
+                .isTrue();
+        assertThat(
+                        safetySourceIdList.stream()
+                                .anyMatch(
                                         id -> id.equals(PrivateSpaceSafetySource.SAFETY_SOURCE_ID)))
                 .isTrue();
     }
diff --git a/tests/unit/src/com/android/settings/safetycenter/WearSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/WearSafetySourceTest.java
new file mode 100644
index 0000000..c0c982d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/safetycenter/WearSafetySourceTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2025 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.safetycenter;
+
+import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.safetycenter.SafetyEvent;
+import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceStatus;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
+import com.android.settings.flags.Flags;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class WearSafetySourceTest {
+
+    private static final ComponentName COMPONENT_NAME = new ComponentName("package", "class");
+    private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
+    private static final SafetyEvent EVENT_SOURCE_STATE_CHANGED =
+            new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
+    public static final String TARGET = "com.active.unlock.target";
+    public static final String PROVIDER = "com.active.unlock.provider";
+    public static final String TARGET_SETTING = "active_unlock_target";
+    public static final String PROVIDER_SETTING = "active_unlock_provider";
+    public static final String SUMMARY = "Wear Summary";
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private Context mApplicationContext;
+
+    @Mock private PackageManager mPackageManager;
+    @Mock private DevicePolicyManager mDevicePolicyManager;
+    @Mock private FingerprintManager mFingerprintManager;
+    @Mock private LockPatternUtils mLockPatternUtils;
+    @Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mApplicationContext = spy(ApplicationProvider.getApplicationContext());
+        when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+        when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
+                .thenReturn(COMPONENT_NAME);
+        when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
+                .thenReturn(mFingerprintManager);
+        when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+                .thenReturn(mDevicePolicyManager);
+        FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+        when(featureFactory.securityFeatureProvider.getLockPatternUtils(mApplicationContext))
+                .thenReturn(mLockPatternUtils);
+        doReturn(true).when(mLockPatternUtils).isSecure(anyInt());
+        SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
+    }
+
+    @After
+    public void tearDown() {
+        SafetyCenterManagerWrapper.sInstance = null;
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
+    public void setSafetyData_whenSafetyCenterIsDisabled_doesNotSetData() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false);
+
+        WearSafetySource.setSafetySourceData(
+                mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        verify(mSafetyCenterManagerWrapper, never())
+                .setSafetySourceData(any(), any(), any(), any());
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
+    public void setSafetySourceData_whenSeparateBiometricsFlagOff_setsNullData() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+
+        WearSafetySource.setSafetySourceData(
+                mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(), eq(WearSafetySource.SAFETY_SOURCE_ID), eq(null), any());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
+    public void setSafetySourceData_whenSafetyCenterIsEnabled_activeUnlockDisabled_setsNullData() {
+        disableActiveUnlock(mApplicationContext);
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+
+        WearSafetySource.setSafetySourceData(
+                mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(), eq(WearSafetySource.SAFETY_SOURCE_ID), eq(null), any());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
+    public void setSafetySourceData_setsDataWithCorrectSafetyEvent() {
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+
+        WearSafetySource.setSafetySourceData(
+                mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(any(), any(), any(), eq(EVENT_SOURCE_STATE_CHANGED));
+    }
+
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
+    public void setSafetySourceData_withWearEnabled_whenWearEnrolled_setsData() {
+        enableActiveUnlock(mApplicationContext);
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+
+        WearSafetySource.setHasEnrolledForTesting(true);
+        WearSafetySource.setSummaryForTesting(SUMMARY);
+
+        WearSafetySource.setSafetySourceData(
+                mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        assertSafetySourceEnabledDataSet(
+                ResourcesUtils.getResourcesString(mApplicationContext,
+                "security_settings_activeunlock"),
+                SUMMARY);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
+    public void setSafetySourceData_withWearEnabled_whenWearNotEnrolled_setsData() {
+        enableActiveUnlock(mApplicationContext);
+        when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+        when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
+
+        WearSafetySource.setHasEnrolledForTesting(false);
+        WearSafetySource.setSummaryForTesting(SUMMARY);
+
+        WearSafetySource.setSafetySourceData(
+                mApplicationContext, EVENT_SOURCE_STATE_CHANGED);
+
+        assertSafetySourceDisabledDataSet(
+                ResourcesUtils.getResourcesString(mApplicationContext,
+                "security_settings_activeunlock"),
+                SUMMARY);
+    }
+
+    private static void disableActiveUnlock(Context context) {
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_REMOTE_AUTH,
+                ActiveUnlockStatusUtils.CONFIG_FLAG_NAME,
+                /* value= */ null,
+                /* makeDefault=*/ false);
+        Settings.Secure.putString(context.getContentResolver(), TARGET_SETTING, null);
+        Settings.Secure.putString(context.getContentResolver(), PROVIDER_SETTING, null);
+    }
+
+    private static void enableActiveUnlock(Context context) {
+        Settings.Secure.putString(
+                context.getContentResolver(), TARGET_SETTING, TARGET);
+        Settings.Secure.putString(
+                context.getContentResolver(), PROVIDER_SETTING, PROVIDER);
+
+        PackageManager packageManager = context.getPackageManager();
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.applicationInfo = applicationInfo;
+        when(packageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
+
+        ProviderInfo providerInfo = new ProviderInfo();
+        providerInfo.authority = PROVIDER;
+        providerInfo.applicationInfo = applicationInfo;
+        when(packageManager.resolveContentProvider(anyString(), any())).thenReturn(providerInfo);
+
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_REMOTE_AUTH,
+                ActiveUnlockStatusUtils.CONFIG_FLAG_NAME,
+                "unlock_intent_layout",
+                false /* makeDefault */);
+    }
+
+    private void assertSafetySourceDisabledDataSet(String expectedTitle, String expectedSummary) {
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(WearSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
+        SafetySourceData safetySourceData = captor.getValue();
+        SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+        assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle);
+        assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary);
+        assertThat(safetySourceStatus.isEnabled()).isTrue();
+        assertThat(safetySourceStatus.getSeverityLevel())
+                .isEqualTo(SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED);
+
+        Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent();
+        assertThat(clickIntent).isNotNull();
+        assertThat(clickIntent.getAction()).isEqualTo(TARGET);
+    }
+
+    private void assertSafetySourceEnabledDataSet(
+            String expectedTitle, String expectedSummary) {
+        ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+        verify(mSafetyCenterManagerWrapper)
+                .setSafetySourceData(
+                        any(),
+                        eq(WearSafetySource.SAFETY_SOURCE_ID),
+                        captor.capture(),
+                        any());
+        SafetySourceData safetySourceData = captor.getValue();
+        SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+        assertThat(safetySourceStatus.getTitle().toString()).isEqualTo(expectedTitle);
+        assertThat(safetySourceStatus.getSummary().toString()).isEqualTo(expectedSummary);
+        assertThat(safetySourceStatus.isEnabled()).isTrue();
+        assertThat(safetySourceStatus.getSeverityLevel())
+                .isEqualTo(SafetySourceData.SEVERITY_LEVEL_INFORMATION);
+        Intent clickIntent = safetySourceStatus.getPendingIntent().getIntent();
+        assertThat(clickIntent).isNotNull();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index d77d7a4..eda0aeb 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 
 import com.android.settings.accessibility.AccessibilityFeedbackFeatureProvider;
-import com.android.settings.accessibility.AccessibilityMetricsFeatureProvider;
+import com.android.settings.accessibility.AccessibilityPageIdFeatureProvider;
 import com.android.settings.accessibility.AccessibilitySearchFeatureProvider;
 import com.android.settings.accounts.AccountFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProvider;
@@ -93,7 +93,7 @@
     public WifiTrackerLibProvider wifiTrackerLibProvider;
     public SecuritySettingsFeatureProvider securitySettingsFeatureProvider;
     public AccessibilitySearchFeatureProvider mAccessibilitySearchFeatureProvider;
-    public AccessibilityMetricsFeatureProvider mAccessibilityMetricsFeatureProvider;
+    public AccessibilityPageIdFeatureProvider mAccessibilityPageIdFeatureProvider;
     public AdvancedVpnFeatureProvider mAdvancedVpnFeatureProvider;
     public WifiFeatureProvider mWifiFeatureProvider;
     public KeyboardSettingsFeatureProvider mKeyboardSettingsFeatureProvider;
@@ -146,7 +146,7 @@
         wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
         securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
         mAccessibilitySearchFeatureProvider = mock(AccessibilitySearchFeatureProvider.class);
-        mAccessibilityMetricsFeatureProvider = mock(AccessibilityMetricsFeatureProvider.class);
+        mAccessibilityPageIdFeatureProvider = mock(AccessibilityPageIdFeatureProvider.class);
         mAdvancedVpnFeatureProvider = mock(AdvancedVpnFeatureProvider.class);
         mWifiFeatureProvider = mock(WifiFeatureProvider.class);
         mKeyboardSettingsFeatureProvider = mock(KeyboardSettingsFeatureProvider.class);
@@ -295,8 +295,8 @@
     }
 
     @Override
-    public AccessibilityMetricsFeatureProvider getAccessibilityMetricsFeatureProvider() {
-        return mAccessibilityMetricsFeatureProvider;
+    public AccessibilityPageIdFeatureProvider getAccessibilityPageIdFeatureProvider() {
+        return mAccessibilityPageIdFeatureProvider;
     }
 
     @Override