Merge "Add refresh broadcast id checks" into tm-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ae87edb..71c07a0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4474,6 +4474,16 @@
             </intent-filter>
         </receiver>
 
+        <activity
+            android:name="com.android.settings.bluetooth.QrCodeScanModeActivity"
+            android:permission="android.permission.BLUETOOTH_CONNECT"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.settings.BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
         <!-- This is the longest AndroidManifest.xml ever. -->
     </application>
 </manifest>
diff --git a/res/drawable/ic_qr_code_scanner.xml b/res/drawable/ic_qr_code_scanner.xml
new file mode 100644
index 0000000..f6f63c5
--- /dev/null
+++ b/res/drawable/ic_qr_code_scanner.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
+        android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path android:fillColor="@android:color/white"
+          android:pathData="M2,7V2H7V4H4V7ZM2,22V17H4V20H7V22ZM17,22V20H20V17H22V22ZM20,7V4H17V2H22V7ZM17.5,17.5H19V19H17.5ZM17.5,14.5H19V16H17.5ZM16,16H17.5V17.5H16ZM14.5,17.5H16V19H14.5ZM13,16H14.5V17.5H13ZM16,13H17.5V14.5H16ZM14.5,14.5H16V16H14.5ZM13,13H14.5V14.5H13ZM19,5V11H13V5ZM11,13V19H5V13ZM11,5V11H5V5ZM9.5,17.5V14.5H6.5V17.5ZM9.5,9.5V6.5H6.5V9.5ZM17.5,9.5V6.5H14.5V9.5Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/qrcode_scan_mode_activity.xml b/res/layout/qrcode_scan_mode_activity.xml
new file mode 100644
index 0000000..f0a182b
--- /dev/null
+++ b/res/layout/qrcode_scan_mode_activity.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/res/layout/qrcode_scanner_fragment.xml b/res/layout/qrcode_scanner_fragment.xml
new file mode 100644
index 0000000..2c543f2
--- /dev/null
+++ b/res/layout/qrcode_scanner_fragment.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/sud_layout_icon_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="3"
+        android:layout_marginBottom="35dp">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:gravity="center"
+            android:orientation="vertical">
+            <ImageView
+                android:id="@+id/sud_layout_icon"
+                android:src="@drawable/ic_qr_code_scanner"
+                android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+                android:layout_width="@dimen/qrcode_icon_size"
+                android:layout_height="@dimen/qrcode_icon_size"
+                android:contentDescription="@null"/>
+
+            <TextView
+                android:id="@+id/sud_layout_title"
+                style="@style/QrCodeScanner"
+                android:textSize="24sp"
+                android:text="@string/bluetooth_find_broadcast_button_scan"
+                android:gravity="center"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="19dp"/>
+
+            <TextView
+                android:id="@+id/sud_layout_subtitle"
+                style="@style/QrCodeScanner"
+                android:text="@string/bt_le_audio_scan_qr_code_scanner"
+                android:gravity="center"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="7"
+        android:orientation="vertical">
+
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top"
+            android:gravity="center"
+            android:clipChildren="true">
+            <TextureView
+                android:id="@+id/preview_view"
+                android:layout_marginStart="@dimen/qrcode_preview_margin"
+                android:layout_marginEnd="@dimen/qrcode_preview_margin"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/qrcode_preview_size"/>
+        </FrameLayout>
+
+        <TextView
+            android:id="@+id/error_message"
+            style="@style/TextAppearance.ErrorText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:layout_marginStart="?attr/sudMarginStart"
+            android:layout_marginEnd="?attr/sudMarginEnd"
+            android:gravity="center"
+            android:layout_gravity="center"
+            android:visibility="invisible"/>
+
+    </LinearLayout>
+
+
+</LinearLayout>
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1582d21..3d616a8 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -485,4 +485,9 @@
     <!-- Sims/Data mobile/Calls/SMS select dialog-->
     <dimen name="sims_select_margin_bottom">24dp</dimen>
     <dimen name="sims_select_margin_top">8dp</dimen>
