Skip to content

Commit e9ad19e

Browse files
Vlad PopaAndroid Build Coastguard Worker
authored andcommitted
Refactor the SADeviceState to AdiDeviceState
The idea is to have a device state catalog for all the known devices. Also refactored the name of the Settings.Secure key entry for persistence. The current code will check the legacy key first, erase it and update the new key. Test: atest SpatializerHelperTest & AudioDeviceBrokerTest Bug: 278265907 Bug: 285588444 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:41f08b8d70260839a96d1ebb412eb8766b386b76) Merged-In: Idabcc84cb0f5f6f88ba5aebc435511ab95016ef3 Change-Id: Idabcc84cb0f5f6f88ba5aebc435511ab95016ef3
1 parent eb7f3a6 commit e9ad19e

10 files changed

Lines changed: 694 additions & 386 deletions

File tree

core/java/android/provider/Settings.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9388,6 +9388,13 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val
93889388
*/
93899389
public static final String SPATIAL_AUDIO_ENABLED = "spatial_audio_enabled";
93909390

9391+
/**
9392+
* Internal collection of audio device inventory items
9393+
* The device item stored are {@link com.android.server.audio.AdiDeviceState}
9394+
* @hide
9395+
*/
9396+
public static final String AUDIO_DEVICE_INVENTORY = "audio_device_inventory";
9397+
93919398
/**
93929399
* Indicates whether notification display on the lock screen is enabled.
93939400
* <p>

media/java/android/media/AudioSystem.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,9 @@ public static String audioSystemErrorToString(@AudioSystemError int error) {
12081208
public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
12091209
/** @hide */
12101210
public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
1211+
/** @hide */
1212+
public static final Set<Integer> DEVICE_IN_ALL_BLE_SET;
1213+
12111214
static {
12121215
DEVICE_IN_ALL_SET = new HashSet<>();
12131216
DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION);
@@ -1247,6 +1250,66 @@ public static String audioSystemErrorToString(@AudioSystemError int error) {
12471250
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY);
12481251
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE);
12491252
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
1253+
1254+
DEVICE_IN_ALL_BLE_SET = new HashSet<>();
1255+
DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET);
1256+
}
1257+
1258+
/** @hide */
1259+
public static boolean isBluetoothDevice(int deviceType) {
1260+
return isBluetoothA2dpOutDevice(deviceType)
1261+
|| isBluetoothScoDevice(deviceType)
1262+
|| isBluetoothLeDevice(deviceType);
1263+
}
1264+
1265+
/** @hide */
1266+
public static boolean isBluetoothOutDevice(int deviceType) {
1267+
return isBluetoothA2dpOutDevice(deviceType)
1268+
|| isBluetoothScoOutDevice(deviceType)
1269+
|| isBluetoothLeOutDevice(deviceType);
1270+
}
1271+
1272+
/** @hide */
1273+
public static boolean isBluetoothInDevice(int deviceType) {
1274+
return isBluetoothScoInDevice(deviceType)
1275+
|| isBluetoothLeInDevice(deviceType);
1276+
}
1277+
1278+
/** @hide */
1279+
public static boolean isBluetoothA2dpOutDevice(int deviceType) {
1280+
return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType);
1281+
}
1282+
1283+
/** @hide */
1284+
public static boolean isBluetoothScoOutDevice(int deviceType) {
1285+
return DEVICE_OUT_ALL_SCO_SET.contains(deviceType);
1286+
}
1287+
1288+
/** @hide */
1289+
public static boolean isBluetoothScoInDevice(int deviceType) {
1290+
return DEVICE_IN_ALL_SCO_SET.contains(deviceType);
1291+
}
1292+
1293+
/** @hide */
1294+
public static boolean isBluetoothScoDevice(int deviceType) {
1295+
return isBluetoothScoOutDevice(deviceType)
1296+
|| isBluetoothScoInDevice(deviceType);
1297+
}
1298+
1299+
/** @hide */
1300+
public static boolean isBluetoothLeOutDevice(int deviceType) {
1301+
return DEVICE_OUT_ALL_BLE_SET.contains(deviceType);
1302+
}
1303+
1304+
/** @hide */
1305+
public static boolean isBluetoothLeInDevice(int deviceType) {
1306+
return DEVICE_IN_ALL_BLE_SET.contains(deviceType);
1307+
}
1308+
1309+
/** @hide */
1310+
public static boolean isBluetoothLeDevice(int deviceType) {
1311+
return isBluetoothLeOutDevice(deviceType)
1312+
|| isBluetoothLeInDevice(deviceType);
12501313
}
12511314

12521315
/** @hide */

packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ public class SettingsBackupTest {
687687
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
688688
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
689689
Settings.Secure.ATTENTIVE_TIMEOUT,
690+
Settings.Secure.AUDIO_DEVICE_INVENTORY, // setting not controllable by user
690691
Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
691692
Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT,
692693
Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/*
2+
* Copyright (C) 2023 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.android.server.audio;
18+
19+
import static android.media.AudioSystem.DEVICE_NONE;
20+
import static android.media.AudioSystem.isBluetoothDevice;
21+
22+
import android.annotation.NonNull;
23+
import android.annotation.Nullable;
24+
import android.media.AudioDeviceAttributes;
25+
import android.media.AudioDeviceInfo;
26+
import android.text.TextUtils;
27+
import android.util.Log;
28+
29+
import java.util.Objects;
30+
31+
/**
32+
* Class representing all devices that were previously or are currently connected. Data is
33+
* persisted in {@link android.provider.Settings.Secure}
34+
*/
35+
/*package*/ final class AdiDeviceState {
36+
private static final String TAG = "AS.AdiDeviceState";
37+
38+
private static final String SETTING_FIELD_SEPARATOR = ",";
39+
40+
@AudioDeviceInfo.AudioDeviceType
41+
private final int mDeviceType;
42+
43+
private final int mInternalDeviceType;
44+
@NonNull
45+
private final String mDeviceAddress;
46+
private boolean mSAEnabled;
47+
private boolean mHasHeadTracker = false;
48+
private boolean mHeadTrackerEnabled;
49+
50+
/**
51+
* Constructor
52+
*
53+
* @param deviceType external audio device type
54+
* @param internalDeviceType if not set pass {@link DEVICE_NONE}, in this case the
55+
* default conversion of the external type will be used
56+
* @param address must be non-null for wireless devices
57+
* @throws NullPointerException if a null address is passed for a wireless device
58+
*/
59+
AdiDeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType,
60+
int internalDeviceType,
61+
@Nullable String address) {
62+
mDeviceType = deviceType;
63+
if (internalDeviceType != DEVICE_NONE) {
64+
mInternalDeviceType = internalDeviceType;
65+
} else {
66+
mInternalDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType);
67+
68+
}
69+
mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull(
70+
address) : "";
71+
}
72+
73+
74+
75+
@AudioDeviceInfo.AudioDeviceType
76+
public int getDeviceType() {
77+
return mDeviceType;
78+
}
79+
80+
public int getInternalDeviceType() {
81+
return mInternalDeviceType;
82+
}
83+
84+
@NonNull
85+
public String getDeviceAddress() {
86+
return mDeviceAddress;
87+
}
88+
89+
public void setSAEnabled(boolean sAEnabled) {
90+
mSAEnabled = sAEnabled;
91+
}
92+
93+
public boolean isSAEnabled() {
94+
return mSAEnabled;
95+
}
96+
97+
public void setHeadTrackerEnabled(boolean headTrackerEnabled) {
98+
mHeadTrackerEnabled = headTrackerEnabled;
99+
}
100+
101+
public boolean isHeadTrackerEnabled() {
102+
return mHeadTrackerEnabled;
103+
}
104+
105+
public void setHasHeadTracker(boolean hasHeadTracker) {
106+
mHasHeadTracker = hasHeadTracker;
107+
}
108+
109+
110+
public boolean hasHeadTracker() {
111+
return mHasHeadTracker;
112+
}
113+
114+
@Override
115+
public boolean equals(Object obj) {
116+
if (this == obj) {
117+
return true;
118+
}
119+
if (obj == null) {
120+
return false;
121+
}
122+
// type check and cast
123+
if (getClass() != obj.getClass()) {
124+
return false;
125+
}
126+
final AdiDeviceState sads = (AdiDeviceState) obj;
127+
return mDeviceType == sads.mDeviceType
128+
&& mInternalDeviceType == sads.mInternalDeviceType
129+
&& mDeviceAddress.equals(sads.mDeviceAddress) // NonNull
130+
&& mSAEnabled == sads.mSAEnabled
131+
&& mHasHeadTracker == sads.mHasHeadTracker
132+
&& mHeadTrackerEnabled == sads.mHeadTrackerEnabled;
133+
}
134+
135+
@Override
136+
public int hashCode() {
137+
return Objects.hash(mDeviceType, mInternalDeviceType, mDeviceAddress, mSAEnabled,
138+
mHasHeadTracker, mHeadTrackerEnabled);
139+
}
140+
141+
@Override
142+
public String toString() {
143+
return "type: " + mDeviceType + "internal type: " + mInternalDeviceType
144+
+ " addr: " + mDeviceAddress + " enabled: " + mSAEnabled
145+
+ " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
146+
}
147+
148+
public String toPersistableString() {
149+
return (new StringBuilder().append(mDeviceType)
150+
.append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress)
151+
.append(SETTING_FIELD_SEPARATOR).append(mSAEnabled ? "1" : "0")
152+
.append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0")
153+
.append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0")
154+
.append(SETTING_FIELD_SEPARATOR).append(mInternalDeviceType)
155+
.toString());
156+
}
157+
158+
/**
159+
* Gets the max size (including separators) when persisting the elements with
160+
* {@link AdiDeviceState#toPersistableString()}.
161+
*/
162+
public static int getPeristedMaxSize() {
163+
return 36; /* (mDeviceType)2 + (mDeviceAddresss)17 + (mInternalDeviceType)9 + (mSAEnabled)1
164+
+ (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1
165+
+ (SETTINGS_FIELD_SEPARATOR)5 */
166+
}
167+
168+
@Nullable
169+
public static AdiDeviceState fromPersistedString(@Nullable String persistedString) {
170+
if (persistedString == null) {
171+
return null;
172+
}
173+
if (persistedString.isEmpty()) {
174+
return null;
175+
}
176+
String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR);
177+
// we may have 5 fields for the legacy AdiDeviceState and 6 containing the internal
178+
// device type
179+
if (fields.length != 5 && fields.length != 6) {
180+
// expecting all fields, fewer may mean corruption, ignore those settings
181+
return null;
182+
}
183+
try {
184+
final int deviceType = Integer.parseInt(fields[0]);
185+
int internalDeviceType = -1;
186+
if (fields.length == 6) {
187+
internalDeviceType = Integer.parseInt(fields[5]);
188+
}
189+
final AdiDeviceState deviceState = new AdiDeviceState(deviceType,
190+
internalDeviceType, fields[1]);
191+
deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1);
192+
deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
193+
deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
194+
return deviceState;
195+
} catch (NumberFormatException e) {
196+
Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e);
197+
return null;
198+
}
199+
}
200+
201+
public AudioDeviceAttributes getAudioDeviceAttributes() {
202+
return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
203+
mDeviceType, mDeviceAddress);
204+
}
205+
206+
}

0 commit comments

Comments
 (0)