Loading core/java/android/provider/Settings.java +7 −0 Original line number Diff line number Diff line Loading @@ -9040,6 +9040,13 @@ public final class Settings { */ public static final String SPATIAL_AUDIO_ENABLED = "spatial_audio_enabled"; /** * Internal collection of audio device inventory items * The device item stored are {@link com.android.server.audio.AdiDeviceState} * @hide */ public static final String AUDIO_DEVICE_INVENTORY = "audio_device_inventory"; /** * Indicates whether notification display on the lock screen is enabled. * <p> Loading media/java/android/media/AudioDeviceAttributes.java +22 −2 Original line number Diff line number Diff line Loading @@ -64,8 +64,7 @@ public final class AudioDeviceAttributes implements Parcelable { /** * The unique address of the device. Some devices don't have addresses, only an empty string. */ private final @NonNull String mAddress; private @NonNull String mAddress; /** * Is input or output device */ Loading Loading @@ -133,6 +132,18 @@ public final class AudioDeviceAttributes implements Parcelable { mNativeType = nativeType; } /** * @hide * Copy Constructor. * @param ada the copied AudioDeviceAttributes */ public AudioDeviceAttributes(AudioDeviceAttributes ada) { mRole = ada.getRole(); mType = ada.getType(); mAddress = ada.getAddress(); mNativeType = ada.getInternalType(); } /** * @hide * Returns the role of a device Loading Loading @@ -163,6 +174,15 @@ public final class AudioDeviceAttributes implements Parcelable { return mAddress; } /** * @hide * Sets the device address. Only used by audio service. */ public void setAddress(@NonNull String address) { Objects.requireNonNull(address); mAddress = address; } /** * @hide * Returns the internal device type of a device Loading media/java/android/media/AudioSystem.java +63 −0 Original line number Diff line number Diff line Loading @@ -1120,6 +1120,9 @@ public class AudioSystem public static final Set<Integer> DEVICE_IN_ALL_SCO_SET; /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_USB_SET; /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_BLE_SET; static { DEVICE_IN_ALL_SET = new HashSet<>(); DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION); Loading Loading @@ -1159,6 +1162,66 @@ public class AudioSystem DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET); DEVICE_IN_ALL_BLE_SET = new HashSet<>(); DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET); } /** @hide */ public static boolean isBluetoothDevice(int deviceType) { return isBluetoothA2dpOutDevice(deviceType) || isBluetoothScoDevice(deviceType) || isBluetoothLeDevice(deviceType); } /** @hide */ public static boolean isBluetoothOutDevice(int deviceType) { return isBluetoothA2dpOutDevice(deviceType) || isBluetoothScoOutDevice(deviceType) || isBluetoothLeOutDevice(deviceType); } /** @hide */ public static boolean isBluetoothInDevice(int deviceType) { return isBluetoothScoInDevice(deviceType) || isBluetoothLeInDevice(deviceType); } /** @hide */ public static boolean isBluetoothA2dpOutDevice(int deviceType) { return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothScoOutDevice(int deviceType) { return DEVICE_OUT_ALL_SCO_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothScoInDevice(int deviceType) { return DEVICE_IN_ALL_SCO_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothScoDevice(int deviceType) { return isBluetoothScoOutDevice(deviceType) || isBluetoothScoInDevice(deviceType); } /** @hide */ public static boolean isBluetoothLeOutDevice(int deviceType) { return DEVICE_OUT_ALL_BLE_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothLeInDevice(int deviceType) { return DEVICE_IN_ALL_BLE_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothLeDevice(int deviceType) { return isBluetoothLeOutDevice(deviceType) || isBluetoothLeInDevice(deviceType); } /** @hide */ Loading packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -614,6 +614,7 @@ public class SettingsBackupTest { Settings.Secure.ASSIST_SCREENSHOT_ENABLED, Settings.Secure.ASSIST_STRUCTURE_ENABLED, Settings.Secure.ATTENTIVE_TIMEOUT, Settings.Secure.AUDIO_DEVICE_INVENTORY, // setting not controllable by user Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE, Loading services/core/java/com/android/server/audio/AdiDeviceState.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.server.audio; import static android.media.AudioSystem.DEVICE_NONE; import static android.media.AudioSystem.isBluetoothDevice; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import java.util.Objects; /** * Class representing all devices that were previously or are currently connected. Data is * persisted in {@link android.provider.Settings.Secure} */ /*package*/ final class AdiDeviceState { private static final String TAG = "AS.AdiDeviceState"; private static final String SETTING_FIELD_SEPARATOR = ","; @AudioDeviceInfo.AudioDeviceType private final int mDeviceType; private final int mInternalDeviceType; @NonNull private final String mDeviceAddress; /** Unique device id from internal device type and address. */ private final Pair<Integer, String> mDeviceId; private boolean mSAEnabled; private boolean mHasHeadTracker = false; private boolean mHeadTrackerEnabled; /** * Constructor * * @param deviceType external audio device type * @param internalDeviceType if not set pass {@link DEVICE_NONE}, in this case the * default conversion of the external type will be used * @param address must be non-null for wireless devices * @throws NullPointerException if a null address is passed for a wireless device */ AdiDeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, int internalDeviceType, @Nullable String address) { mDeviceType = deviceType; if (internalDeviceType != DEVICE_NONE) { mInternalDeviceType = internalDeviceType; } else { mInternalDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType); } mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull( address) : ""; mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress); } public Pair<Integer, String> getDeviceId() { return mDeviceId; } @AudioDeviceInfo.AudioDeviceType public int getDeviceType() { return mDeviceType; } public int getInternalDeviceType() { return mInternalDeviceType; } @NonNull public String getDeviceAddress() { return mDeviceAddress; } public void setSAEnabled(boolean sAEnabled) { mSAEnabled = sAEnabled; } public boolean isSAEnabled() { return mSAEnabled; } public void setHeadTrackerEnabled(boolean headTrackerEnabled) { mHeadTrackerEnabled = headTrackerEnabled; } public boolean isHeadTrackerEnabled() { return mHeadTrackerEnabled; } public void setHasHeadTracker(boolean hasHeadTracker) { mHasHeadTracker = hasHeadTracker; } public boolean hasHeadTracker() { return mHasHeadTracker; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } // type check and cast if (getClass() != obj.getClass()) { return false; } final AdiDeviceState sads = (AdiDeviceState) obj; return mDeviceType == sads.mDeviceType && mInternalDeviceType == sads.mInternalDeviceType && mDeviceAddress.equals(sads.mDeviceAddress) // NonNull && mSAEnabled == sads.mSAEnabled && mHasHeadTracker == sads.mHasHeadTracker && mHeadTrackerEnabled == sads.mHeadTrackerEnabled; } @Override public int hashCode() { return Objects.hash(mDeviceType, mInternalDeviceType, mDeviceAddress, mSAEnabled, mHasHeadTracker, mHeadTrackerEnabled); } @Override public String toString() { return "type: " + mDeviceType + " internal type: 0x" + Integer.toHexString(mInternalDeviceType) + " addr: " + mDeviceAddress + " enabled: " + mSAEnabled + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled; } public String toPersistableString() { return (new StringBuilder().append(mDeviceType) .append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress) .append(SETTING_FIELD_SEPARATOR).append(mSAEnabled ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mInternalDeviceType) .toString()); } /** * Gets the max size (including separators) when persisting the elements with * {@link AdiDeviceState#toPersistableString()}. */ public static int getPeristedMaxSize() { return 36; /* (mDeviceType)2 + (mDeviceAddresss)17 + (mInternalDeviceType)9 + (mSAEnabled)1 + (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1 + (SETTINGS_FIELD_SEPARATOR)5 */ } @Nullable public static AdiDeviceState fromPersistedString(@Nullable String persistedString) { if (persistedString == null) { return null; } if (persistedString.isEmpty()) { return null; } String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR); // we may have 5 fields for the legacy AdiDeviceState and 6 containing the internal // device type if (fields.length != 5 && fields.length != 6) { // expecting all fields, fewer may mean corruption, ignore those settings return null; } try { final int deviceType = Integer.parseInt(fields[0]); int internalDeviceType = -1; if (fields.length == 6) { internalDeviceType = Integer.parseInt(fields[5]); } final AdiDeviceState deviceState = new AdiDeviceState(deviceType, internalDeviceType, fields[1]); deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1); deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1); deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1); return deviceState; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e); return null; } } public AudioDeviceAttributes getAudioDeviceAttributes() { return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, mDeviceType, mDeviceAddress); } } Loading
core/java/android/provider/Settings.java +7 −0 Original line number Diff line number Diff line Loading @@ -9040,6 +9040,13 @@ public final class Settings { */ public static final String SPATIAL_AUDIO_ENABLED = "spatial_audio_enabled"; /** * Internal collection of audio device inventory items * The device item stored are {@link com.android.server.audio.AdiDeviceState} * @hide */ public static final String AUDIO_DEVICE_INVENTORY = "audio_device_inventory"; /** * Indicates whether notification display on the lock screen is enabled. * <p> Loading
media/java/android/media/AudioDeviceAttributes.java +22 −2 Original line number Diff line number Diff line Loading @@ -64,8 +64,7 @@ public final class AudioDeviceAttributes implements Parcelable { /** * The unique address of the device. Some devices don't have addresses, only an empty string. */ private final @NonNull String mAddress; private @NonNull String mAddress; /** * Is input or output device */ Loading Loading @@ -133,6 +132,18 @@ public final class AudioDeviceAttributes implements Parcelable { mNativeType = nativeType; } /** * @hide * Copy Constructor. * @param ada the copied AudioDeviceAttributes */ public AudioDeviceAttributes(AudioDeviceAttributes ada) { mRole = ada.getRole(); mType = ada.getType(); mAddress = ada.getAddress(); mNativeType = ada.getInternalType(); } /** * @hide * Returns the role of a device Loading Loading @@ -163,6 +174,15 @@ public final class AudioDeviceAttributes implements Parcelable { return mAddress; } /** * @hide * Sets the device address. Only used by audio service. */ public void setAddress(@NonNull String address) { Objects.requireNonNull(address); mAddress = address; } /** * @hide * Returns the internal device type of a device Loading
media/java/android/media/AudioSystem.java +63 −0 Original line number Diff line number Diff line Loading @@ -1120,6 +1120,9 @@ public class AudioSystem public static final Set<Integer> DEVICE_IN_ALL_SCO_SET; /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_USB_SET; /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_BLE_SET; static { DEVICE_IN_ALL_SET = new HashSet<>(); DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION); Loading Loading @@ -1159,6 +1162,66 @@ public class AudioSystem DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE); DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET); DEVICE_IN_ALL_BLE_SET = new HashSet<>(); DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET); } /** @hide */ public static boolean isBluetoothDevice(int deviceType) { return isBluetoothA2dpOutDevice(deviceType) || isBluetoothScoDevice(deviceType) || isBluetoothLeDevice(deviceType); } /** @hide */ public static boolean isBluetoothOutDevice(int deviceType) { return isBluetoothA2dpOutDevice(deviceType) || isBluetoothScoOutDevice(deviceType) || isBluetoothLeOutDevice(deviceType); } /** @hide */ public static boolean isBluetoothInDevice(int deviceType) { return isBluetoothScoInDevice(deviceType) || isBluetoothLeInDevice(deviceType); } /** @hide */ public static boolean isBluetoothA2dpOutDevice(int deviceType) { return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothScoOutDevice(int deviceType) { return DEVICE_OUT_ALL_SCO_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothScoInDevice(int deviceType) { return DEVICE_IN_ALL_SCO_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothScoDevice(int deviceType) { return isBluetoothScoOutDevice(deviceType) || isBluetoothScoInDevice(deviceType); } /** @hide */ public static boolean isBluetoothLeOutDevice(int deviceType) { return DEVICE_OUT_ALL_BLE_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothLeInDevice(int deviceType) { return DEVICE_IN_ALL_BLE_SET.contains(deviceType); } /** @hide */ public static boolean isBluetoothLeDevice(int deviceType) { return isBluetoothLeOutDevice(deviceType) || isBluetoothLeInDevice(deviceType); } /** @hide */ Loading
packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +1 −0 Original line number Diff line number Diff line Loading @@ -614,6 +614,7 @@ public class SettingsBackupTest { Settings.Secure.ASSIST_SCREENSHOT_ENABLED, Settings.Secure.ASSIST_STRUCTURE_ENABLED, Settings.Secure.ATTENTIVE_TIMEOUT, Settings.Secure.AUDIO_DEVICE_INVENTORY, // setting not controllable by user Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE, Loading
services/core/java/com/android/server/audio/AdiDeviceState.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.server.audio; import static android.media.AudioSystem.DEVICE_NONE; import static android.media.AudioSystem.isBluetoothDevice; import android.annotation.NonNull; import android.annotation.Nullable; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import java.util.Objects; /** * Class representing all devices that were previously or are currently connected. Data is * persisted in {@link android.provider.Settings.Secure} */ /*package*/ final class AdiDeviceState { private static final String TAG = "AS.AdiDeviceState"; private static final String SETTING_FIELD_SEPARATOR = ","; @AudioDeviceInfo.AudioDeviceType private final int mDeviceType; private final int mInternalDeviceType; @NonNull private final String mDeviceAddress; /** Unique device id from internal device type and address. */ private final Pair<Integer, String> mDeviceId; private boolean mSAEnabled; private boolean mHasHeadTracker = false; private boolean mHeadTrackerEnabled; /** * Constructor * * @param deviceType external audio device type * @param internalDeviceType if not set pass {@link DEVICE_NONE}, in this case the * default conversion of the external type will be used * @param address must be non-null for wireless devices * @throws NullPointerException if a null address is passed for a wireless device */ AdiDeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, int internalDeviceType, @Nullable String address) { mDeviceType = deviceType; if (internalDeviceType != DEVICE_NONE) { mInternalDeviceType = internalDeviceType; } else { mInternalDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType); } mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull( address) : ""; mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress); } public Pair<Integer, String> getDeviceId() { return mDeviceId; } @AudioDeviceInfo.AudioDeviceType public int getDeviceType() { return mDeviceType; } public int getInternalDeviceType() { return mInternalDeviceType; } @NonNull public String getDeviceAddress() { return mDeviceAddress; } public void setSAEnabled(boolean sAEnabled) { mSAEnabled = sAEnabled; } public boolean isSAEnabled() { return mSAEnabled; } public void setHeadTrackerEnabled(boolean headTrackerEnabled) { mHeadTrackerEnabled = headTrackerEnabled; } public boolean isHeadTrackerEnabled() { return mHeadTrackerEnabled; } public void setHasHeadTracker(boolean hasHeadTracker) { mHasHeadTracker = hasHeadTracker; } public boolean hasHeadTracker() { return mHasHeadTracker; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } // type check and cast if (getClass() != obj.getClass()) { return false; } final AdiDeviceState sads = (AdiDeviceState) obj; return mDeviceType == sads.mDeviceType && mInternalDeviceType == sads.mInternalDeviceType && mDeviceAddress.equals(sads.mDeviceAddress) // NonNull && mSAEnabled == sads.mSAEnabled && mHasHeadTracker == sads.mHasHeadTracker && mHeadTrackerEnabled == sads.mHeadTrackerEnabled; } @Override public int hashCode() { return Objects.hash(mDeviceType, mInternalDeviceType, mDeviceAddress, mSAEnabled, mHasHeadTracker, mHeadTrackerEnabled); } @Override public String toString() { return "type: " + mDeviceType + " internal type: 0x" + Integer.toHexString(mInternalDeviceType) + " addr: " + mDeviceAddress + " enabled: " + mSAEnabled + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled; } public String toPersistableString() { return (new StringBuilder().append(mDeviceType) .append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress) .append(SETTING_FIELD_SEPARATOR).append(mSAEnabled ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0") .append(SETTING_FIELD_SEPARATOR).append(mInternalDeviceType) .toString()); } /** * Gets the max size (including separators) when persisting the elements with * {@link AdiDeviceState#toPersistableString()}. */ public static int getPeristedMaxSize() { return 36; /* (mDeviceType)2 + (mDeviceAddresss)17 + (mInternalDeviceType)9 + (mSAEnabled)1 + (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1 + (SETTINGS_FIELD_SEPARATOR)5 */ } @Nullable public static AdiDeviceState fromPersistedString(@Nullable String persistedString) { if (persistedString == null) { return null; } if (persistedString.isEmpty()) { return null; } String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR); // we may have 5 fields for the legacy AdiDeviceState and 6 containing the internal // device type if (fields.length != 5 && fields.length != 6) { // expecting all fields, fewer may mean corruption, ignore those settings return null; } try { final int deviceType = Integer.parseInt(fields[0]); int internalDeviceType = -1; if (fields.length == 6) { internalDeviceType = Integer.parseInt(fields[5]); } final AdiDeviceState deviceState = new AdiDeviceState(deviceType, internalDeviceType, fields[1]); deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1); deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1); deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1); return deviceState; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e); return null; } } public AudioDeviceAttributes getAudioDeviceAttributes() { return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, mDeviceType, mDeviceAddress); } }