+
+    <!-- QR code picture size -->
+    <dimen name="qrcode_preview_margin">40dp</dimen>
+    <dimen name="qrcode_preview_radius">30dp</dimen>
+    <dimen name="qrcode_icon_size">27dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5d61307..1d685ef 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -14171,4 +14171,11 @@
     <string name="find_broadcast_password_dialog_connection_error">Can\u2019t connect. Try again.</string>
     <!-- The error message of enter password dialog in bluetooth find broadcast page [CHAR LIMIT=none] -->
     <string name="find_broadcast_password_dialog_password_error">Wrong password</string>
+
+
+    <!-- [CHAR LIMIT=NONE] Le audio QR code scanner sub-title -->
+    <string name="bt_le_audio_scan_qr_code_scanner">To start listening, center the QR code below</string>
+    <!-- [CHAR LIMIT=NONE] Hint for QR code process failure -->
+    <string name="bt_le_audio_qr_code_is_not_valid_format">QR code isn\u0027t a valid format</string>
+
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7a87993..f147ce9 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -962,4 +962,11 @@
         <item name="android:minWidth">0dp</item>
         <item name="android:textAllCaps">false</item>
     </style>
+
+    <style name="QrCodeScanner">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textDirection">locale</item>
+    </style>
 </resources>
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 4e2088e..f5fb26c 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -34,6 +34,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.graphics.drawable.Icon;
@@ -152,6 +153,8 @@
      */
     public static final String EXTRA_IS_FROM_SLICE = "is_from_slice";
 
+    public static final String EXTRA_USER_HANDLE = "user_handle";
+
     /**
      * Personal or Work profile tab of {@link ProfileSelectFragment}
      * <p>0: Personal tab.
@@ -427,7 +430,14 @@
         }
 
         try {
-            startActivity(trampolineIntent);
+            final UserManager um = getSystemService(UserManager.class);
+            final UserInfo userInfo = um.getUserInfo(getUser().getIdentifier());
+            if (userInfo.isManagedProfile()) {
+                trampolineIntent.putExtra(EXTRA_USER_HANDLE, getUser());
+                startActivityAsUser(trampolineIntent, um.getPrimaryUser().getUserHandle());
+            } else {
+                startActivity(trampolineIntent);
+            }
         } catch (ActivityNotFoundException e) {
             Log.e(LOG_TAG, "Deep link homepage is not available to show 2-pane UI");
             return false;
diff --git a/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
index 930fbe4..6ead390 100644
--- a/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizard.java
@@ -27,8 +27,11 @@
 
 import com.android.settings.R;
 import com.android.settingslib.Utils;
+import com.android.settingslib.widget.LayoutPreference;
 
 import com.google.android.setupdesign.GlifPreferenceLayout;
+import com.google.android.setupdesign.util.LayoutStyler;
+
 
 /**
  * A {@link androidx.preference.PreferenceFragmentCompat} that displays the settings page related
@@ -47,6 +50,8 @@
         icon.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorPrimary));
         AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
                 /* description= */ null, icon);
+
+        updateResetButtonPadding();
     }
 
     @Override
@@ -66,4 +71,14 @@
         // Hides help center in action bar and footer bar in SuW
         return 0;
     }
+
+    /**
+     * Updates the padding of the reset button to meet for SetupWizard style.
+     */
+    private void updateResetButtonPadding() {
+        final LayoutPreference resetPreference = (LayoutPreference) findPreference(RESET_KEY);
+        final ViewGroup parentView =
+                (ViewGroup) resetPreference.findViewById(R.id.reset_button).getParent();
+        LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(parentView);
+    }
 }
diff --git a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
index f9a1113..0af8aa1 100644
--- a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
@@ -49,6 +49,8 @@
         if (mTopIntroPreference != null) {
             mTopIntroPreference.setVisible(false);
         }
