summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/companion/AssociationRequest.java2
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java3
-rw-r--r--core/java/com/android/internal/util/BinaryXmlSerializer.java10
-rw-r--r--core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp81
-rw-r--r--core/tests/coretests/src/android/util/BinaryXmlTest.java50
-rw-r--r--media/java/android/media/AudioDeviceAttributes.java24
-rw-r--r--media/java/android/media/AudioSystem.java63
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--services/core/java/com/android/server/audio/AdiDeviceState.java215
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java116
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java138
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java150
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java6
-rw-r--r--services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java21
18 files changed, 203 insertions, 757 deletions
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index bb8fa9eb4007..6b836adb248c 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -148,7 +148,7 @@ public final class AssociationRequest implements Parcelable {
/** @hide */
public void setSkipPrompt(boolean value) {
- mSkipPrompt = true;
+ mSkipPrompt = value;
}
/** @hide */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1b57aa7e69cb..a1b4deae0989 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9041,13 +9041,6 @@ 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>
* Type: int (0 for false, 1 for true)
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 993e4e7b4b3d..765901a043a0 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -93,6 +93,9 @@ class ZygoteConnection {
throw ex;
}
+ if (peer.getUid() != Process.SYSTEM_UID) {
+ throw new ZygoteSecurityException("Only system UID is allowed to connect to Zygote.");
+ }
isEof = false;
}
diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java
index 9df4bdb157c8..6e33dada9589 100644
--- a/core/java/com/android/internal/util/BinaryXmlSerializer.java
+++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java
@@ -97,6 +97,8 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
*/
private static final int BUFFER_SIZE = 32_768;
+ private static final int MAX_UNSIGNED_SHORT = 65_535;
+
private FastDataOutput mOut;
/**
@@ -221,6 +223,10 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
mOut.writeByte(ATTRIBUTE | TYPE_BYTES_HEX);
mOut.writeInternedUTF(name);
+ if (value.length > MAX_UNSIGNED_SHORT) {
+ throw new IOException("attributeBytesHex: input size (" + value.length
+ + ") exceeds maximum allowed size (" + MAX_UNSIGNED_SHORT + ")");
+ }
mOut.writeShort(value.length);
mOut.write(value);
return this;
@@ -232,6 +238,10 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
if (namespace != null && !namespace.isEmpty()) throw illegalNamespace();
mOut.writeByte(ATTRIBUTE | TYPE_BYTES_BASE64);
mOut.writeInternedUTF(name);
+ if (value.length > MAX_UNSIGNED_SHORT) {
+ throw new IOException("attributeBytesBase64: input size (" + value.length
+ + ") exceeds maximum allowed size (" + MAX_UNSIGNED_SHORT + ")");
+ }
mOut.writeShort(value.length);
mOut.write(value);
return this;
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 248db76da71d..1ad64d58b7c9 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -341,6 +341,18 @@ jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, j
return result;
}
+static uid_t getSocketPeerUid(int socket, const std::function<void(const std::string&)>& fail_fn) {
+ struct ucred credentials;
+ socklen_t cred_size = sizeof credentials;
+ if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
+ || cred_size != sizeof credentials) {
+ fail_fn(CREATE_ERROR("Failed to get socket credentials, %s",
+ strerror(errno)));
+ }
+
+ return credentials.uid;
+}
+
// Read all lines from the current command into the buffer, and then reset the buffer, so
// we will start reading again at the beginning of the command, starting with the argument
// count. And we don't need access to the fd to do so.
@@ -398,18 +410,12 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
fail_fn_z("Failed to retrieve session socket timeout");
}
- struct ucred credentials;
- socklen_t cred_size = sizeof credentials;
- if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
- || cred_size != sizeof credentials) {
- fail_fn_1(CREATE_ERROR("ForkMany failed to get initial credentials, %s", strerror(errno)));
+ uid_t peerUid = getSocketPeerUid(session_socket, fail_fn_1);
+ if (peerUid != static_cast<uid_t>(expected_uid)) {
+ return JNI_FALSE;
}
-
bool first_time = true;
do {
- if (credentials.uid != expected_uid) {
- return JNI_FALSE;
- }
n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
n_buffer->reset();
int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
@@ -439,30 +445,56 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
// Clear buffer and get count from next command.
n_buffer->clear();
for (;;) {
+ bool valid_session_socket = true;
// Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */));
if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
if (n_buffer->getCount(fail_fn_z) != 0) {
break;
- } // else disconnected;
+ } else {
+ // Session socket was disconnected
+ valid_session_socket = false;
+ close(session_socket);
+ }
} else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) {
fail_fn_z(
CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res));
}
- // We've now seen either a disconnect or connect request.
- close(session_socket);
- int new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
+ int new_fd = -1;
+ do {
+ // We've now seen either a disconnect or connect request.
+ new_fd = TEMP_FAILURE_RETRY(accept(zygote_socket_fd, nullptr, nullptr));
+ if (new_fd == -1) {
+ fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
+ }
+ uid_t newPeerUid = getSocketPeerUid(new_fd, fail_fn_1);
+ if (newPeerUid != static_cast<uid_t>(expected_uid)) {
+ ALOGW("Dropping new connection with a mismatched uid %d\n", newPeerUid);
+ close(new_fd);
+ new_fd = -1;
+ } else {
+ // If we still have a valid session socket, close it now
+ if (valid_session_socket) {
+ close(session_socket);
+ }
+ valid_session_socket = true;
+ }
+ } while (!valid_session_socket);
+
+ // At this point we either have a valid new connection (new_fd > 0), or
+ // an existing session socket we can poll on
if (new_fd == -1) {
- fail_fn_z(CREATE_ERROR("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)));
+ // The new connection wasn't valid, and we still have an old one; retry polling
+ continue;
}
if (new_fd != session_socket) {
- // Move new_fd back to the old value, so that we don't have to change Java-level data
- // structures to reflect a change. This implicitly closes the old one.
- if (TEMP_FAILURE_RETRY(dup2(new_fd, session_socket)) != session_socket) {
- fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
- new_fd, session_socket, strerror(errno)));
- }
- close(new_fd); // On Linux, fd is closed even if EINTR is returned.
+ // Move new_fd back to the old value, so that we don't have to change Java-level data
+ // structures to reflect a change. This implicitly closes the old one.
+ if (TEMP_FAILURE_RETRY(dup2(new_fd, session_socket)) != session_socket) {
+ fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
+ new_fd, session_socket, strerror(errno)));
+ }
+ close(new_fd); // On Linux, fd is closed even if EINTR is returned.
}
// If we ever return, we effectively reuse the old Java ZygoteConnection.
// None of its state needs to change.
@@ -474,13 +506,6 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
session_socket, strerror(errno)));
}
- if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) {
- fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno)));
- }
- if (cred_size != sizeof credentials) {
- fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d",
- cred_size, static_cast<int>(sizeof credentials)));
- }
}
first_time = false;
} while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));
diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java
index fd625dce0254..b369868c0d9a 100644
--- a/core/tests/coretests/src/android/util/BinaryXmlTest.java
+++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java
@@ -24,6 +24,8 @@ import static android.util.XmlTest.doVerifyRead;
import static android.util.XmlTest.doVerifyWrite;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.os.PersistableBundle;
@@ -38,12 +40,15 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
@RunWith(AndroidJUnit4.class)
public class BinaryXmlTest {
+ private static final int MAX_UNSIGNED_SHORT = 65_535;
+
/**
* Verify that we can write and read large numbers of interned
* {@link String} values.
@@ -167,4 +172,49 @@ public class BinaryXmlTest {
}
}
}
+
+ @Test
+ public void testAttributeBytes_BinaryDataOverflow() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT + 1];
+ assertThrows(IOException.class,
+ () -> out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex",
+ testBytes));
+
+ assertThrows(IOException.class,
+ () -> out.attributeBytesBase64(/* namespace */ null, /* name */
+ "attributeBytesBase64", testBytes));
+ }
+
+ @Test
+ public void testAttributeBytesHex_MaximumBinaryData() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+ try {
+ out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex", testBytes);
+ } catch (Exception e) {
+ fail("testAttributeBytesHex fails with exception: " + e.toString());
+ }
+ }
+
+ @Test
+ public void testAttributeBytesBase64_MaximumBinaryData() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+ try {
+ out.attributeBytesBase64(/* namespace */ null, /* name */ "attributeBytesBase64",
+ testBytes);
+ } catch (Exception e) {
+ fail("testAttributeBytesBase64 fails with exception: " + e.toString());
+ }
+ }
}
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 5341e7e5adc0..7caac899a603 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -64,7 +64,8 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* The unique address of the device. Some devices don't have addresses, only an empty string.
*/
- private @NonNull String mAddress;
+ private final @NonNull String mAddress;
+
/**
* Is input or output device
*/
@@ -134,18 +135,6 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* @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
* @return the role
*/
@@ -176,15 +165,6 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* @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
* @return the internal device type
*/
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 94cf93eafb57..6ff551a68c18 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1120,9 +1120,6 @@ 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);
@@ -1162,66 +1159,6 @@ 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 */
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 229511c26667..9e1a3035a3a7 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -614,7 +614,6 @@ 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,
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
deleted file mode 100644
index eab1eca90f6c..000000000000
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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);
- }
-
-}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2c0b6bd9d4fa..b1e62483c472 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -47,7 +47,6 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -64,11 +63,8 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
-/**
- * @hide
- * (non final for mocking/spying)
- */
-public class AudioDeviceBroker {
+/** @hide */
+/*package*/ final class AudioDeviceBroker {
private static final String TAG = "AS.AudioDeviceBroker";
@@ -757,8 +753,8 @@ public class AudioDeviceBroker {
}
/*package*/ void registerStrategyPreferredDevicesDispatcher(
- @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
- mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher, isPrivileged);
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+ mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
}
/*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -776,8 +772,8 @@ public class AudioDeviceBroker {
}
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
- @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
- mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher, isPrivileged);
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
}
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -785,11 +781,6 @@ public class AudioDeviceBroker {
mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
}
- /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
- List<AudioDeviceAttributes> devices) {
- return mAudioService.anonymizeAudioDeviceAttributesListUnchecked(devices);
- }
-
/*package*/ void registerCommunicationDeviceDispatcher(
@NonNull ICommunicationDeviceDispatcher dispatcher) {
mCommDevDispatchers.register(dispatcher);
@@ -1519,9 +1510,6 @@ public class AudioDeviceBroker {
final int capturePreset = msg.arg1;
mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
} break;
- case MSG_PERSIST_AUDIO_DEVICE_SETTINGS:
- onPersistAudioDeviceSettings();
- break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1606,8 +1594,6 @@ public class AudioDeviceBroker {
private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44;
- private static final int MSG_PERSIST_AUDIO_DEVICE_SETTINGS = 54;
-
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -1972,94 +1958,4 @@ public class AudioDeviceBroker {
}
return null;
}
-
- /**
- * post a message to persist the audio device settings.
- * Message is delayed by 1s on purpose in case of successive changes in quick succession (at
- * init time for instance)
- * Note this method is made public to work around a Mockito bug where it needs to be public
- * in order to be mocked by a test a the same package
- * (see https://code.google.com/archive/p/mockito/issues/127)
- */
- public void persistAudioDeviceSettings() {
- sendMsg(MSG_PERSIST_AUDIO_DEVICE_SETTINGS, SENDMSG_REPLACE, /*delay*/ 1000);
- }
-
- void onPersistAudioDeviceSettings() {
- final String deviceSettings = mDeviceInventory.getDeviceSettings();
- Log.v(TAG, "saving audio device settings: " + deviceSettings);
- boolean res = Settings.Secure.putStringForUser(
- mAudioService.getContentResolver(), Settings.Secure.AUDIO_DEVICE_INVENTORY,
- deviceSettings, UserHandle.USER_CURRENT);
-
- if (!res) {
- Log.e(TAG, "error saving audio device settings: " + deviceSettings);
- }
- }
-
- void onReadAudioDeviceSettings() {
- final ContentResolver contentResolver = mAudioService.getContentResolver();
- String settings = Settings.Secure.getStringForUser(contentResolver,
- Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
- if (settings == null) {
- Log.i(TAG, "reading spatial audio device settings from legacy key"
- + Settings.Secure.SPATIAL_AUDIO_ENABLED);
- // legacy string format for key SPATIAL_AUDIO_ENABLED has the same order of fields like
- // the strings for key AUDIO_DEVICE_INVENTORY. This will ensure to construct valid
- // device settings when calling {@link #setDeviceSettings()}
- settings = Settings.Secure.getStringForUser(contentResolver,
- Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
- if (settings == null) {
- Log.i(TAG, "no spatial audio device settings stored with legacy key");
- } else if (!settings.equals("")) {
- // Delete old key value and update the new key
- if (!Settings.Secure.putStringForUser(contentResolver,
- Settings.Secure.SPATIAL_AUDIO_ENABLED,
- /*value=*/"",
- UserHandle.USER_CURRENT)) {
- Log.w(TAG, "cannot erase the legacy audio device settings with key "
- + Settings.Secure.SPATIAL_AUDIO_ENABLED);
- }
- if (!Settings.Secure.putStringForUser(contentResolver,
- Settings.Secure.AUDIO_DEVICE_INVENTORY,
- settings,
- UserHandle.USER_CURRENT)) {
- Log.e(TAG, "error updating the new audio device settings with key "
- + Settings.Secure.AUDIO_DEVICE_INVENTORY);
- }
- }
- }
-
- if (settings != null && !settings.equals("")) {
- setDeviceSettings(settings);
- }
- }
-
- void setDeviceSettings(String settings) {
- mDeviceInventory.setDeviceSettings(settings);
- }
-
- /** Test only method. */
- String getDeviceSettings() {
- return mDeviceInventory.getDeviceSettings();
- }
-
- List<AdiDeviceState> getImmutableDeviceInventory() {
- return mDeviceInventory.getImmutableDeviceInventory();
- }
-
- void addDeviceStateToInventory(AdiDeviceState deviceState) {
- mDeviceInventory.addDeviceStateToInventory(deviceState);
- }
-
- AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
- int canonicalType) {
- return mDeviceInventory.findDeviceStateForAudioDeviceAttributes(ada, canonicalType);
- }
-
- //------------------------------------------------
- // for testing purposes only
- void clearDeviceInventory() {
- mDeviceInventory.clearDeviceInventory();
- }
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 4c2b1a6bd7e5..5288a04ce86e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -15,8 +15,6 @@
*/
package com.android.server.audio;
-import static android.media.AudioSystem.isBluetoothDevice;
-
import android.annotation.NonNull;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
@@ -42,7 +40,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -50,9 +47,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
@@ -66,81 +61,12 @@ public class AudioDeviceInventory {
private static final String TAG = "AS.AudioDeviceInventory";
- private static final String SETTING_DEVICE_SEPARATOR_CHAR = "|";
- private static final String SETTING_DEVICE_SEPARATOR = "\\|";
-
// lock to synchronize all access to mConnectedDevices and mApmConnectedDevices
private final Object mDevicesLock = new Object();
//Audio Analytics ids.
private static final String mMetricsId = "audio.device.";
- private final Object mDeviceInventoryLock = new Object();
- @GuardedBy("mDeviceInventoryLock")
- private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>();
-
- List<AdiDeviceState> getImmutableDeviceInventory() {
- synchronized (mDeviceInventoryLock) {
- return new ArrayList<AdiDeviceState>(mDeviceInventory.values());
- }
- }
-
- void addDeviceStateToInventory(AdiDeviceState deviceState) {
- synchronized (mDeviceInventoryLock) {
- mDeviceInventory.put(deviceState.getDeviceId(), deviceState);
- }
- }
-
- /**
- * Adds a new entry in mDeviceInventory if the AudioDeviceAttributes passed is an sink
- * Bluetooth device and no corresponding entry already exists.
- * @param ada the device to add if needed
- */
- void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) {
- if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) {
- return;
- }
- synchronized (mDeviceInventoryLock) {
- if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) {
- return;
- }
- AdiDeviceState ads = new AdiDeviceState(
- ada.getType(), ada.getInternalType(), ada.getAddress());
- mDeviceInventory.put(ads.getDeviceId(), ads);
- }
- mDeviceBroker.persistAudioDeviceSettings();
- }
-
- /**
- * Finds the device state that matches the passed {@link AudioDeviceAttributes} and device
- * type. Note: currently this method only returns a valid device for A2DP and BLE devices.
- *
- * @param ada attributes of device to match
- * @param canonicalDeviceType external device type to match
- * @return the found {@link AdiDeviceState} matching a cached A2DP or BLE device or
- * {@code null} otherwise.
- */
- AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
- int canonicalDeviceType) {
- final boolean isWireless = isBluetoothDevice(ada.getInternalType());
- synchronized (mDeviceInventoryLock) {
- for (AdiDeviceState deviceState : mDeviceInventory.values()) {
- if (deviceState.getDeviceType() == canonicalDeviceType
- && (!isWireless || ada.getAddress().equals(
- deviceState.getDeviceAddress()))) {
- return deviceState;
- }
- }
- }
- return null;
- }
-
- void clearDeviceInventory() {
- synchronized (mDeviceInventoryLock) {
- mDeviceInventory.clear();
- }
- }
-
// List of connected devices
// Key for map created from DeviceInfo.makeDeviceListKey()
@GuardedBy("mDevicesLock")
@@ -330,12 +256,6 @@ public class AudioDeviceInventory {
mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
pw.println(" " + prefix + "capturePreset:" + capturePreset
+ " devices:" + devices); });
- pw.println("\ndevices:\n");
- synchronized (mDeviceInventoryLock) {
- for (AdiDeviceState device : mDeviceInventory.values()) {
- pw.println("\t" + device + "\n");
- }
- }
}
//------------------------------------------------------------
@@ -761,8 +681,8 @@ public class AudioDeviceInventory {
}
/*package*/ void registerStrategyPreferredDevicesDispatcher(
- @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
- mPrefDevDispatchers.register(dispatcher, isPrivileged);
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+ mPrefDevDispatchers.register(dispatcher);
}
/*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -796,8 +716,8 @@ public class AudioDeviceInventory {
}
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
- @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
- mDevRoleCapturePresetDispatchers.register(dispatcher, isPrivileged);
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDevRoleCapturePresetDispatchers.register(dispatcher);
}
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -852,10 +772,6 @@ public class AudioDeviceInventory {
mConnectedDevices.put(deviceKey, new DeviceInfo(
device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
- if (AudioSystem.isBluetoothScoDevice(device)) {
- addAudioDeviceInInventoryIfNeeded(new AudioDeviceAttributes(
- device, address));
- }
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
return true;
} else if (!connect && isConnected) {
@@ -1074,11 +990,8 @@ public class AudioDeviceInventory {
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
- addAudioDeviceInInventoryIfNeeded(new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
}
-
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
@@ -1181,8 +1094,6 @@ public class AudioDeviceInventory {
mDeviceBroker.postApplyVolumeOnDevice(streamType,
AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
- addAudioDeviceInInventoryIfNeeded(new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_HEARING_AID, address));
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
.set(MediaMetrics.Property.DEVICE,
@@ -1479,9 +1390,6 @@ public class AudioDeviceInventory {
final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; i++) {
try {
- if (!((Boolean) mPrefDevDispatchers.getBroadcastCookie(i))) {
- devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
- }
mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
strategy, devices);
} catch (RemoteException e) {
@@ -1495,9 +1403,6 @@ public class AudioDeviceInventory {
final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; ++i) {
try {
- if (!((Boolean) mDevRoleCapturePresetDispatchers.getBroadcastCookie(i))) {
- devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
- }
mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
capturePreset, role, devices);
} catch (RemoteException e) {
@@ -1506,41 +1411,6 @@ public class AudioDeviceInventory {
mDevRoleCapturePresetDispatchers.finishBroadcast();
}
- /*package*/ String getDeviceSettings() {
- int deviceCatalogSize = 0;
- synchronized (mDeviceInventoryLock) {
- deviceCatalogSize = mDeviceInventory.size();
-
- final StringBuilder settingsBuilder = new StringBuilder(
- deviceCatalogSize * AdiDeviceState.getPeristedMaxSize());
-
- Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator();
- if (iterator.hasNext()) {
- settingsBuilder.append(iterator.next().toPersistableString());
- }
- while (iterator.hasNext()) {
- settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
- settingsBuilder.append(iterator.next().toPersistableString());
- }
- return settingsBuilder.toString();
- }
- }
-
- /*package*/ void setDeviceSettings(String settings) {
- clearDeviceInventory();
- String[] devSettings = TextUtils.split(settings,
- SETTING_DEVICE_SEPARATOR);
- // small list, not worth overhead of Arrays.stream(devSettings)
- for (String setting : devSettings) {
- AdiDeviceState devState = AdiDeviceState.fromPersistedString(setting);
- // Note if the device is not compatible with spatialization mode or the device
- // type is not canonical, it will be ignored in {@link SpatializerHelper}.
- if (devState != null) {
- addDeviceStateToInventory(devState);
- }
- }
- }
-
//----------------------------------------------------------
// For tests only
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2221d343da0f..2e02e4977094 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2408,11 +2408,8 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.ERROR;
}
enforceModifyAudioRoutingPermission();
-
- devices = retrieveBluetoothAddresses(devices);
-
final String logString = String.format(
- "setPreferredDevicesForStrategy u/pid:%d/%d strat:%d dev:%s",
+ "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
Binder.getCallingUid(), Binder.getCallingPid(), strategy,
devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
@@ -2460,7 +2457,7 @@ public class AudioService extends IAudioService.Stub
status, strategy));
return new ArrayList<AudioDeviceAttributes>();
} else {
- return anonymizeAudioDeviceAttributesList(devices);
+ return devices;
}
}
@@ -2473,8 +2470,7 @@ public class AudioService extends IAudioService.Stub
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerStrategyPreferredDevicesDispatcher(
- dispatcher, isBluetoothPrividged());
+ mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
}
/** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
@@ -2490,7 +2486,7 @@ public class AudioService extends IAudioService.Stub
}
/**
- * @see AudioManager#setPreferredDevicesForCapturePreset(int, AudioDeviceAttributes)
+ * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
*/
public int setPreferredDevicesForCapturePreset(
int capturePreset, List<AudioDeviceAttributes> devices) {
@@ -2509,8 +2505,6 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.ERROR;
}
- devices = retrieveBluetoothAddresses(devices);
-
final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
capturePreset, devices);
if (status != AudioSystem.SUCCESS) {
@@ -2549,7 +2543,7 @@ public class AudioService extends IAudioService.Stub
status, capturePreset));
return new ArrayList<AudioDeviceAttributes>();
} else {
- return anonymizeAudioDeviceAttributesList(devices);
+ return devices;
}
}
@@ -2563,8 +2557,7 @@ public class AudioService extends IAudioService.Stub
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(
- dispatcher, isBluetoothPrividged());
+ mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
}
/**
@@ -2584,8 +2577,7 @@ public class AudioService extends IAudioService.Stub
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
enforceQueryStateOrModifyRoutingPermission();
- return new ArrayList<AudioDeviceAttributes>(anonymizeAudioDeviceAttributesList(
- getDevicesForAttributesInt(attributes)));
+ return getDevicesForAttributesInt(attributes);
}
/**
@@ -6166,9 +6158,6 @@ public class AudioService extends IAudioService.Stub
// verify arguments
Objects.requireNonNull(device);
AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
-
- device = retrieveBluetoothAddress(device);
-
if (pkgName == null) {
pkgName = "";
}
@@ -6221,8 +6210,6 @@ public class AudioService extends IAudioService.Stub
// verify permissions
enforceQueryStateOrModifyRoutingPermission();
- device = retrieveBluetoothAddress(device);
-
// translate Java device type to native device type (for the devices masks for full / fixed)
final int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
device.getType());
@@ -6271,9 +6258,6 @@ public class AudioService extends IAudioService.Stub
@ConnectionState int state, String address, String name,
String caller) {
enforceModifyAudioRoutingPermission();
-
- address = retrieveBluetoothAddress(type, address);
-
if (state != CONNECTION_STATE_CONNECTED
&& state != CONNECTION_STATE_DISCONNECTED) {
throw new IllegalArgumentException("Invalid state " + state);
@@ -8546,117 +8530,6 @@ public class AudioService extends IAudioService.Stub
/*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
}
- private boolean isBluetoothPrividged() {
- return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.BLUETOOTH_CONNECT)
- || Binder.getCallingUid() == Process.SYSTEM_UID;
- }
-
- List<AudioDeviceAttributes> retrieveBluetoothAddresses(List<AudioDeviceAttributes> devices) {
- if (isBluetoothPrividged()) {
- return devices;
- }
-
- List<AudioDeviceAttributes> checkedDevices = new ArrayList<AudioDeviceAttributes>();
- for (AudioDeviceAttributes ada : devices) {
- if (ada == null) {
- continue;
- }
- checkedDevices.add(retrieveBluetoothAddressUncheked(ada));
- }
- return checkedDevices;
- }
-
- AudioDeviceAttributes retrieveBluetoothAddress(@NonNull AudioDeviceAttributes ada) {
- if (isBluetoothPrividged()) {
- return ada;
- }
- return retrieveBluetoothAddressUncheked(ada);
- }
-
- AudioDeviceAttributes retrieveBluetoothAddressUncheked(@NonNull AudioDeviceAttributes ada) {
- Objects.requireNonNull(ada);
- if (AudioSystem.isBluetoothDevice(ada.getInternalType())) {
- String anonymizedAddress = anonymizeBluetoothAddress(ada.getAddress());
- for (AdiDeviceState ads : mDeviceBroker.getImmutableDeviceInventory()) {
- if (!(AudioSystem.isBluetoothDevice(ads.getInternalDeviceType())
- && (ada.getInternalType() == ads.getInternalDeviceType())
- && anonymizedAddress.equals(anonymizeBluetoothAddress(
- ads.getDeviceAddress())))) {
- continue;
- }
- ada.setAddress(ads.getDeviceAddress());
- break;
- }
- }
- return ada;
- }
-
- private String retrieveBluetoothAddress(int type, String address) {
- if (isBluetoothPrividged() || !AudioSystem.isBluetoothDevice(type)
- || address == null) {
- return address;
- }
- String anonymizedAddress = anonymizeBluetoothAddress(address);
- for (AdiDeviceState ads : mDeviceBroker.getImmutableDeviceInventory()) {
- if (!(AudioSystem.isBluetoothDevice(ads.getInternalDeviceType())
- && anonymizedAddress.equals(anonymizeBluetoothAddress(
- ads.getDeviceAddress())))) {
- continue;
- }
- return ads.getDeviceAddress();
- }
- return address;
- }
-
- /**
- * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
- * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
- * @param address Mac address to be anonymized
- * @return anonymized mac address
- */
- static String anonymizeBluetoothAddress(String address) {
- if (address == null || address.length() != "AA:BB:CC:DD:EE:FF".length()) {
- return null;
- }
- return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
- }
-
- private List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesList(
- List<AudioDeviceAttributes> devices) {
- if (isBluetoothPrividged()) {
- return devices;
- }
- return anonymizeAudioDeviceAttributesListUnchecked(devices);
- }
-
- /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
- List<AudioDeviceAttributes> devices) {
- List<AudioDeviceAttributes> anonymizedDevices = new ArrayList<AudioDeviceAttributes>();
- for (AudioDeviceAttributes ada : devices) {
- anonymizedDevices.add(anonymizeAudioDeviceAttributesUnchecked(ada));
- }
- return anonymizedDevices;
- }
-
- private AudioDeviceAttributes anonymizeAudioDeviceAttributesUnchecked(
- AudioDeviceAttributes ada) {
- if (!AudioSystem.isBluetoothDevice(ada.getInternalType())) {
- return ada;
- }
- AudioDeviceAttributes res = new AudioDeviceAttributes(ada);
- res.setAddress(anonymizeBluetoothAddress(ada.getAddress()));
- return res;
- }
-
- private AudioDeviceAttributes anonymizeAudioDeviceAttributes(AudioDeviceAttributes ada) {
- if (isBluetoothPrividged()) {
- return ada;
- }
-
- return anonymizeAudioDeviceAttributesUnchecked(ada);
- }
-
//==========================================================================================
private boolean readCameraSoundForced() {
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
@@ -10781,9 +10654,6 @@ public class AudioService extends IAudioService.Stub
@NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
Objects.requireNonNull(device, "device must not be null");
enforceModifyAudioRoutingPermission();
-
- device = retrieveBluetoothAddress(device);
-
final String getterKey = "additional_output_device_delay="
+ device.getInternalType() + "," + device.getAddress(); // "getter" key as an id.
final String setterKey = getterKey + "," + delayMillis; // append the delay for setter
@@ -10804,9 +10674,6 @@ public class AudioService extends IAudioService.Stub
@IntRange(from = 0)
public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(device, "device must not be null");
-
- device = retrieveBluetoothAddress(device);
-
final String key = "additional_output_device_delay";
final String reply = AudioSystem.getParameters(
key + "=" + device.getInternalType() + "," + device.getAddress());
@@ -10834,9 +10701,6 @@ public class AudioService extends IAudioService.Stub
@IntRange(from = 0)
public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(device, "device must not be null");
-
- device = retrieveBluetoothAddress(device);
-
final String key = "max_additional_output_device_delay";
final String reply = AudioSystem.getParameters(
key + "=" + device.getInternalType() + "," + device.getAddress());
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 31babe0418b8..93f9e1c2295c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -687,7 +687,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (bp == null) {
return;
}
- if (bp.isDynamic()) {
+ if (!bp.isDynamic()) {
// TODO: switch this back to SecurityException
Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
+ permName);
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ee72fc8622a5..b847d2530abb 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -78,6 +78,7 @@ import android.view.RemoteAnimationDefinition;
import android.window.SizeConfigurationBuckets;
import android.window.TransitionInfo;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.protolog.common.ProtoLog;
@@ -85,6 +86,9 @@ import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.uri.NeededUriGrants;
+import com.android.server.utils.quota.Categorizer;
+import com.android.server.utils.quota.Category;
+import com.android.server.utils.quota.CountQuotaTracker;
import com.android.server.vr.VrManagerInternal;
/**
@@ -100,6 +104,13 @@ class ActivityClientController extends IActivityClientController.Stub {
private final ActivityTaskSupervisor mTaskSupervisor;
private final Context mContext;
+ // Prevent malicious app abusing the Activity#setPictureInPictureParams API
+ @VisibleForTesting CountQuotaTracker mSetPipAspectRatioQuotaTracker;
+ // Limit to 60 times / minute
+ private static final int SET_PIP_ASPECT_RATIO_LIMIT = 60;
+ // The timeWindowMs here can not be smaller than QuotaTracker#MIN_WINDOW_SIZE_MS
+ private static final long SET_PIP_ASPECT_RATIO_TIME_WINDOW_MS = 60_000;
+
/** Wrapper around VoiceInteractionServiceManager. */
private AssistUtils mAssistUtils;
@@ -719,6 +730,7 @@ class ActivityClientController extends IActivityClientController.Stub {
public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
final long origId = Binder.clearCallingIdentity();
try {
+ ensureSetPipAspectRatioQuotaTracker();
synchronized (mGlobalLock) {
final ActivityRecord r = ensureValidPictureInPictureActivityParams(
"enterPictureInPictureMode", token, params);
@@ -733,6 +745,7 @@ class ActivityClientController extends IActivityClientController.Stub {
public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
final long origId = Binder.clearCallingIdentity();
try {
+ ensureSetPipAspectRatioQuotaTracker();
synchronized (mGlobalLock) {
final ActivityRecord r = ensureValidPictureInPictureActivityParams(
"setPictureInPictureParams", token, params);
@@ -767,6 +780,19 @@ class ActivityClientController extends IActivityClientController.Stub {
}
/**
+ * Initialize the {@link #mSetPipAspectRatioQuotaTracker} if applicable, which should happen
+ * out of {@link #mGlobalLock} to avoid deadlock (AM lock is used in QuotaTrack ctor).
+ */
+ private void ensureSetPipAspectRatioQuotaTracker() {
+ if (mSetPipAspectRatioQuotaTracker == null) {
+ mSetPipAspectRatioQuotaTracker = new CountQuotaTracker(mContext,
+ Categorizer.SINGLE_CATEGORIZER);
+ mSetPipAspectRatioQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
+ SET_PIP_ASPECT_RATIO_LIMIT, SET_PIP_ASPECT_RATIO_TIME_WINDOW_MS);
+ }
+ }
+
+ /**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
*
@@ -790,6 +816,19 @@ class ActivityClientController extends IActivityClientController.Stub {
+ ": Current activity does not support picture-in-picture.");
}
+ // Rate limit how frequent an app can request aspect ratio change via
+ // Activity#setPictureInPictureParams
+ final int userId = UserHandle.getCallingUserId();
+ if (r.pictureInPictureArgs.hasSetAspectRatio()
+ && params.hasSetAspectRatio()
+ && !r.pictureInPictureArgs.getAspectRatioRational().equals(
+ params.getAspectRatioRational())
+ && !mSetPipAspectRatioQuotaTracker.noteEvent(
+ userId, r.packageName, "setPipAspectRatio")) {
+ throw new IllegalStateException(caller
+ + ": Too many PiP aspect ratio change requests from " + r.packageName);
+ }
+
if (params.hasSetAspectRatio()
&& !mService.mWindowManager.isValidPictureInPictureAspectRatio(
r.mDisplayContent, params.getAspectRatio())) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index d33ff03df02a..5c53d43fa1df 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -29,7 +29,6 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
-import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.util.Log;
@@ -200,37 +199,6 @@ public class AudioDeviceBrokerTest {
any(Intent.class));
}
- /**
- * Test that constructing an AdiDeviceState instance requires a non-null address for a
- * wireless type, but can take null for a non-wireless type;
- * @throws Exception
- */
- @Test
- public void testAdiDeviceStateNullAddressCtor() throws Exception {
- try {
- new AdiDeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
- AudioManager.DEVICE_OUT_SPEAKER, null);
- new AdiDeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, null);
- Assert.fail();
- } catch (NullPointerException e) { }
- }
-
- @Test
- public void testAdiDeviceStateStringSerialization() throws Exception {
- Log.i(TAG, "starting testAdiDeviceStateStringSerialization");
- final AdiDeviceState devState = new AdiDeviceState(
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioManager.DEVICE_OUT_SPEAKER, "bla");
- devState.setHasHeadTracker(false);
- devState.setHeadTrackerEnabled(false);
- devState.setSAEnabled(true);
- final String persistString = devState.toPersistableString();
- final AdiDeviceState result = AdiDeviceState.fromPersistedString(persistString);
- Log.i(TAG, "original:" + devState);
- Log.i(TAG, "result :" + result);
- Assert.assertEquals(devState, result);
- }
-
private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection,
boolean mockMediaPlayback, boolean guaranteeSingleConnection) throws Exception {
when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 75a87ba9e04d..89ffb2b4bfc7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -881,6 +881,12 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertNotNull(o.mInfo);
assertNotNull(o.mInfo.pictureInPictureParams);
+ // Bypass the quota check, which causes NPE in current test setup.
+ if (mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker != null) {
+ mWm.mAtmService.mActivityClientController.mSetPipAspectRatioQuotaTracker
+ .setEnabled(false);
+ }
+
final PictureInPictureParams p2 = new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(3, 4)).build();
mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2);
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index f63866054064..32bb643b8763 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
+
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import android.annotation.NonNull;
@@ -42,6 +44,7 @@ import android.os.AsyncTask;
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.service.usb.UsbProfileGroupSettingsManagerProto;
import android.service.usb.UsbSettingsAccessoryPreferenceProto;
import android.service.usb.UsbSettingsDevicePreferenceProto;
@@ -908,10 +911,28 @@ class UsbProfileGroupSettingsManager {
return;
}
+ if (shouldRestrictOverlayActivities()) {
+ return;
+ }
+
// Start activity with registered intent
resolveActivity(intent, matches, defaultActivity, device, null);
}
+ private boolean shouldRestrictOverlayActivities() {
+ if (Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ USER_SETUP_COMPLETE,
+ /* defaultValue= */ 1,
+ UserHandle.CURRENT.getIdentifier())
+ == 0) {
+ Slog.d(TAG, "restricting usb overlay activities as setup is not complete");
+ return true;
+ }
+
+ return false;
+ }
+
public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
final Intent intent = createDeviceAttachedIntent(device);