Add device settings service and aidl definition

Bug: 343317785
Test: run atest for all new tests
Flag: EXEMPT it's not used in any places for now
Change-Id: Iac5c96c653f149b9fd02edd1209f2e2e0dc94d1e
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d6cbf2a..0cb85d8 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -64,6 +64,7 @@
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
+        "src/**/I*.aidl",
     ],
 }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreference.java
new file mode 100644
index 0000000..1cbb8b4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreference.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Objects;
+
+/**
+ * A data class representing an action/switch preference. The preference could be one of the four
+ * following forms: 1. Texted row with action to jump to another page 2. Texted row without action
+ * 3. Texted row with action and switch 4. Texted row with switch
+ */
+public class ActionSwitchPreference extends DeviceSettingPreference implements Parcelable {
+    private final String mTitle;
+    private final String mSummary;
+    private final Bitmap mIcon;
+    private final Intent mIntent;
+    private final boolean mHasSwitch;
+    private final boolean mChecked;
+    private final boolean mIsAllowedChangingState;
+    private final Bundle mExtras;
+
+    ActionSwitchPreference(
+            String title,
+            @Nullable String summary,
+            @Nullable Bitmap icon,
+            @Nullable Intent intent,
+            boolean hasSwitch,
+            boolean checked,
+            boolean allowChangingState,
+            @NonNull Bundle extras) {
+        super(DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH);
+        validate(title);
+        mTitle = title;
+        mSummary = summary;
+        mIcon = icon;
+        mIntent = intent;
+        mHasSwitch = hasSwitch;
+        mChecked = checked;
+        mIsAllowedChangingState = allowChangingState;
+        mExtras = extras;
+    }
+
+    private static void validate(String title) {
+        if (Objects.isNull(title)) {
+            throw new IllegalArgumentException("Title must be set");
+        }
+    }
+
+    /**
+     * Reads an {@link ActionSwitchPreference} instance from {@link Parcel}
+     * @param in The parcel to read from
+     * @return The instance read
+     */
+    @NonNull
+    public static ActionSwitchPreference readFromParcel(@NonNull Parcel in) {
+        String title = in.readString();
+        String summary = in.readString();
+        Bitmap icon = in.readParcelable(Bitmap.class.getClassLoader());
+        Intent intent = in.readParcelable(Intent.class.getClassLoader());
+        boolean hasSwitch = in.readBoolean();
+        boolean checked = in.readBoolean();
+        boolean allowChangingState = in.readBoolean();
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new ActionSwitchPreference(
+                title, summary, icon, intent, hasSwitch, checked, allowChangingState, extras);
+    }
+
+    public static final Creator<ActionSwitchPreference> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public ActionSwitchPreference createFromParcel(@NonNull Parcel in) {
+                    in.readInt();
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public ActionSwitchPreference[] newArray(int size) {
+                    return new ActionSwitchPreference[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mTitle);
+        dest.writeString(mSummary);
+        dest.writeParcelable(mIcon, flags);
+        dest.writeParcelable(mIntent, flags);
+        dest.writeBoolean(mHasSwitch);
+        dest.writeBoolean(mChecked);
+        dest.writeBoolean(mIsAllowedChangingState);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link ActionSwitchPreference}. */
+    public static final class Builder {
+        private String mTitle;
+        private String mSummary;
+        private Bitmap mIcon;
+        private Intent mIntent;
+        private boolean mHasSwitch;
+        private boolean mChecked;
+        private boolean mIsAllowedChangingState;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * Sets the title of the preference.
+         *
+         * @param title The title of the preference.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setTitle(@NonNull String title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets the summary of the preference, optional.
+         *
+         * @param summary The preference summary.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setSummary(@Nullable String summary) {
+            mSummary = summary;
+            return this;
+        }
+
+        /**
+         * Sets the icon to be displayed on the left of the preference, optional.
+         *
+         * @param icon The icon.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setIcon(@Nullable Bitmap icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the Intent to launch when the preference is clicked, optional.
+         *
+         * @param intent The Intent.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setIntent(@Nullable Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
+         * Sets whether the preference will contain a switch.
+         *
+         * @param hasSwitch Whether the preference contains a switch.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setHasSwitch(boolean hasSwitch) {
+            mHasSwitch = hasSwitch;
+            return this;
+        }
+
+        /**
+         * Sets the state of the preference.
+         *
+         * @param checked Whether the switch is checked.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setChecked(boolean checked) {
+            mChecked = checked;
+            return this;
+        }
+
+        /**
+         * Sets whether state can be changed by user.
+         *
+         * @param allowChangingState Whether user is allowed to change state.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setAllowedChangingState(boolean allowChangingState) {
+            mIsAllowedChangingState = allowChangingState;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link ActionSwitchPreference} object.
+         *
+         * @return Returns the built {@link ActionSwitchPreference} object.
+         */
+        @NonNull
+        public ActionSwitchPreference build() {
+            return new ActionSwitchPreference(
+                    mTitle,
+                    mSummary,
+                    mIcon,
+                    mIntent,
+                    mHasSwitch,
+                    mChecked,
+                    mIsAllowedChangingState,
+                    mExtras);
+        }
+    }
+
+    /**
+     * Gets the title of the preference.
+     *
+     * @return Returns the title of the preference.
+     */
+    @NonNull
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Gets the summary of the preference.
+     *
+     * @return Returns the summary of the preference.
+     */
+    @Nullable
+    public String getSummary() {
+        return mSummary;
+    }
+
+    /**
+     * Gets the icon of the preference.
+     *
+     * @return Returns the icon of the preference.
+     */
+    @Nullable
+    public Bitmap getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Gets the Intent to launch when the preference is clicked.
+     *
+     * @return Returns the intent to launch.
+     */
+    @Nullable
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * Whether the preference contains a switch.
+     *
+     * @return Whether the preference contains a switch.
+     */
+    public boolean hasSwitch() {
+        return mHasSwitch;
+    }
+
+    /**
+     * Whether the switch is checked.
+     *
+     * @return Whether the switch is checked.
+     */
+    public boolean getChecked() {
+        return mChecked;
+    }
+
+    /**
+     * Gets whether the state can be changed by user.
+     *
+     * @return Whether the state can be changed by user.
+     */
+    public boolean isAllowedChangingState() {
+        return mIsAllowedChangingState;
+    }
+
+    /**
+     * Gets the extras bundle.
+     *
+     * @return The extra bundle.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceState.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceState.java
new file mode 100644
index 0000000..91c1a59
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceState.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/** A data class representing the state of an action/switch preference. */
+public class ActionSwitchPreferenceState extends DeviceSettingPreferenceState
+        implements Parcelable {
+    private final boolean mChecked;
+    private final Bundle mExtras;
+
+    ActionSwitchPreferenceState(boolean checked, @NonNull Bundle extras) {
+        super(DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH);
+        mChecked = checked;
+        mExtras = extras;
+    }
+
+    /**
+     * Reads an {@link ActionSwitchPreferenceState} instance from {@link Parcel}
+     * @param in The parcel to read from
+     * @return The instance read
+     */
+    @NonNull
+    public static ActionSwitchPreferenceState readFromParcel(@NonNull Parcel in) {
+        boolean checked = in.readBoolean();
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new ActionSwitchPreferenceState(checked, extras);
+    }
+
+    public static final Creator<ActionSwitchPreferenceState> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public ActionSwitchPreferenceState createFromParcel(@NonNull Parcel in) {
+                    in.readInt();
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public ActionSwitchPreferenceState[] newArray(int size) {
+                    return new ActionSwitchPreferenceState[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeBoolean(mChecked);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link ActionSwitchPreferenceState}. */
+    public static final class Builder {
+        private boolean mChecked;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        public Builder() {}
+
+        /**
+         * Sets the state of the preference.
+         *
+         * @param checked Whether the switch is checked.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setChecked(boolean checked) {
+            mChecked = checked;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the object.
+         *
+         * @return Returns the built object.
+         */
+        @NonNull
+        public ActionSwitchPreferenceState build() {
+            return new ActionSwitchPreferenceState(mChecked, mExtras);
+        }
+    }
+
+    /**
+     * Whether the switch is checked.
+     *
+     * @return Whether the switch is checked.
+     */
+    public boolean getChecked() {
+        return mChecked;
+    }
+
+    /**
+     * Gets the extras bundle.
+     *
+     * @return The extra bundle.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.aidl
new file mode 100644
index 0000000..acbaf2d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+parcelable DeviceInfo;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.java
new file mode 100644
index 0000000..52e520e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfo.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/** A data class representing a bluetooth device. */
+public class DeviceInfo implements Parcelable {
+    private final String mBluetoothAddress;
+    private final Bundle mExtras;
+
+    DeviceInfo(String bluetoothAddress, Bundle extras) {
+        validate(bluetoothAddress);
+        mBluetoothAddress = bluetoothAddress;
+        mExtras = extras;
+    }
+
+    private static void validate(String bluetoothAddress) {
+        if (Objects.isNull(bluetoothAddress)) {
+            throw new IllegalArgumentException("Bluetooth address must be set");
+        }
+    }
+
+    /** Read a {@link DeviceInfo} instance from {@link Parcel} */
+    @NonNull
+    public static DeviceInfo readFromParcel(@NonNull Parcel in) {
+        String bluetoothAddress = in.readString();
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new DeviceInfo(bluetoothAddress, extras);
+    }
+
+    public static final Creator<DeviceInfo> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public DeviceInfo createFromParcel(@NonNull Parcel in) {
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public DeviceInfo[] newArray(int size) {
+                    return new DeviceInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mBluetoothAddress);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link DeviceInfo}. */
+    public static final class Builder {
+        private String mBluetoothAddress;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * Sets the bluetooth address of the device, from {@link
+         * android.bluetooth.BluetoothDevice#getAddress()}.
+         *
+         * @param bluetoothAddress The bluetooth address.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setBluetoothAddress(@NonNull String bluetoothAddress) {
+            mBluetoothAddress = bluetoothAddress;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link DeviceInfo} object.
+         *
+         * @return Returns the built {@link DeviceInfo} object.
+         */
+        @NonNull
+        public DeviceInfo build() {
+            return new DeviceInfo(mBluetoothAddress, mExtras);
+        }
+    }
+
+    /**
+     * Gets the bluetooth address of the device.
+     *
+     * @return The bluetooth address from {@link android.bluetooth.BluetoothDevice#getAddress()}.
+     */
+    @NonNull
+    public String getBluetoothAddress() {
+        return mBluetoothAddress;
+    }
+
+    /**
+     * Gets the extras bundle.
+     *
+     * @return The extra bundle.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSetting.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSetting.aidl
new file mode 100644
index 0000000..043cae3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSetting.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+parcelable DeviceSetting;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSetting.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSetting.java
new file mode 100644
index 0000000..dc219a9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSetting.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/** A data class representing a device setting item in bluetooth device details page. */
+public final class DeviceSetting implements Parcelable {
+    @DeviceSettingId private final int mSettingId;
+    private final DeviceSettingPreference mPreference;
+    private final Bundle mExtras;
+
+    DeviceSetting(
+            int settingId, @NonNull DeviceSettingPreference preference, @NonNull Bundle extras) {
+        validate(preference);
+        mSettingId = settingId;
+        mPreference = preference;
+        mExtras = extras;
+    }
+
+    private static void validate(DeviceSettingPreference preference) {
+        if (Objects.isNull(preference)) {
+            throw new IllegalArgumentException("Preference must be set");
+        }
+    }
+
+    /** Read a {@link DeviceSetting} instance from {@link Parcel} */
+    @NonNull
+    public static DeviceSetting readFromParcel(@NonNull Parcel in) {
+        int settingId = in.readInt();
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        DeviceSettingPreference settingPreference = DeviceSettingPreference.readFromParcel(in);
+        return new DeviceSetting(settingId, settingPreference, extras);
+    }
+
+    public static final Creator<DeviceSetting> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public DeviceSetting createFromParcel(@NonNull Parcel in) {
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public DeviceSetting[] newArray(int size) {
+                    return new DeviceSetting[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSettingId);
+        dest.writeBundle(mExtras);
+        mPreference.writeToParcel(dest, flags);
+    }
+
+    /** Builder class for {@link DeviceSetting}. */
+    public static final class Builder {
+        private int mSettingId;
+        private DeviceSettingPreference mPreference;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        public Builder() {}
+
+        /**
+         * Sets the setting ID, as defined by IntDef {@link DeviceSettingId}.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setSettingId(@DeviceSettingId int settingId) {
+            mSettingId = settingId;
+            return this;
+        }
+
+        /**
+         * Sets the setting preference.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setPreference(@NonNull DeviceSettingPreference settingPreference) {
+            mPreference = settingPreference;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /** Build the object. */
+        @NonNull
+        public DeviceSetting build() {
+            return new DeviceSetting(mSettingId, mPreference, mExtras);
+        }
+    }
+
+    /**
+     * Gets the setting ID as defined by IntDef {@link DeviceSettingId}.
+     *
+     * @return Returns the setting ID.
+     */
+    @DeviceSettingId
+    public int getSettingId() {
+        return mSettingId;
+    }
+
+    /**
+     * Gets the setting preference.
+     *
+     * @return Returns the setting preference.
+     */
+    @NonNull
+    public DeviceSettingPreference getPreference() {
+        return mPreference;
+    }
+
+    /**
+     * Gets the extras Bundle.
+     *
+     * @return Returns a Bundle object.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
new file mode 100644
index 0000000..20a0339
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.SOURCE)
+@IntDef(
+        value = {
+            DeviceSettingId.DEVICE_SETTING_ID_UNKNOWN,
+            DeviceSettingId.DEVICE_SETTING_ID_HEADER,
+            DeviceSettingId.DEVICE_SETTING_ID_ADVANCED_HEADER,
+            DeviceSettingId.DEVICE_SETTING_ID_LE_AUDIO_HEADER,
+            DeviceSettingId.DEVICE_SETTING_ID_HEARING_AID_PAIR_OTHER_BUTTON,
+            DeviceSettingId.DEVICE_SETTING_ID_HEARING_AID_SPACE_LAYOUT,
+            DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS,
+            DeviceSettingId.DEVICE_SETTING_ID_DEVICE_STYLUS,
+            DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_EXTRA_CONTROL,
+            DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_DEVICE_SLICE_CATEGORY,
+            DeviceSettingId.DEVICE_SETTING_ID_DEVICE_COMPANION_APPS,
+            DeviceSettingId.DEVICE_SETTING_ID_HEARING_DEVICE_GROUP,
+            DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_AUDIO_DEVICE_TYPE_GROUP,
+            DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_GROUP,
+            DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES,
+            DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_EXTRA_OPTIONS,
+            DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_RELATED_TOOLS,
+            DeviceSettingId.DEVICE_SETTING_ID_DATA_SYNC_GROUP,
+            DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
+            DeviceSettingId.DEVICE_SETTING_ID_DEVICE_DETAILS_FOOTER,
+            DeviceSettingId.DEVICE_SETTING_ID_ANC,
+        },
+        open = true)
+public @interface DeviceSettingId {
+    /** Device setting ID is unknown. */
+    int DEVICE_SETTING_ID_UNKNOWN = 0;
+
+    /** Device setting ID for header. */
+    int DEVICE_SETTING_ID_HEADER = 1;
+
+    /** Device setting ID for advanced header. */
+    int DEVICE_SETTING_ID_ADVANCED_HEADER = 2;
+
+    /** Device setting ID for LeAudio header. */
+    int DEVICE_SETTING_ID_LE_AUDIO_HEADER = 3;
+
+    /** Device setting ID for hearing aid “pair other” button. */
+    int DEVICE_SETTING_ID_HEARING_AID_PAIR_OTHER_BUTTON = 4;
+
+    /** Device setting ID for hearing aid space layout. */
+    int DEVICE_SETTING_ID_HEARING_AID_SPACE_LAYOUT = 5;
+
+    /** Device setting ID for action buttons(Forget, Connect/Disconnect). */
+    int DEVICE_SETTING_ID_ACTION_BUTTONS = 6;
+
+    /** Device setting ID for stylus device. */
+    int DEVICE_SETTING_ID_DEVICE_STYLUS = 7;
+
+    /** Device setting ID for bluetooth extra control. */
+    int DEVICE_SETTING_ID_BLUETOOTH_EXTRA_CONTROL = 8;
+
+    /** Device setting ID for bluetooth device slice category. */
+    int DEVICE_SETTING_ID_BLUETOOTH_DEVICE_SLICE_CATEGORY = 9;
+
+    /** Device setting ID for device companion apps. */
+    int DEVICE_SETTING_ID_DEVICE_COMPANION_APPS = 10;
+
+    /** Device setting ID for hearing device group. */
+    int DEVICE_SETTING_ID_HEARING_DEVICE_GROUP = 11;
+
+    /** Device setting ID for bluetooth audio device type group. */
+    int DEVICE_SETTING_ID_BLUETOOTH_AUDIO_DEVICE_TYPE_GROUP = 12;
+
+    /** Device setting ID for spatial audio group. */
+    int DEVICE_SETTING_ID_SPATIAL_AUDIO_GROUP = 13;
+
+    /** Device setting ID for bluetooth profiles. */
+    int DEVICE_SETTING_ID_BLUETOOTH_PROFILES = 14;
+
+    /** Device setting ID for bluetooth extra options. */
+    int DEVICE_SETTING_ID_BLUETOOTH_EXTRA_OPTIONS = 15;
+
+    /** Device setting ID for bluetooth related tools. */
+    int DEVICE_SETTING_ID_BLUETOOTH_RELATED_TOOLS = 16;
+
+    /** Device setting ID for data sync group. */
+    int DEVICE_SETTING_ID_DATA_SYNC_GROUP = 17;
+
+    /** Device setting ID for keyboard settings. */
+    int DEVICE_SETTING_ID_KEYBOARD_SETTINGS = 18;
+
+    /** Device setting ID for device details footer. */
+    int DEVICE_SETTING_ID_DEVICE_DETAILS_FOOTER = 19;
+
+    /** Device setting ID for ANC. */
+    int DEVICE_SETTING_ID_ANC = 1001;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
new file mode 100644
index 0000000..9ee33b0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * A data class representing a device settings item in bluetooth device details config.
+ *
+ * @property settingId The setting ID of the item, as defined by IntDef [DeviceSettingId].
+ * @property packageName The package name for service binding.
+ * @property className The class name for service binding.
+ * @property intentAction The intent action for service binding.
+ * @property extras Extra bundle
+ */
+data class DeviceSettingItem(
+    @DeviceSettingId val settingId: Int,
+    val packageName: String,
+    val className: String,
+    val intentAction: String,
+    val extras: Bundle = Bundle.EMPTY,
+) : Parcelable {
+
+    override fun describeContents(): Int = 0
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.run {
+            writeInt(settingId)
+            writeString(packageName)
+            writeString(className)
+            writeString(intentAction)
+            writeBundle(extras)
+        }
+    }
+
+    companion object {
+        @JvmField
+        val CREATOR: Parcelable.Creator<DeviceSettingItem> =
+            object : Parcelable.Creator<DeviceSettingItem> {
+                override fun createFromParcel(parcel: Parcel) =
+                    parcel.run {
+                        DeviceSettingItem(
+                            settingId = readInt(),
+                            packageName = readString() ?: "",
+                            className = readString() ?: "",
+                            intentAction = readString() ?: "",
+                            extras = readBundle((Bundle::class.java.classLoader)) ?: Bundle.EMPTY,
+                        )
+                    }
+
+                override fun newArray(size: Int): Array<DeviceSettingItem?> {
+                    return arrayOfNulls(size)
+                }
+            }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreference.java
new file mode 100644
index 0000000..790939a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreference.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+
+/** An abstract class representing a device setting preference. */
+public abstract class DeviceSettingPreference {
+    @DeviceSettingType private final int mSettingType;
+
+    public static final DeviceSettingPreference UNKNOWN =
+            new DeviceSettingPreference(DeviceSettingType.DEVICE_SETTING_TYPE_UNKNOWN) {};
+
+    protected DeviceSettingPreference(@DeviceSettingType int settingType) {
+        mSettingType = settingType;
+    }
+
+    /** Read a {@link DeviceSettingPreference} instance from {@link Parcel} */
+    @NonNull
+    public static DeviceSettingPreference readFromParcel(@NonNull Parcel in) {
+        int type = in.readInt();
+        switch (type) {
+            case DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH:
+                return ActionSwitchPreference.readFromParcel(in);
+            case DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE:
+                return MultiTogglePreference.readFromParcel(in);
+            default:
+                return UNKNOWN;
+        }
+    }
+
+    /** Writes the instance to {@link Parcel}. */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSettingType);
+    }
+
+    /**
+     * Gets the setting type, as defined by IntDef {@link DeviceSettingType}.
+     *
+     * @return the setting type.
+     */
+    @DeviceSettingType
+    public int getSettingType() {
+        return mSettingType;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreferenceState.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreferenceState.java
new file mode 100644
index 0000000..a982af7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreferenceState.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+
+/** An abstract class representing a device setting preference state. */
+public abstract class DeviceSettingPreferenceState {
+    @DeviceSettingType private final int mSettingType;
+
+    public static final DeviceSettingPreferenceState UNKNOWN =
+            new DeviceSettingPreferenceState(DeviceSettingType.DEVICE_SETTING_TYPE_UNKNOWN) {};
+
+    protected DeviceSettingPreferenceState(@DeviceSettingType int settingType) {
+        mSettingType = settingType;
+    }
+
+    /** Reads a {@link DeviceSettingPreferenceState} from {@link Parcel}. */
+    @NonNull
+    public static DeviceSettingPreferenceState readFromParcel(@NonNull Parcel in) {
+        int type = in.readInt();
+        switch (type) {
+            case DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH:
+                return ActionSwitchPreferenceState.readFromParcel(in);
+            case DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE:
+                return MultiTogglePreferenceState.readFromParcel(in);
+            default:
+                return UNKNOWN;
+        }
+    }
+
+    /** Writes the object to parcel. */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSettingType);
+    }
+
+    /**
+     * Gets the setting type, as defined by IntDef {@link DeviceSettingType}.
+     *
+     * @return The setting type.
+     */
+    @DeviceSettingType
+    public int getSettingType() {
+        return mSettingType;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.aidl
new file mode 100644
index 0000000..61429a6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+parcelable DeviceSettingState;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.java
new file mode 100644
index 0000000..63fd4eb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingState.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/** A data class representing a device setting state. */
+public class DeviceSettingState implements Parcelable {
+    @DeviceSettingId private final int mSettingId;
+    private final DeviceSettingPreferenceState mPreferenceState;
+    private final Bundle mExtras;
+
+    DeviceSettingState(
+            @DeviceSettingId int settingId,
+            @NonNull DeviceSettingPreferenceState preferenceState,
+            @NonNull Bundle extras) {
+        validate(preferenceState);
+        mSettingId = settingId;
+        mPreferenceState = preferenceState;
+        mExtras = extras;
+    }
+
+    private static void validate(DeviceSettingPreferenceState preferenceState) {
+        if (Objects.isNull(preferenceState)) {
+            throw new IllegalArgumentException("PreferenceState must be set");
+        }
+    }
+
+    /** Reads a {@link DeviceSettingState} from {@link Parcel}. */
+    @NonNull
+    public static DeviceSettingState readFromParcel(@NonNull Parcel in) {
+        int settingId = in.readInt();
+        Bundle extra = in.readBundle(Bundle.class.getClassLoader());
+        DeviceSettingPreferenceState preferenceState =
+                DeviceSettingPreferenceState.readFromParcel(in);
+        return new DeviceSettingState(settingId, preferenceState, extra);
+    }
+
+    public static final Creator<DeviceSettingState> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public DeviceSettingState createFromParcel(@NonNull Parcel in) {
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public DeviceSettingState[] newArray(int size) {
+                    return new DeviceSettingState[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Writes the instance to {@link Parcel}. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSettingId);
+        dest.writeBundle(mExtras);
+        mPreferenceState.writeToParcel(dest, flags);
+    }
+
+    /** Builder class for {@link DeviceSettingState}. */
+    public static final class Builder {
+        private int mSettingId;
+        private DeviceSettingPreferenceState mSettingPreferenceState;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        public Builder() {}
+
+        /**
+         * Sets the setting ID, as defined by IntDef {@link DeviceSettingId}.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setSettingId(@DeviceSettingId int settingId) {
+            mSettingId = settingId;
+            return this;
+        }
+
+        /**
+         * Sets the setting preference state.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setPreferenceState(
+                @NonNull DeviceSettingPreferenceState settingPreferenceState) {
+            mSettingPreferenceState = settingPreferenceState;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /** Build the object. */
+        @NonNull
+        public DeviceSettingState build() {
+            return new DeviceSettingState(mSettingId, mSettingPreferenceState, mExtras);
+        }
+    }
+
+    /**
+     * Gets the setting ID, as defined by IntDef {@link DeviceSettingId}.
+     *
+     * @return the setting ID.
+     */
+    @DeviceSettingId
+    public int getSettingId() {
+        return mSettingId;
+    }
+
+    /**
+     * Gets the preference state of the setting.
+     *
+     * @return the setting preference state.
+     */
+    @NonNull
+    public DeviceSettingPreferenceState getPreferenceState() {
+        return mPreferenceState;
+    }
+
+    /**
+     * Gets the extras Bundle.
+     *
+     * @return Returns a Bundle object.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java
new file mode 100644
index 0000000..ee4d90f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.SOURCE)
+@IntDef(
+        value = {
+            DeviceSettingType.DEVICE_SETTING_TYPE_UNKNOWN,
+            DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH,
+            DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE,
+        },
+        open = true)
+public @interface DeviceSettingType {
+    /** Device setting type is unknown. */
+    int DEVICE_SETTING_TYPE_UNKNOWN = 0;
+
+    /** Device setting type is action/switch preference. */
+    int DEVICE_SETTING_TYPE_ACTION_SWITCH = 1;
+
+    /** Device setting type is multi-toggle preference. */
+    int DEVICE_SETTING_TYPE_MULTI_TOGGLE = 2;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.aidl
new file mode 100644
index 0000000..3201d13
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+parcelable DeviceSettingsConfig;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt
new file mode 100644
index 0000000..c8a2e9c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * A data class representing a bluetooth device details config.
+ *
+ * @property mainContentItems The setting items to be shown in main page.
+ * @property moreSettingsItems The setting items to be shown in more settings page.
+ * @property moreSettingsFooter The footer in more settings page.
+ * @property extras Extra bundle
+ */
+data class DeviceSettingsConfig(
+    val mainContentItems: List<DeviceSettingItem>,
+    val moreSettingsItems: List<DeviceSettingItem>,
+    val moreSettingsFooter: String,
+    val extras: Bundle = Bundle.EMPTY,
+) : Parcelable {
+
+    override fun describeContents(): Int = 0
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.run {
+            writeTypedList(mainContentItems)
+            writeTypedList(moreSettingsItems)
+            writeString(moreSettingsFooter)
+            writeBundle(extras)
+        }
+    }
+
+    companion object {
+        @JvmField
+        val CREATOR: Parcelable.Creator<DeviceSettingsConfig> =
+            object : Parcelable.Creator<DeviceSettingsConfig> {
+                override fun createFromParcel(parcel: Parcel): DeviceSettingsConfig =
+                    parcel.run {
+                        DeviceSettingsConfig(
+                            mainContentItems =
+                                arrayListOf<DeviceSettingItem>().also {
+                                    readTypedList(it, DeviceSettingItem.CREATOR)
+                                },
+                            moreSettingsItems =
+                                arrayListOf<DeviceSettingItem>().also {
+                                    readTypedList(it, DeviceSettingItem.CREATOR)
+                                },
+                            moreSettingsFooter = readString()!!,
+                            extras = readBundle((Bundle::class.java.classLoader))!!,
+                        )
+                    }
+
+                override fun newArray(size: Int): Array<DeviceSettingsConfig?> {
+                    return arrayOfNulls(size)
+                }
+            }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsListener.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsListener.aidl
new file mode 100644
index 0000000..385a780
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsListener.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import com.android.settingslib.bluetooth.devicesettings.DeviceSetting;
+
+interface IDeviceSettingsListener {
+   oneway void onDeviceSettingsChanged(in List<DeviceSetting> settings) = 0;
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
new file mode 100644
index 0000000..d5efac9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import com.android.settingslib.bluetooth.devicesettings.DeviceInfo;
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState;
+import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener;
+
+oneway interface IDeviceSettingsProviderService {
+   void registerDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+   void unregisterDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+   void updateDeviceSettings(in DeviceInfo device, in DeviceSettingState params);
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreference.java
new file mode 100644
index 0000000..01bb6f0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreference.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/** A data class representing a multi-toggle preference. */
+public class MultiTogglePreference extends DeviceSettingPreference implements Parcelable {
+    private final String mTitle;
+    private final ImmutableList<ToggleInfo> mToggleInfos;
+    private final int mState;
+    private final boolean mIsAllowedChangingState;
+    private final Bundle mExtras;
+
+    MultiTogglePreference(
+            @NonNull String title,
+            List<ToggleInfo> toggleInfos,
+            int state,
+            boolean allowChangingState,
+            Bundle extras) {
+        super(DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE);
+        validate(title, state);
+        mTitle = title;
+        mToggleInfos = ImmutableList.copyOf(toggleInfos);
+        mState = state;
+        mIsAllowedChangingState = allowChangingState;
+        mExtras = extras;
+    }
+
+    private static void validate(String title, int state) {
+        if (Objects.isNull(title)) {
+            throw new IllegalArgumentException("Title must be set");
+        }
+        if (state < 0) {
+            throw new IllegalArgumentException("State must be a non-negative integer");
+        }
+    }
+
+    /** Read a {@link MultiTogglePreference} from {@link Parcel}. */
+    @NonNull
+    public static MultiTogglePreference readFromParcel(@NonNull Parcel in) {
+        String title = in.readString();
+        List<ToggleInfo> toggleInfos = new ArrayList<>();
+        in.readTypedList(toggleInfos, ToggleInfo.CREATOR);
+        int state = in.readInt();
+        boolean allowChangingState = in.readBoolean();
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new MultiTogglePreference(title, toggleInfos, state, allowChangingState, extras);
+    }
+
+    public static final Creator<MultiTogglePreference> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public MultiTogglePreference createFromParcel(@NonNull Parcel in) {
+                    in.readInt();
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public MultiTogglePreference[] newArray(int size) {
+                    return new MultiTogglePreference[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mTitle);
+        dest.writeTypedList(mToggleInfos, flags);
+        dest.writeInt(mState);
+        dest.writeBoolean(mIsAllowedChangingState);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link MultiTogglePreference}. */
+    public static final class Builder {
+        private String mTitle;
+        private ImmutableList.Builder<ToggleInfo> mToggleInfos = new ImmutableList.Builder<>();
+        private int mState;
+        private boolean mAllowChangingState;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * Sets the title of the preference.
+         *
+         * @param title The title of the preference.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setTitle(@NonNull String title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Adds a toggle in the preference.
+         *
+         * @param toggleInfo The toggle to add.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder addToggleInfo(@NonNull ToggleInfo toggleInfo) {
+            mToggleInfos.add(toggleInfo);
+            return this;
+        }
+
+        /**
+         * Sets the state of the preference.
+         *
+         * @param state The index of the enabled toggle.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setState(int state) {
+            mState = state;
+            return this;
+        }
+
+        /**
+         * Sets whether state can be changed by user.
+         *
+         * @param allowChangingState Whether user is allowed to change state.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setAllowChangingState(boolean allowChangingState) {
+            mAllowChangingState = allowChangingState;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link ToggleInfo} object.
+         *
+         * @return Returns the built {@link ToggleInfo} object.
+         */
+        @NonNull
+        public MultiTogglePreference build() {
+            return new MultiTogglePreference(
+                    mTitle, mToggleInfos.build(), mState, mAllowChangingState, mExtras);
+        }
+    }
+
+    /**
+     * Gets the title of the preference.
+     *
+     * @return The title.
+     */
+    @NonNull
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Gets the state of the {@link MultiTogglePreference}.
+     *
+     * @return Returns the index of the enabled toggle.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Gets the toggle list in the preference.
+     *
+     * @return the toggle list.
+     */
+    @NonNull
+    public List<ToggleInfo> getToggleInfos() {
+        return mToggleInfos;
+    }
+
+    /**
+     * Gets whether the state can be changed by user.
+     *
+     * @return Whether the state can be changed by user.
+     */
+    public boolean isAllowedChangingState() {
+        return mIsAllowedChangingState;
+    }
+
+    /**
+     * Gets the extras Bundle.
+     *
+     * @return Returns a Bundle object.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceState.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceState.java
new file mode 100644
index 0000000..239df0b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceState.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/** A data class representing a multi-toggle preference state. */
+public class MultiTogglePreferenceState extends DeviceSettingPreferenceState implements Parcelable {
+    private final int mState;
+    private final Bundle mExtras;
+
+    MultiTogglePreferenceState(int state, @NonNull Bundle extras) {
+        super(DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE);
+        mState = state;
+        mExtras = extras;
+    }
+
+    /** Reads a {@link MultiTogglePreferenceState} from {@link Parcel}. */
+    @NonNull
+    public static MultiTogglePreferenceState readFromParcel(@NonNull Parcel in) {
+        int state = in.readInt();
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new MultiTogglePreferenceState(state, extras);
+    }
+
+    public static final Creator<MultiTogglePreferenceState> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public MultiTogglePreferenceState createFromParcel(@NonNull Parcel in) {
+                    in.readInt();
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public MultiTogglePreferenceState[] newArray(int size) {
+                    return new MultiTogglePreferenceState[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeInt(mState);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link MultiTogglePreferenceState}. */
+    public static final class Builder {
+        private int mState;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        public Builder() {}
+
+        /**
+         * Sets the state of {@link MultiTogglePreference}.
+         *
+         * @return Returns the index of enabled toggle.
+         */
+        @NonNull
+        public Builder setState(int state) {
+            mState = state;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /** Builds the object. */
+        @NonNull
+        public MultiTogglePreferenceState build() {
+            return new MultiTogglePreferenceState(mState, mExtras);
+        }
+    }
+
+    /**
+     * Gets the state of the {@link MultiTogglePreference}.
+     *
+     * @return Returns the index of the enabled toggle.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Gets the extras Bundle.
+     *
+     * @return Returns a Bundle object.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ToggleInfo.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ToggleInfo.java
new file mode 100644
index 0000000..7dcf3aa
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/ToggleInfo.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/** A data class representing a toggle in {@link MultiTogglePreference}. */
+public class ToggleInfo implements Parcelable {
+    private final String mLabel;
+    private final Bitmap mIcon;
+    private final Bundle mExtras;
+
+    ToggleInfo(@NonNull String label, @NonNull Bitmap icon, @NonNull Bundle extras) {
+        validate(label, icon);
+        mLabel = label;
+        mIcon = icon;
+        mExtras = extras;
+    }
+
+    private static void validate(String label, Bitmap icon) {
+        if (Objects.isNull(label)) {
+            throw new IllegalArgumentException("Label must be set");
+        }
+        if (Objects.isNull(icon)) {
+            throw new IllegalArgumentException("Icon must be set");
+        }
+    }
+
+    /** Read a {@link ToggleInfo} instance from {@link Parcel}. */
+    @NonNull
+    public static ToggleInfo readFromParcel(@NonNull Parcel in) {
+        String label = in.readString();
+        Bitmap icon = in.readParcelable(Bitmap.class.getClassLoader());
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new ToggleInfo(label, icon, extras);
+    }
+
+    public static final Creator<ToggleInfo> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public ToggleInfo createFromParcel(@NonNull Parcel in) {
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public ToggleInfo[] newArray(int size) {
+                    return new ToggleInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mLabel);
+        dest.writeParcelable(mIcon, flags);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link ToggleInfo}. */
+    public static final class Builder {
+        private Bitmap mIcon;
+        private String mLabel;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * Sets the label of the toggle.
+         *
+         * @param label The label of the toggle.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setLabel(@NonNull String label) {
+            mLabel = label;
+            return this;
+        }
+
+        /**
+         * Sets the icon of the toggle.
+         *
+         * @param icon The icon of the toggle.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setIcon(@NonNull Bitmap icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link ToggleInfo} object.
+         *
+         * @return Returns the built {@link ToggleInfo} object.
+         */
+        @NonNull
+        public ToggleInfo build() {
+            return new ToggleInfo(mLabel, mIcon, mExtras);
+        }
+    }
+
+    /**
+     * Gets the label of the toggle.
+     *
+     * @return the label to be shown under the toggle
+     */
+    @NonNull
+    public String getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Gets the icon of the toggle.
+     *
+     * @return the icon in toggle
+     */
+    @NonNull
+    public Bitmap getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Gets the extras Bundle.
+     *
+     * @return Returns a Bundle object.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceStateTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceStateTest.java
new file mode 100644
index 0000000..e8e1556
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceStateTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ActionSwitchPreferenceStateTest {
+
+    @Test
+    public void getMethods() {
+        ActionSwitchPreferenceState state1 =
+                new ActionSwitchPreferenceState.Builder()
+                        .setChecked(true)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+        ActionSwitchPreferenceState state2 =
+                new ActionSwitchPreferenceState.Builder()
+                        .setChecked(false)
+                        .setExtras(buildBundle("key2", "value2"))
+                        .build();
+
+        assertThat(state1.getChecked()).isTrue();
+        assertThat(state2.getChecked()).isFalse();
+        assertThat(state1.getExtras().getString("key1")).isEqualTo("value1");
+        assertThat(state2.getExtras().getString("key2")).isEqualTo("value2");
+    }
+
+    @Test
+    public void parcelOperation_notChecked() {
+        ActionSwitchPreferenceState state =
+                new ActionSwitchPreferenceState.Builder()
+                        .setChecked(false)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        ActionSwitchPreferenceState fromParcel = writeAndRead(state);
+
+        assertThat(fromParcel.getChecked()).isEqualTo(state.getChecked());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(state.getExtras().getString("key1"));
+    }
+
+    @Test
+    public void parcelOperation_checked() {
+        ActionSwitchPreferenceState state =
+                new ActionSwitchPreferenceState.Builder()
+                        .setChecked(true)
+                        .setExtras(buildBundle("key2", "value2"))
+                        .build();
+
+        ActionSwitchPreferenceState fromParcel = writeAndRead(state);
+
+        assertThat(fromParcel.getChecked()).isEqualTo(state.getChecked());
+        assertThat(fromParcel.getExtras().getString("key2"))
+                .isEqualTo(state.getExtras().getString("key2"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private ActionSwitchPreferenceState writeAndRead(ActionSwitchPreferenceState state) {
+        Parcel parcel = Parcel.obtain();
+        state.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        ActionSwitchPreferenceState fromParcel =
+                ActionSwitchPreferenceState.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceTest.java
new file mode 100644
index 0000000..354d0f6
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ActionSwitchPreferenceTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ActionSwitchPreferenceTest {
+
+    @Test
+    public void build_withoutTitle_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    ActionSwitchPreference unused =
+                            new ActionSwitchPreference.Builder().setSummary("summary").build();
+                });
+    }
+
+    @Test
+    public void build_withTitle_successfully() {
+        ActionSwitchPreference unused =
+                new ActionSwitchPreference.Builder().setTitle("title").build();
+    }
+
+    @Test
+    public void build_withAllFields_successfully() {
+        ActionSwitchPreference unused =
+                new ActionSwitchPreference.Builder()
+                        .setTitle("title")
+                        .setSummary("summary")
+                        .setIntent(new Intent("intent_action"))
+                        .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
+                        .setHasSwitch(true)
+                        .setChecked(true)
+                        .setAllowedChangingState(true)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+    }
+
+    @Test
+    public void getMethods() {
+        Intent intent = new Intent("intent_action");
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        ActionSwitchPreference preference = builder().setIcon(icon).setIntent(intent).build();
+
+        assertThat(preference.getTitle()).isEqualTo("title");
+        assertThat(preference.getSummary()).isEqualTo("summary");
+        assertThat(preference.getIcon()).isSameInstanceAs(icon);
+        assertThat(preference.getIntent()).isSameInstanceAs(intent);
+        assertThat(preference.hasSwitch()).isTrue();
+        assertThat(preference.getChecked()).isTrue();
+        assertThat(preference.isAllowedChangingState()).isTrue();
+        assertThat(preference.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation() {
+        Intent intent = new Intent("intent_action");
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        ActionSwitchPreference preference = builder().setIcon(icon).setIntent(intent).build();
+
+        ActionSwitchPreference fromParcel = writeAndRead(preference);
+
+        assertThat(fromParcel.getTitle()).isEqualTo(preference.getTitle());
+        assertThat(fromParcel.getSummary()).isEqualTo(preference.getSummary());
+        assertThat(fromParcel.getIcon().sameAs(preference.getIcon())).isTrue();
+        assertThat(fromParcel.getIntent().getAction()).isSameInstanceAs("intent_action");
+        assertThat(fromParcel.hasSwitch()).isEqualTo(preference.hasSwitch());
+        assertThat(fromParcel.getChecked()).isEqualTo(preference.getChecked());
+        assertThat(fromParcel.isAllowedChangingState())
+                .isEqualTo(preference.isAllowedChangingState());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(preference.getExtras().getString("key1"));
+    }
+
+    @Test
+    public void parcelOperation_noIntent() {
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        ActionSwitchPreference preference = builder().setIcon(icon).setIntent(null).build();
+
+        ActionSwitchPreference fromParcel = writeAndRead(preference);
+
+        assertThat(fromParcel.getTitle()).isEqualTo(preference.getTitle());
+        assertThat(fromParcel.getSummary()).isEqualTo(preference.getSummary());
+        assertThat(fromParcel.getIcon().sameAs(preference.getIcon())).isTrue();
+        assertThat(preference.getIntent()).isNull();
+        assertThat(fromParcel.hasSwitch()).isEqualTo(preference.hasSwitch());
+        assertThat(fromParcel.getChecked()).isEqualTo(preference.getChecked());
+        assertThat(fromParcel.isAllowedChangingState())
+                .isEqualTo(preference.isAllowedChangingState());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(preference.getExtras().getString("key1"));
+    }
+
+    @Test
+    public void parcelOperation_noIcon() {
+        Intent intent = new Intent("intent_action");
+        ActionSwitchPreference preference = builder().setIcon(null).setIntent(intent).build();
+
+        ActionSwitchPreference fromParcel = writeAndRead(preference);
+
+        assertThat(fromParcel.getTitle()).isEqualTo(preference.getTitle());
+        assertThat(fromParcel.getSummary()).isEqualTo(preference.getSummary());
+        assertThat(fromParcel.getIcon()).isNull();
+        assertThat(fromParcel.getIntent().getAction()).isSameInstanceAs("intent_action");
+        assertThat(fromParcel.hasSwitch()).isEqualTo(preference.hasSwitch());
+        assertThat(fromParcel.getChecked()).isEqualTo(preference.getChecked());
+        assertThat(fromParcel.isAllowedChangingState())
+                .isEqualTo(preference.isAllowedChangingState());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(preference.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private ActionSwitchPreference writeAndRead(ActionSwitchPreference preference) {
+        Parcel parcel = Parcel.obtain();
+        preference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        ActionSwitchPreference fromParcel = ActionSwitchPreference.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+
+    private ActionSwitchPreference.Builder builder() {
+        return new ActionSwitchPreference.Builder()
+                .setTitle("title")
+                .setSummary("summary")
+                .setHasSwitch(true)
+                .setChecked(true)
+                .setAllowedChangingState(true)
+                .setExtras(buildBundle("key1", "value1"));
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfoTest.java
new file mode 100644
index 0000000..fd5b075
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceInfoTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class DeviceInfoTest {
+    @Test
+    public void build_withoutBluetoothAddress_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    DeviceInfo unused =
+                            new DeviceInfo.Builder()
+                                    .setExtras(buildBundle("key1", "value1"))
+                                    .build();
+                });
+    }
+
+    @Test
+    public void build_withoutExtra_successfully() {
+        DeviceInfo unused = new DeviceInfo.Builder().setBluetoothAddress("12:34:56:78").build();
+    }
+
+    @Test
+    public void build_withAllFields_successfully() {
+        DeviceInfo unused =
+                new DeviceInfo.Builder()
+                        .setBluetoothAddress("12:34:56:78")
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+    }
+
+    @Test
+    public void getMethods() {
+        DeviceInfo info =
+                new DeviceInfo.Builder()
+                        .setBluetoothAddress("12:34:56:78")
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(info.getBluetoothAddress()).isEqualTo("12:34:56:78");
+        assertThat(info.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation() {
+        DeviceInfo info =
+                new DeviceInfo.Builder()
+                        .setBluetoothAddress("12:34:56:78")
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceInfo fromParcel = writeAndRead(info);
+
+        assertThat(fromParcel.getBluetoothAddress()).isEqualTo(info.getBluetoothAddress());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(info.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private DeviceInfo writeAndRead(DeviceInfo state) {
+        Parcel parcel = Parcel.obtain();
+        state.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        DeviceInfo fromParcel = DeviceInfo.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItemTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItemTest.kt
new file mode 100644
index 0000000..56e9b6c
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItemTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DeviceSettingItemTest {
+
+    @Test
+    fun parcelOperation() {
+        val item =
+            DeviceSettingItem(
+                settingId = 1,
+                packageName = "package_name",
+                className = "class_name",
+                intentAction = "intent_action",
+                extras = Bundle().apply { putString("key1", "value1") },
+            )
+
+        val fromParcel = writeAndRead(item)
+
+        assertThat(fromParcel.settingId).isEqualTo(item.settingId)
+        assertThat(fromParcel.packageName).isEqualTo(item.packageName)
+        assertThat(fromParcel.className).isEqualTo(item.className)
+        assertThat(fromParcel.intentAction).isEqualTo(item.intentAction)
+        assertThat(fromParcel.extras.getString("key1")).isEqualTo(item.extras.getString("key1"))
+    }
+
+    private fun writeAndRead(item: DeviceSettingItem): DeviceSettingItem {
+        val parcel = Parcel.obtain()
+        item.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+        return DeviceSettingItem.CREATOR.createFromParcel(parcel)
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingStateTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingStateTest.java
new file mode 100644
index 0000000..12b7a0f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingStateTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class DeviceSettingStateTest {
+    private static final ActionSwitchPreferenceState ACTION_SWITCH_PREFERENCE_STATE =
+            new ActionSwitchPreferenceState.Builder().setChecked(true).build();
+    private static final MultiTogglePreferenceState MULTI_TOGGLE_PREFERENCE_STATE =
+            new MultiTogglePreferenceState.Builder().setState(123).build();
+
+    @Test
+    public void build_withoutPreferenceState_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    DeviceSettingState unused =
+                            new DeviceSettingState.Builder()
+                                    .setSettingId(123)
+                                    .setExtras(buildBundle("key1", "value1"))
+                                    .build();
+                });
+    }
+
+    @Test
+    public void build_withoutExtra_successfully() {
+        DeviceSettingState unused =
+                new DeviceSettingState.Builder()
+                        .setSettingId(123)
+                        .setPreferenceState(ACTION_SWITCH_PREFERENCE_STATE)
+                        .build();
+    }
+
+    @Test
+    public void build_withAllFields_successfully() {
+        DeviceSettingState unused =
+                new DeviceSettingState.Builder()
+                        .setSettingId(123)
+                        .setPreferenceState(ACTION_SWITCH_PREFERENCE_STATE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+    }
+
+    @Test
+    public void getMethods_actionSwitchPreferenceState() {
+        DeviceSettingState state =
+                new DeviceSettingState.Builder()
+                        .setSettingId(123)
+                        .setPreferenceState(ACTION_SWITCH_PREFERENCE_STATE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(state.getSettingId()).isEqualTo(123);
+        assertThat(state.getPreferenceState()).isInstanceOf(ActionSwitchPreferenceState.class);
+        assertThat(state.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void getMethods_multiTogglePreference() {
+        DeviceSettingState state =
+                new DeviceSettingState.Builder()
+                        .setSettingId(123)
+                        .setPreferenceState(MULTI_TOGGLE_PREFERENCE_STATE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(state.getSettingId()).isEqualTo(123);
+        assertThat(state.getPreferenceState()).isInstanceOf(MultiTogglePreferenceState.class);
+        assertThat(state.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation_actionSwitchPreferenceState() {
+        DeviceSettingState state =
+                new DeviceSettingState.Builder()
+                        .setSettingId(123)
+                        .setPreferenceState(ACTION_SWITCH_PREFERENCE_STATE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSettingState fromParcel = writeAndRead(state);
+
+        assertThat(fromParcel.getSettingId()).isEqualTo(state.getSettingId());
+        assertThat(fromParcel.getPreferenceState()).isInstanceOf(ActionSwitchPreferenceState.class);
+        assertThat(fromParcel.getPreferenceState().getSettingType())
+                .isEqualTo(DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH);
+        assertThat(((ActionSwitchPreferenceState) fromParcel.getPreferenceState()).getChecked())
+                .isTrue();
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(state.getExtras().getString("key1"));
+    }
+
+    @Test
+    public void parcelOperation_multiTogglePreferenceState() {
+        DeviceSettingState state =
+                new DeviceSettingState.Builder()
+                        .setSettingId(123)
+                        .setPreferenceState(MULTI_TOGGLE_PREFERENCE_STATE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSettingState fromParcel = writeAndRead(state);
+
+        assertThat(fromParcel.getSettingId()).isEqualTo(state.getSettingId());
+        assertThat(fromParcel.getPreferenceState()).isInstanceOf(MultiTogglePreferenceState.class);
+        assertThat(fromParcel.getPreferenceState().getSettingType())
+                .isEqualTo(DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE);
+        assertThat(((MultiTogglePreferenceState) fromParcel.getPreferenceState()).getState())
+                .isEqualTo(123);
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(state.getExtras().getString("key1"));
+    }
+
+    @Test
+    public void parcelOperation_unknownPreferenceState() {
+        DeviceSettingState state =
+                new DeviceSettingState.Builder()
+                        .setSettingId(123)
+                        .setPreferenceState(new DeviceSettingPreferenceState(123) {})
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSettingState fromParcel = writeAndRead(state);
+
+        assertThat(fromParcel.getSettingId()).isEqualTo(state.getSettingId());
+        assertThat(fromParcel.getPreferenceState())
+                .isSameInstanceAs(DeviceSettingPreferenceState.UNKNOWN);
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(state.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private DeviceSettingState writeAndRead(DeviceSettingState state) {
+        Parcel parcel = Parcel.obtain();
+        state.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        DeviceSettingState fromParcel = DeviceSettingState.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingTest.java
new file mode 100644
index 0000000..98dc54b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class DeviceSettingTest {
+    private static final ActionSwitchPreference ACTION_SWITCH_PREFERENCE =
+            new ActionSwitchPreference.Builder().setTitle("action_switch_preference").build();
+    private static final MultiTogglePreference MULTI_TOGGLE_PREFERENCE =
+            new MultiTogglePreference.Builder().setTitle("multi_toggle_preference").build();
+
+    @Test
+    public void build_withoutPreference_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    DeviceSetting unused =
+                            new DeviceSetting.Builder()
+                                    .setSettingId(123)
+                                    .setExtras(buildBundle("key1", "value1"))
+                                    .build();
+                });
+    }
+
+    @Test
+    public void build_withoutExtra_successfully() {
+        DeviceSetting unused =
+                new DeviceSetting.Builder()
+                        .setSettingId(123)
+                        .setPreference(ACTION_SWITCH_PREFERENCE)
+                        .build();
+    }
+
+    @Test
+    public void build_withAllFields_successfully() {
+        DeviceSetting unused =
+                new DeviceSetting.Builder()
+                        .setSettingId(123)
+                        .setPreference(ACTION_SWITCH_PREFERENCE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+    }
+
+    @Test
+    public void getMethods_actionSwitchPreference() {
+        DeviceSetting setting =
+                new DeviceSetting.Builder()
+                        .setSettingId(123)
+                        .setPreference(ACTION_SWITCH_PREFERENCE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(setting.getSettingId()).isEqualTo(123);
+        assertThat(setting.getPreference()).isInstanceOf(ActionSwitchPreference.class);
+        assertThat(setting.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void getMethods_multiTogglePreference() {
+        DeviceSetting setting =
+                new DeviceSetting.Builder()
+                        .setSettingId(123)
+                        .setPreference(MULTI_TOGGLE_PREFERENCE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(setting.getSettingId()).isEqualTo(123);
+        assertThat(setting.getPreference()).isInstanceOf(MultiTogglePreference.class);
+        assertThat(setting.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation_actionSwitchPreference() {
+        DeviceSetting setting =
+                new DeviceSetting.Builder()
+                        .setSettingId(123)
+                        .setPreference(ACTION_SWITCH_PREFERENCE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSetting fromParcel = writeAndRead(setting);
+
+        assertThat(fromParcel.getSettingId()).isEqualTo(setting.getSettingId());
+        assertThat(fromParcel.getPreference()).isInstanceOf(ActionSwitchPreference.class);
+        assertThat(fromParcel.getPreference().getSettingType())
+                .isEqualTo(DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH);
+        assertThat(((ActionSwitchPreference) fromParcel.getPreference()).getTitle())
+                .isEqualTo("action_switch_preference");
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(setting.getExtras().getString("key1"));
+    }
+
+    @Test
+    public void parcelOperation_multiTogglePreference() {
+        DeviceSetting setting =
+                new DeviceSetting.Builder()
+                        .setSettingId(123)
+                        .setPreference(MULTI_TOGGLE_PREFERENCE)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSetting fromParcel = writeAndRead(setting);
+
+        assertThat(fromParcel.getSettingId()).isEqualTo(setting.getSettingId());
+        assertThat(fromParcel.getPreference()).isInstanceOf(MultiTogglePreference.class);
+        assertThat(fromParcel.getPreference().getSettingType())
+                .isEqualTo(DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE);
+        assertThat(((MultiTogglePreference) fromParcel.getPreference()).getTitle())
+                .isEqualTo("multi_toggle_preference");
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(setting.getExtras().getString("key1"));
+    }
+
+    @Test
+    public void parcelOperation_unknownPreference() {
+        DeviceSetting setting =
+                new DeviceSetting.Builder()
+                        .setSettingId(123)
+                        .setPreference(new DeviceSettingPreference(123) {})
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSetting fromParcel = writeAndRead(setting);
+
+        assertThat(fromParcel.getSettingId()).isEqualTo(setting.getSettingId());
+        assertThat(fromParcel.getPreference()).isSameInstanceAs(DeviceSettingPreference.UNKNOWN);
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(setting.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private DeviceSetting writeAndRead(DeviceSetting state) {
+        Parcel parcel = Parcel.obtain();
+        state.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        DeviceSetting fromParcel = DeviceSetting.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt
new file mode 100644
index 0000000..2b29a6e
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DeviceSettingsConfigTest {
+
+    @Test
+    fun parcelOperation() {
+        val config =
+            DeviceSettingsConfig(
+                mainContentItems =
+                    listOf(
+                        DeviceSettingItem(
+                            1,
+                            "package_name_1",
+                            "class_name_1",
+                            "intent_action_1",
+                            Bundle()
+                        )
+                    ),
+                moreSettingsItems =
+                    listOf(
+                        DeviceSettingItem(
+                            2,
+                            "package_name_2",
+                            "class_name_2",
+                            "intent_action_2",
+                            Bundle()
+                        )
+                    ),
+                moreSettingsFooter = "footer",
+                extras = Bundle().apply { putString("key1", "value1") },
+            )
+
+        val fromParcel = writeAndRead(config)
+
+        assertThat(fromParcel.mainContentItems.stream().map { it.settingId }.toList())
+            .containsExactly(1)
+        assertThat(fromParcel.mainContentItems.stream().map { it.packageName }.toList())
+            .containsExactly("package_name_1")
+        assertThat(fromParcel.mainContentItems.stream().map { it.className }.toList())
+            .containsExactly("class_name_1")
+        assertThat(fromParcel.mainContentItems.stream().map { it.intentAction }.toList())
+            .containsExactly("intent_action_1")
+        assertThat(fromParcel.moreSettingsItems.stream().map { it.settingId }.toList())
+            .containsExactly(2)
+        assertThat(fromParcel.moreSettingsItems.stream().map { it.packageName }.toList())
+            .containsExactly("package_name_2")
+        assertThat(fromParcel.moreSettingsItems.stream().map { it.className }.toList())
+            .containsExactly("class_name_2")
+        assertThat(fromParcel.moreSettingsItems.stream().map { it.intentAction }.toList())
+            .containsExactly("intent_action_2")
+        assertThat(fromParcel.moreSettingsFooter).isEqualTo(config.moreSettingsFooter)
+    }
+
+    private fun writeAndRead(item: DeviceSettingsConfig): DeviceSettingsConfig {
+        val parcel = Parcel.obtain()
+        item.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+        return DeviceSettingsConfig.CREATOR.createFromParcel(parcel)
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceStateTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceStateTest.java
new file mode 100644
index 0000000..2645fc5
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceStateTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class MultiTogglePreferenceStateTest {
+
+    @Test
+    public void getMethods() {
+        MultiTogglePreferenceState state1 =
+                new MultiTogglePreferenceState.Builder()
+                        .setState(1)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+        MultiTogglePreferenceState state2 =
+                new MultiTogglePreferenceState.Builder()
+                        .setState(2)
+                        .setExtras(buildBundle("key2", "value2"))
+                        .build();
+
+        assertThat(state1.getState()).isEqualTo(1);
+        assertThat(state2.getState()).isEqualTo(2);
+        assertThat(state1.getExtras().getString("key1")).isEqualTo("value1");
+        assertThat(state2.getExtras().getString("key2")).isEqualTo("value2");
+    }
+
+    @Test
+    public void parcelOperation() {
+        MultiTogglePreferenceState state =
+                new MultiTogglePreferenceState.Builder()
+                        .setState(123)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        MultiTogglePreferenceState fromParcel = writeAndRead(state);
+
+        assertThat(fromParcel.getState()).isEqualTo(state.getState());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(state.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private MultiTogglePreferenceState writeAndRead(MultiTogglePreferenceState state) {
+        Parcel parcel = Parcel.obtain();
+        state.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        MultiTogglePreferenceState fromParcel =
+                MultiTogglePreferenceState.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceTest.java
new file mode 100644
index 0000000..62fcb5e
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/MultiTogglePreferenceTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class MultiTogglePreferenceTest {
+    private static final Bitmap ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+    private static final ToggleInfo TOGGLE_INFO_1 =
+            new ToggleInfo.Builder().setLabel("label1").setIcon(ICON).build();
+    private static final ToggleInfo TOGGLE_INFO_2 =
+            new ToggleInfo.Builder().setLabel("label2").setIcon(ICON).build();
+
+    @Test
+    public void build_withoutTitle_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    MultiTogglePreference unused =
+                            new MultiTogglePreference.Builder()
+                                    .addToggleInfo(TOGGLE_INFO_1)
+                                    .setState(0)
+                                    .setAllowChangingState(true)
+                                    .setExtras(buildBundle("key1", "value1"))
+                                    .build();
+                });
+    }
+
+    @Test
+    public void build_withNegativeState_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    MultiTogglePreference unused =
+                            new MultiTogglePreference.Builder()
+                                    .setTitle("title")
+                                    .addToggleInfo(TOGGLE_INFO_1)
+                                    .setState(-1)
+                                    .setAllowChangingState(true)
+                                    .setExtras(buildBundle("key1", "value1"))
+                                    .build();
+                });
+    }
+
+    @Test
+    public void build_withoutExtra_successfully() {
+        MultiTogglePreference unused =
+                new MultiTogglePreference.Builder()
+                        .setTitle("title")
+                        .addToggleInfo(TOGGLE_INFO_1)
+                        .addToggleInfo(TOGGLE_INFO_2)
+                        .setState(123)
+                        .setAllowChangingState(true)
+                        .build();
+    }
+
+    @Test
+    public void build_withAllFields_successfully() {
+        MultiTogglePreference unused =
+                new MultiTogglePreference.Builder()
+                        .setTitle("title")
+                        .addToggleInfo(TOGGLE_INFO_1)
+                        .addToggleInfo(TOGGLE_INFO_2)
+                        .setState(123)
+                        .setAllowChangingState(true)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+    }
+
+    @Test
+    public void getMethods() {
+        MultiTogglePreference preference =
+                new MultiTogglePreference.Builder()
+                        .setTitle("title")
+                        .addToggleInfo(TOGGLE_INFO_1)
+                        .addToggleInfo(TOGGLE_INFO_2)
+                        .setState(123)
+                        .setAllowChangingState(true)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(preference.getTitle()).isEqualTo("title");
+        assertThat(preference.getToggleInfos().stream().map(ToggleInfo::getLabel).toList())
+                .containsExactly("label1", "label2");
+        assertThat(preference.getState()).isEqualTo(123);
+        assertThat(preference.isAllowedChangingState()).isTrue();
+        assertThat(preference.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation() {
+        MultiTogglePreference preference =
+                new MultiTogglePreference.Builder()
+                        .setTitle("title")
+                        .addToggleInfo(TOGGLE_INFO_1)
+                        .addToggleInfo(TOGGLE_INFO_2)
+                        .setState(123)
+                        .setAllowChangingState(true)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        MultiTogglePreference fromParcel = writeAndRead(preference);
+
+        assertThat(fromParcel.getTitle()).isEqualTo(preference.getTitle());
+        assertThat(fromParcel.getToggleInfos().stream().map(ToggleInfo::getLabel).toList())
+                .containsExactly("label1", "label2");
+        assertThat(fromParcel.getState()).isEqualTo(preference.getState());
+        assertThat(fromParcel.isAllowedChangingState())
+                .isEqualTo(preference.isAllowedChangingState());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(preference.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private MultiTogglePreference writeAndRead(MultiTogglePreference preference) {
+        Parcel parcel = Parcel.obtain();
+        preference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        MultiTogglePreference fromParcel = MultiTogglePreference.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ToggleInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ToggleInfoTest.java
new file mode 100644
index 0000000..439749a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/ToggleInfoTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ToggleInfoTest {
+
+    @Test
+    public void build_withoutIcon_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    ToggleInfo unused =
+                            new ToggleInfo.Builder()
+                                    .setLabel("label")
+                                    .setExtras(buildBundle("key1", "value1"))
+                                    .build();
+                });
+    }
+
+    @Test
+    public void build_withoutLabel_fail() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> {
+                    ToggleInfo unused =
+                            new ToggleInfo.Builder()
+                                    .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
+                                    .setExtras(buildBundle("key1", "value1"))
+                                    .build();
+                });
+    }
+
+    @Test
+    public void build_withoutExtra_successfully() {
+        ToggleInfo unused =
+                new ToggleInfo.Builder()
+                        .setLabel("label")
+                        .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
+                        .build();
+    }
+
+    @Test
+    public void build_withAllFields_successfully() {
+        ToggleInfo unused =
+                new ToggleInfo.Builder()
+                        .setLabel("label")
+                        .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+    }
+
+    @Test
+    public void getMethods() {
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        ToggleInfo info =
+                new ToggleInfo.Builder()
+                        .setLabel("label")
+                        .setIcon(icon)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(info.getLabel()).isEqualTo("label");
+        assertThat(info.getIcon()).isSameInstanceAs(icon);
+        assertThat(info.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation() {
+        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        ToggleInfo info =
+                new ToggleInfo.Builder()
+                        .setLabel("label")
+                        .setIcon(icon)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        ToggleInfo fromParcel = writeAndRead(info);
+
+        assertThat(fromParcel.getLabel()).isEqualTo(info.getLabel());
+        assertThat(fromParcel.getIcon().sameAs(info.getIcon())).isTrue();
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(info.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private ToggleInfo writeAndRead(ToggleInfo state) {
+        Parcel parcel = Parcel.obtain();
+        state.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        ToggleInfo fromParcel = ToggleInfo.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}