+
+        mToggleServiceSwitchPreference.applyPartnerCustomizationPaddingStyle();
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
index 1527f21..1282abd 100644
--- a/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothFindBroadcastsHeaderController.java
@@ -33,7 +33,6 @@
 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.qrcode.QrCodeScanModeActivity;
 import com.android.settingslib.widget.LayoutPreference;
 
 /**
@@ -135,7 +134,7 @@
     private void launchQrCodeScanner() {
         final Intent intent = new Intent(mContext, QrCodeScanModeActivity.class);
         intent.setAction(BluetoothBroadcastUtils.ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER)
-                .putExtra(BluetoothBroadcastUtils.EXTRA_BLUETOOTH_SINK_IS_GROUP, false)
+                .putExtra(BluetoothBroadcastUtils.EXTRA_BLUETOOTH_SINK_IS_GROUP, true)
                 .putExtra(BluetoothBroadcastUtils.EXTRA_BLUETOOTH_DEVICE_SINK,
                         mCachedDevice.getDevice());
         mContext.startActivity(intent);
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java b/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java
new file mode 100644
index 0000000..5c5b61f
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import static com.android.settingslib.bluetooth.BluetoothBroadcastUtils.EXTRA_BLUETOOTH_DEVICE_SINK;
+import static com.android.settingslib.bluetooth.BluetoothBroadcastUtils.EXTRA_BLUETOOTH_SINK_IS_GROUP;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
+//TODO (b/232365943): Add test case for tthe QrCode UI.
+public class QrCodeScanModeActivity extends QrCodeScanModeBaseActivity {
+    private static final boolean DEBUG = BluetoothUtils.D;
+    private static final String TAG = "QrCodeScanModeActivity";
+
+    private boolean mIsGroupOp;
+    private BluetoothDevice mSink;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void handleIntent(Intent intent) {
+        String action = intent != null ? intent.getAction() : null;
+        if (DEBUG) {
+            Log.d(TAG, "handleIntent(), action = " + action);
+        }
+
+        if (action == null) {
+            finish();
+            return;
+        }
+
+        switch (action) {
+            case BluetoothBroadcastUtils.ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER:
+                showQrCodeScannerFragment(intent);
+                break;
+            default:
+                if (DEBUG) {
+                    Log.e(TAG, "Launch with an invalid action");
+                }
+                finish();
+        }
+    }
+
+    protected void showQrCodeScannerFragment(Intent intent) {
+        if (intent == null) {
+            if (DEBUG) {
+                Log.d(TAG, "intent is null, can not get bluetooth information from intent.");
+            }
+            return;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "showQrCodeScannerFragment");
+        }
+
+        mSink = intent.getParcelableExtra(EXTRA_BLUETOOTH_DEVICE_SINK);
+        mIsGroupOp = intent.getBooleanExtra(EXTRA_BLUETOOTH_SINK_IS_GROUP, false);
+        if (DEBUG) {
+            Log.d(TAG, "get extra from intent");
+        }
+
+        QrCodeScanModeFragment fragment =
+                (QrCodeScanModeFragment) mFragmentManager.findFragmentByTag(
+                        BluetoothBroadcastUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
+
+        if (fragment == null) {
+            fragment = new QrCodeScanModeFragment(mIsGroupOp, mSink);
+        } else {
+            if (fragment.isVisible()) {
+                return;
+            }
+
+            // When the fragment in back stack but not on top of the stack, we can simply pop
+            // stack because current fragment transactions are arranged in an order
+            mFragmentManager.popBackStackImmediate();
+            return;
+        }
+        final FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
+
+        fragmentTransaction.replace(R.id.fragment_container, fragment,
+                BluetoothBroadcastUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
+        fragmentTransaction.commit();
+    }
+}
+
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeBaseActivity.java b/src/com/android/settings/bluetooth/QrCodeScanModeBaseActivity.java
new file mode 100644
index 0000000..af8a6e9
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeBaseActivity.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.ObservableActivity;
+
+public abstract class QrCodeScanModeBaseActivity extends ObservableActivity {
+
+    protected FragmentManager mFragmentManager;
+
+    protected abstract void handleIntent(Intent intent);
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setTheme(R.style.SudThemeGlifV3_DayNight);
+
+        setContentView(R.layout.qrcode_scan_mode_activity);
+        mFragmentManager = getSupportFragmentManager();
+
+        if (savedInstanceState == null) {
+            handleIntent(getIntent());
+        }
+    }
+}
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeController.java b/src/com/android/settings/bluetooth/QrCodeScanModeController.java
new file mode 100644
index 0000000..4504b4b
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeController.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+public class QrCodeScanModeController {
+
+    private static final boolean DEBUG = BluetoothUtils.D;
+    private static final String TAG = "QrCodeScanModeController";
+
+    private LocalBluetoothLeBroadcastMetadata mLocalBroadcastMetadata;
+    private LocalBluetoothLeBroadcastAssistant mLocalBroadcastAssistant;
+    private LocalBluetoothManager mLocalBluetoothManager;
+    private LocalBluetoothProfileManager mProfileManager;
+
+    public QrCodeScanModeController(Context context) {
+        if (DEBUG) {
+            Log.d(TAG, "QrCodeScanModeController constructor.");
+        }
+        mLocalBluetoothManager = Utils.getLocalBtManager(context);
+        mProfileManager = mLocalBluetoothManager.getProfileManager();
+        mLocalBroadcastMetadata = new LocalBluetoothLeBroadcastMetadata();
+        CachedBluetoothDeviceManager cachedDeviceManager = new CachedBluetoothDeviceManager(context,
+                mLocalBluetoothManager);
+        mLocalBroadcastAssistant = new LocalBluetoothLeBroadcastAssistant(context,
+                cachedDeviceManager, mProfileManager);
+    }
+
+    private BluetoothLeBroadcastMetadata convertToBroadcastMetadata(String qrCodeString) {
+        return mLocalBroadcastMetadata.convertToBroadcastMetadata(qrCodeString);
+    }
+
+    public void addSource(BluetoothDevice sink, String sourceMetadata,
+            boolean isGroupOp) {
+        mLocalBroadcastAssistant.addSource(sink,
+                convertToBroadcastMetadata(sourceMetadata), isGroupOp);
+    }
+}
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
new file mode 100644
index 0000000..dcf89ca
--- /dev/null
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
@@ -0,0 +1,243 @@
+/**
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.TextView;
+
+import com.android.settings.core.InstrumentedFragment;
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.core.lifecycle.ObservableFragment;
+import com.android.settingslib.qrcode.QrCamera;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+
+public class QrCodeScanModeFragment extends InstrumentedFragment implements
+        TextureView.SurfaceTextureListener,
+        QrCamera.ScannerCallback {
+    private static final boolean DEBUG = BluetoothUtils.D;
+    private static final String TAG = "QrCodeScanModeFragment";
+
+    /** Message sent to hide error message */
+    private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;
+    /** Message sent to show error message */
+    private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;
+    /** Message sent to broadcast QR code */
+    private static final int MESSAGE_SCAN_BROADCAST_SUCCESS = 3;
+
+    private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;
+    private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;
+
+    private boolean mIsGroupOp;
+    private int mCornerRadius;
+    private BluetoothDevice mSink;
+    private String mBroadcastMetadata;
+    private Context mContext;
+    private QrCamera mCamera;
+    private QrCodeScanModeController mController;
+    private TextureView mTextureView;
+    private TextView mSummary;
+    private TextView mErrorMessage;
+
+    public QrCodeScanModeFragment(boolean isGroupOp, BluetoothDevice sink) {
+        mIsGroupOp = isGroupOp;
+        mSink = sink;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mContext = getContext();
+        mController = new QrCodeScanModeController(mContext);
+    }
+
+    @Override
+    public final View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.qrcode_scanner_fragment, container,
+                /* attachToRoot */ false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        mTextureView = view.findViewById(R.id.preview_view);
+        mCornerRadius = mContext.getResources().getDimensionPixelSize(
+                R.dimen.qrcode_preview_radius);
+        mTextureView.setSurfaceTextureListener(this);
+        mTextureView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0,0, view.getWidth(), view.getHeight(), mCornerRadius);
+            }
+        });
+        mTextureView.setClipToOutline(true);
+        mErrorMessage = view.findViewById(R.id.error_message);
+    }
+
+    private void initCamera(SurfaceTexture surface) {
+        // Check if the camera has already created.
+        if (mCamera == null) {
+            mCamera = new QrCamera(mContext, this);
+            mCamera.start(surface);
+        }
+    }
+
+    private void destroyCamera() {
+        if (mCamera != null) {
+            mCamera.stop();
+            mCamera = null;
+        }
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
+        initCamera(surface);
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width,
+            int height) {
+    }
+
+    @Override
+    public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
+        destroyCamera();
+        return true;
+    }
+
+    @Override
+    public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
+    }
+
+    @Override
+    public void handleSuccessfulResult(String qrCode) {
+        if (DEBUG) {
+            Log.d(TAG, "handleSuccessfulResult(), get the qr code string.");
+        }
+        mBroadcastMetadata = qrCode;
+        handleBtLeAudioScanner();
+    }
+
+    @Override
+    public void handleCameraFailure() {
+        destroyCamera();
+    }
+
+    @Override
+    public Size getViewSize() {
+        return new Size(mTextureView.getWidth(), mTextureView.getHeight());
+    }
+
+    @Override
+    public Rect getFramePosition(Size previewSize, int cameraOrientation) {
+        return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
+    }
+
+    @Override
+    public void setTransform(Matrix transform) {
+        mTextureView.setTransform(transform);
+    }
+
+    @Override
+    public boolean isValid(String qrCode) {
+        if (qrCode.startsWith(BluetoothBroadcastUtils.SCHEME_BT_BROADCAST_METADATA)) {
+            return true;
+        } else {
+            showErrorMessage(R.string.bt_le_audio_qr_code_is_not_valid_format);
+            return false;
+        }
+    }
+
+    protected boolean isDecodeTaskAlive() {
+        return mCamera != null && mCamera.isDecodeTaskAlive();
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_HIDE_ERROR_MESSAGE:
+                    mErrorMessage.setVisibility(View.INVISIBLE);
+                    break;
+
+                case MESSAGE_SHOW_ERROR_MESSAGE:
+                    final String errorMessage = (String) msg.obj;
+
+                    mErrorMessage.setVisibility(View.VISIBLE);
+                    mErrorMessage.setText(errorMessage);
+                    mErrorMessage.sendAccessibilityEvent(
+                            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
+                    // Cancel any pending messages to hide error view and requeue the message so
+                    // user has time to see error
+                    removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
+                    sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
+                            SHOW_ERROR_MESSAGE_INTERVAL);
+                    break;
+
+                case MESSAGE_SCAN_BROADCAST_SUCCESS:
+                    mController.addSource(mSink, mBroadcastMetadata, mIsGroupOp);
+                    updateSummary();
+                    mSummary.sendAccessibilityEvent(
+                            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+                    break;
+                default:
+            }
+        }
+    };
+
+    private void showErrorMessage(@StringRes int messageResId) {
+        final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
+                getString(messageResId));
+        message.sendToTarget();
+    }
+
+    private void handleBtLeAudioScanner() {
+        Message message = mHandler.obtainMessage(MESSAGE_SCAN_BROADCAST_SUCCESS);
+        mHandler.sendMessageDelayed(message, SHOW_SUCCESS_SQUARE_INTERVAL);
+    }
+
+    private void updateSummary() {
+        mSummary.setText(getString(R.string.bt_le_audio_scan_qr_code_scanner,
+                null /* broadcast_name*/));;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.LE_AUDIO_BROADCAST_SCAN_QR_CODE;
+    }
+}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 0251687..86b123b 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -20,6 +20,8 @@
 import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY;
 import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI;
 
+import static com.android.settings.SettingsActivity.EXTRA_USER_HANDLE;
+
 import android.animation.LayoutTransition;
 import android.app.ActivityManager;
 import android.app.settings.SettingsEnums;
@@ -27,6 +29,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.FeatureFlagUtils;
@@ -449,7 +452,13 @@
                 SplitRule.FINISH_ALWAYS,
                 SplitRule.FINISH_ALWAYS,
                 true /* clearTop */);
-        startActivity(targetIntent);
+
+        final UserHandle user = intent.getParcelableExtra(EXTRA_USER_HANDLE, UserHandle.class);
+        if (user != null) {
+            startActivityAsUser(targetIntent, user);
+        } else {
+            startActivity(targetIntent);
+        }
     }
 
     private String getHighlightMenuKey() {
diff --git a/src/com/android/settings/widget/SettingsMainSwitchPreference.java b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
index b7c6901..9264911 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchPreference.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
@@ -22,6 +22,7 @@
 import android.content.res.TypedArray;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.ViewGroup;
 import android.widget.Switch;
 
 import androidx.core.content.res.TypedArrayUtils;
@@ -33,6 +34,8 @@
 import com.android.settingslib.RestrictedPreferenceHelper;
 import com.android.settingslib.widget.OnMainSwitchChangeListener;
 
+import com.google.android.setupdesign.util.LayoutStyler;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -48,6 +51,7 @@
             new ArrayList<>();
     private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
 
+    private boolean mApplyPartnerCustomizationPaddingStyle;
     private SettingsMainSwitchBar mMainSwitchBar;
     private CharSequence mTitle;
     private EnforcedAdmin mEnforcedAdmin;
@@ -95,6 +99,12 @@
         } else {
             mMainSwitchBar.hide();
         }
+
+        if (mApplyPartnerCustomizationPaddingStyle) {
+            // TODO(b/232494666): Replace all margins of the root view with the padding
+            final ViewGroup parentView = (ViewGroup) mMainSwitchBar.getParent();
+            LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(parentView);
+        }
     }
 
     private void init(Context context, AttributeSet attrs) {
@@ -241,6 +251,14 @@
         }
     }
 
+    /**
+     * Apples the padding style of the partner's customization. It's used in the SetupWizard.
+     */
+    public void applyPartnerCustomizationPaddingStyle() {
+        mApplyPartnerCustomizationPaddingStyle = true;
+        notifyChanged();
+    }
+
     private void initMainSwitchBar() {
         if (mMainSwitchBar != null) {
             mMainSwitchBar.setTitle(mTitle);
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
index 5d28bcc..10ee241 100644
--- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
+++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
@@ -522,7 +522,7 @@
             final LayoutInflater layoutInflater = LayoutInflater.from(getPrefContext());
             final View root = layoutInflater.inflate(R.layout.dialog_edittext, null /* root */);
             mDeviceNameText = root.findViewById(R.id.edittext);
-            mDeviceNameText.setFilters(new InputFilter[] {new InputFilter.LengthFilter(30)});
+            mDeviceNameText.setFilters(new InputFilter[] {new InputFilter.LengthFilter(22)});
             if (mSavedDeviceName != null) {
                 mDeviceNameText.setText(mSavedDeviceName);
                 mDeviceNameText.setSelection(mSavedDeviceName.length());
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
index 0418906..bddaed5 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentForSetupWizardTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.accessibility;
 
+import static com.android.settings.accessibility.TextReadingPreferenceFragment.RESET_KEY;
+
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -25,6 +27,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
+import com.android.settingslib.widget.LayoutPreference;
 
 import com.google.android.setupdesign.GlifPreferenceLayout;
 
@@ -51,6 +54,9 @@
         MockitoAnnotations.initMocks(this);
 
         mFragment = spy(new TextReadingPreferenceFragmentForSetupWizard());
+        final LayoutPreference resetPreference =
+                new LayoutPreference(mContext, R.layout.accessibility_text_reading_reset_button);
+        doReturn(resetPreference).when(mFragment).findPreference(RESET_KEY);
     }
 
     @Test