Merge "Allow-list service-permission to not track latest api"
diff --git a/Android.bp b/Android.bp
index 4b3c22d..d234411 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1268,7 +1268,6 @@
// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
- "--ignore-classes-on-classpath " +
"--hide-package com.android.server " +
"--hide-package android.audio.policy.configuration.V7_0 " +
"--error UnhiddenSystemApi " +
diff --git a/ApiDocs.bp b/ApiDocs.bp
index ba93a48..ada80bb 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -91,7 +91,9 @@
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args,
+ args: metalava_framework_docs_args +
+ // Needed for hidden libcore annotations for now.
+ " --ignore-classes-on-classpath ",
write_sdk_values: true,
}
@@ -101,7 +103,10 @@
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+ args: metalava_framework_docs_args +
+ // Needed for hidden libcore annotations for now.
+ " --ignore-classes-on-classpath " +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
write_sdk_values: true,
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 6cabc57..d4abeee 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -50,7 +50,9 @@
":art.module.public.api{.public.stubs.source}",
"**/package.html",
],
- sdk_version: "core_platform",
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
arg_files: ["core/res/AndroidManifest.xml"],
// TODO(b/147699819): remove below aidl includes.
aidl: {
@@ -77,6 +79,7 @@
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.vibrator-V1.3-java",
"framework-protos",
+ "stable.core.platform.api.stubs",
// There are a few classes from modules used as type arguments that
// need to be resolved by metalava. For now, we can use a previously
// finalized stub library to resolve them. If a new class gets added,
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 8323d0b..437180d 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -71,7 +71,7 @@
}
// next, try to parse as a package:type/name string
- if (auto resid = am.GetResourceId(res, "", fallback_package)) {
+ if (auto resid = am.GetResourceId(res, "", fallback_package); resid.ok()) {
return *resid;
}
@@ -94,7 +94,7 @@
case Res_value::TYPE_STRING: {
const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie);
out->append("\"");
- if (auto str = pool->string8ObjectAt(value.data)) {
+ if (auto str = pool->string8ObjectAt(value.data); str.ok()) {
out->append(*str);
}
} break;
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 7c55b64..4030b83 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -98,7 +98,7 @@
switch ((*value).dataType) {
case Res_value::TYPE_STRING: {
- if (auto str = parser_.getStrings().string8ObjectAt((*value).data)) {
+ if (auto str = parser_.getStrings().string8ObjectAt((*value).data); str.ok()) {
return std::string(str->string());
}
break;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 29f6055..5e88d97d 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -3716,9 +3716,9 @@
android.media.IRingtonePlayer$Stub$Proxy
android.media.IRingtonePlayer$Stub
android.media.IRingtonePlayer
-android.media.IStrategyPreferredDeviceDispatcher$Stub$Proxy
-android.media.IStrategyPreferredDeviceDispatcher$Stub
-android.media.IStrategyPreferredDeviceDispatcher
+android.media.IStrategyPreferredDevicesDispatcher$Stub$Proxy
+android.media.IStrategyPreferredDevicesDispatcher$Stub
+android.media.IStrategyPreferredDevicesDispatcher
android.media.IVolumeController$Stub$Proxy
android.media.IVolumeController$Stub
android.media.IVolumeController
diff --git a/core/api/current.txt b/core/api/current.txt
index 3f173bd..65d3e4a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19373,14 +19373,17 @@
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
method public int abandonAudioFocusRequest(@NonNull android.media.AudioFocusRequest);
+ method public void addOnCommunicationDeviceChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
method public void adjustStreamVolume(int, int, int);
method public void adjustSuggestedStreamVolume(int, int, int);
method public void adjustVolume(int, int);
+ method public void clearDeviceForCommunication();
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
method @NonNull public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
method @NonNull public java.util.List<android.media.AudioRecordingConfiguration> getActiveRecordingConfigurations();
method public int getAllowedCapturePolicy();
+ method @Nullable public android.media.AudioDeviceInfo getDeviceForCommunication();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
method public int getMode();
@@ -19415,11 +19418,13 @@
method @Deprecated public void registerMediaButtonEventReceiver(android.app.PendingIntent);
method @Deprecated public void registerRemoteControlClient(android.media.RemoteControlClient);
method @Deprecated public boolean registerRemoteController(android.media.RemoteController);
+ method public void removeOnCommunicationDeviceChangedListener(@NonNull android.media.AudioManager.OnCommunicationDeviceChangedListener);
method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest);
method public void setAllowedCapturePolicy(int);
method @Deprecated public void setBluetoothA2dpOn(boolean);
method public void setBluetoothScoOn(boolean);
+ method public boolean setDeviceForCommunication(@NonNull android.media.AudioDeviceInfo);
method public void setMicrophoneMute(boolean);
method public void setMode(int);
method public void setParameters(String);
@@ -19554,6 +19559,10 @@
method public void onAudioFocusChange(int);
}
+ public static interface AudioManager.OnCommunicationDeviceChangedListener {
+ method public void onCommunicationDeviceChanged(@Nullable android.media.AudioDeviceInfo);
+ }
+
public final class AudioMetadata {
method @NonNull public static android.media.AudioMetadataMap createMap();
}
@@ -33225,8 +33234,8 @@
}
protected static interface ContactsContract.DataColumns {
- field public static final String CARRIER_PRESENCE = "carrier_presence";
- field public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
+ field @Deprecated public static final String CARRIER_PRESENCE = "carrier_presence";
+ field @Deprecated public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1
field public static final String DATA1 = "data1";
field public static final String DATA10 = "data10";
field public static final String DATA11 = "data11";
@@ -40257,7 +40266,7 @@
field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
- field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+ field @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool";
field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
@@ -40298,9 +40307,12 @@
}
public static final class CarrierConfigManager.Ims {
+ field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool";
+ field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
field public static final String KEY_PREFIX = "ims.";
+ field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
}
@@ -41762,7 +41774,7 @@
method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
method public boolean isConcurrentVoiceAndDataSupported();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cbd65d1..b4f1395 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4145,8 +4145,11 @@
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
method public void clearAudioServerStateCallback();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
@@ -4156,13 +4159,17 @@
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method public boolean isAudioServerRunning();
method public boolean isHdmiSystemAudioSupported();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
@@ -4170,7 +4177,9 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4189,8 +4198,16 @@
method public void onAudioServerUp();
}
- public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
- method public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+ @Deprecated public static interface AudioManager.OnPreferredDeviceForStrategyChangedListener {
+ method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
+ }
+
+ public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener {
+ method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
+ }
+
+ public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
+ method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
}
public abstract static class AudioManager.VolumeGroupCallback {
@@ -6968,12 +6985,14 @@
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int) throws java.lang.RuntimeException;
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStartedFromSource(@NonNull android.os.WorkSource, @IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStoppedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastDisabled(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastEnabled(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOff();
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOn();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRadioPowerState(boolean, int) throws java.lang.RuntimeException;
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRssiChanged(@IntRange(from=0xffffff81, to=0) int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStartedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStoppedFromSource(@NonNull android.os.WorkSource);
@@ -7354,7 +7373,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
- method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index feadd06..9cf9ce4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -850,6 +850,7 @@
}
public class AudioManager {
+ method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method public boolean hasRegisteredDynamicPolicy();
}
@@ -871,6 +872,9 @@
method public static float getMasterBalance();
method public static final int getNumStreamTypes();
method public static int setMasterBalance(float);
+ field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
+ field public static final int DEVICE_ROLE_NONE = 0; // 0x0
+ field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
field public static final int STREAM_DEFAULT = -1; // 0xffffffff
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6edc8ea..9acf6756 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -53,7 +53,6 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
-import android.net.NetworkUtils;
import android.net.PrivateDnsConnectivityChecker;
import android.net.ProxyInfo;
import android.net.Uri;
@@ -90,6 +89,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -11466,7 +11466,7 @@
return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
}
- if (NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+ if (NetworkUtilsInternal.isWeaklyValidatedHostname(privateDnsHost)) {
if (!PrivateDnsConnectivityChecker.canConnectToPrivateDnsServer(privateDnsHost)) {
return PRIVATE_DNS_SET_ERROR_HOST_NOT_SERVING;
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2dfbb3a..e4b2d70 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2933,6 +2933,16 @@
}
});
}
+ synchronized (mBluetoothConnectionCallbackExecutorMap) {
+ if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+ try {
+ mService.registerBluetoothConnectionCallback(mConnectionCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
+ + "connection callback", e);
+ }
+ }
+ }
}
public void onBluetoothServiceDown() {
@@ -3582,25 +3592,25 @@
return false;
}
- // If the callback map is empty, we register the service-to-app callback
- if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
- return false;
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- mBluetoothConnectionCallbackExecutorMap.remove(callback);
- } finally {
- mServiceLock.readLock().unlock();
- }
- }
-
- // Adds the passed in callback to our map of callbacks to executors
synchronized (mBluetoothConnectionCallbackExecutorMap) {
+ // If the callback map is empty, we register the service-to-app callback
+ if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) {
+ return false;
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ mBluetoothConnectionCallbackExecutorMap.remove(callback);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ // Adds the passed in callback to our map of callbacks to executors
if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) {
throw new IllegalArgumentException("This callback has already been registered");
}
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index f0fe92e..55ae218 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -85,11 +85,14 @@
/**
* Interface data activity status is changed.
*
- * @param iface The interface.
+ * @param label Unique identifier indicates the network type of the data activity change.
* @param active True if the interface is actively transmitting data, false if it is idle.
* @param tsNanos Elapsed realtime in nanos when the state of the network interface changed.
+ * @param uid Uid of this event. It represents the uid that was responsible for waking the
+ * radio. For those events that are reported by system itself, not from specific uid,
+ * use -1 for the events which means no uid.
*/
- void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos);
+ void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos, int uid);
/**
* Information about available DNS servers has been received.
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index fba7561..bf25602 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -26,9 +26,9 @@
import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
-import static android.net.NetworkUtils.multiplySafeByRational;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
import static com.android.internal.util.ArrayUtils.total;
import android.compat.annotation.UnsupportedAppUsage;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index d84ee2a..b5962c5 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,14 +16,9 @@
package android.net;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-
-import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.system.ErrnoException;
-import android.system.Os;
import android.util.Log;
import android.util.Pair;
@@ -155,14 +150,6 @@
public static native Network getDnsNetwork() throws ErrnoException;
/**
- * Allow/Disallow creating AF_INET/AF_INET6 sockets and DNS lookups for current process.
- *
- * @param allowNetworking whether to allow or disallow creating AF_INET/AF_INET6 sockets
- * and DNS lookups.
- */
- public static native void setAllowNetworkingForProcess(boolean allowNetworking);
-
- /**
* Get the tcp repair window associated with the {@code fd}.
*
* @param fd the tcp socket's {@link FileDescriptor}.
@@ -437,60 +424,4 @@
return routedIPCount;
}
- private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6};
-
- /**
- * Returns true if the hostname is weakly validated.
- * @param hostname Name of host to validate.
- * @return True if it's a valid-ish hostname.
- *
- * @hide
- */
- public static boolean isWeaklyValidatedHostname(@NonNull String hostname) {
- // TODO(b/34953048): Use a validation method that permits more accurate,
- // but still inexpensive, checking of likely valid DNS hostnames.
- final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$";
- if (!hostname.matches(weakHostnameRegex)) {
- return false;
- }
-
- for (int address_family : ADDRESS_FAMILIES) {
- if (Os.inet_pton(address_family, hostname) != null) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Safely multiple a value by a rational.
- * <p>
- * Internally it uses integer-based math whenever possible, but switches
- * over to double-based math if values would overflow.
- * @hide
- */
- public static long multiplySafeByRational(long value, long num, long den) {
- if (den == 0) {
- throw new ArithmeticException("Invalid Denominator");
- }
- long x = value;
- long y = num;
-
- // Logic shamelessly borrowed from Math.multiplyExact()
- long r = x * y;
- long ax = Math.abs(x);
- long ay = Math.abs(y);
- if (((ax | ay) >>> 31 != 0)) {
- // Some bits greater than 2^31 that might cause overflow
- // Check the result using the divide operator
- // and check for the special case of Long.MIN_VALUE * -1
- if (((y != 0) && (r / y != x)) ||
- (x == Long.MIN_VALUE && y == -1)) {
- // Use double math to avoid overflowing
- return (long) (((double) num / den) * value);
- }
- }
- return r / den;
- }
}
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 46d1c1c..19c183f 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -30,6 +30,30 @@
/**
* VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
*
+ * <p>A VCN creates a virtualization layer to allow MVNOs to aggregate heterogeneous physical
+ * networks, unifying them as a single carrier network. This enables infrastructure flexibility on
+ * the part of MVNOs without impacting user connectivity, abstracting the physical network
+ * technologies as an implementation detail of their public network.
+ *
+ * <p>Each VCN virtualizes an Carrier's network by building tunnels to a carrier's core network over
+ * carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions
+ * between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link
+ * android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on
+ * a profile or suggestion in the specified Subscription Group.
+ *
+ * <p>The VCN can be configured to expose one or more {@link android.net.Network}(s), each with
+ * different capabilities, allowing for APN virtualization.
+ *
+ * <p>If a tunnel fails to connect, or otherwise encounters a fatal error, the VCN will attempt to
+ * reestablish the connection. If the tunnel still has not reconnected after a system-determined
+ * timeout, the VCN Safe Mode (see below) will be entered.
+ *
+ * <p>The VCN Safe Mode ensures that users (and carriers) have a fallback to restore system
+ * connectivity to update profiles, diagnose issues, contact support, or perform other remediation
+ * tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default.
+ * Additionally, during Safe Mode, the VCN will continue to retry the connections, and will
+ * automatically exit Safe Mode if all active tunnels connect successfully.
+ *
* @hide
*/
@SystemService(Context.VCN_MANAGEMENT_SERVICE)
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index a9585c6..258e58d 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.WifiBatteryStats;
+import android.telephony.DataConnectionRealTimeInfo;
import com.android.internal.app.IBatteryStats;
@@ -376,4 +377,50 @@
e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Indicates that the radio power state has changed.
+ *
+ * @param isActive indicates if the mobile radio is powered.
+ * @param uid Uid of this event. For the active state it represents the uid that was responsible
+ * for waking the radio, or -1 if the system was responsible for waking the radio.
+ * For inactive state, the UID should always be -1.
+ * @throws RuntimeException if there are binder remote-invocation errors.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportMobileRadioPowerState(boolean isActive, int uid) throws RuntimeException {
+ try {
+ mBatteryStats.noteMobileRadioPowerState(getDataConnectionPowerState(isActive),
+ SystemClock.elapsedRealtimeNanos(), uid);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that the wifi power state has changed.
+ *
+ * @param isActive indicates if the wifi radio is powered.
+ * @param uid Uid of this event. For the active state it represents the uid that was responsible
+ * for waking the radio, or -1 if the system was responsible for waking the radio.
+ * For inactive state, the UID should always be -1.
+ * @throws RuntimeException if there are binder remote-invocation errors.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportWifiRadioPowerState(boolean isActive, int uid) throws RuntimeException {
+ try {
+ mBatteryStats.noteWifiRadioPowerState(getDataConnectionPowerState(isActive),
+ SystemClock.elapsedRealtimeNanos(), uid);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ private static int getDataConnectionPowerState(boolean isActive) {
+ // TODO: DataConnectionRealTimeInfo is under telephony package but the constants are used
+ // for both Wifi and mobile. It would make more sense to separate the constants to a generic
+ // class or move it to generic package.
+ return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+ : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ }
}
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 5f8b932..2052883 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -30,5 +30,6 @@
boolean requestLskf(in String packageName, in IntentSender sender);
boolean clearLskf(in String packageName);
boolean isLskfCaptured(in String packageName);
+ boolean rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
boolean rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 189e62b..6713de8 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -687,8 +687,8 @@
}
/**
- * Request that the device reboot and apply the update that has been prepared. Callers are
- * recommended to use {@link #rebootAndApply(Context, String, boolean)} instead.
+ * Request that the device reboot and apply the update that has been prepared. This API is
+ * deprecated, and is expected to be used by OTA only on devices running Android 11.
*
* @param context the Context to use.
* @param updateToken this parameter is deprecated and won't be used. See details in
@@ -699,18 +699,18 @@
* unattended reboot or if the {@code updateToken} did not match the previously
* given token
* @hide
+ * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead
*/
@SystemApi
- @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
- android.Manifest.permission.REBOOT})
+ @RequiresPermission(android.Manifest.permission.RECOVERY)
public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken,
@NonNull String reason) throws IOException {
if (updateToken == null) {
throw new NullPointerException("updateToken == null");
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
- // OTA is the sole user before S, and a slot switch is required for ota update.
- if (!rs.rebootWithLskf(context.getPackageName(), reason, true)) {
+ // OTA is the sole user, who expects a slot switch.
+ if (!rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)) {
throw new IOException("system not prepared to apply update");
}
}
@@ -738,7 +738,7 @@
*
* @param context the Context to use.
* @param reason the reboot reason to give to the {@link PowerManager}
- * @param slotSwitch true if the caller intends to switch the slot on an A/B device.
+ * @param slotSwitch true if the caller expects the slot to be switched on A/B devices.
* @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
* unattended reboot.
* @hide
@@ -1387,6 +1387,21 @@
}
}
+
+ /**
+ * Calls the recovery system service to reboot and apply update. This is the legacy API and
+ * expects a slot switch for A/B devices.
+ *
+ */
+ private boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason)
+ throws IOException {
+ try {
+ return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason);
+ } catch (RemoteException e) {
+ throw new IOException("could not reboot for update");
+ }
+ }
+
/**
* Internally, recovery treats each line of the command file as a separate
* argv, so we only need to protect against newlines and nulls.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index fa1b7d5..38a59f0 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4305,13 +4305,23 @@
* <P>
* Type: INTEGER (A bitmask of CARRIER_PRESENCE_* fields)
* </P>
+ *
+ * @deprecated The contacts database will only show presence
+ * information on devices where
+ * {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is true,
+ * otherwise use {@link android.telephony.ims.RcsUceAdapter}.
*/
+ @Deprecated
public static final String CARRIER_PRESENCE = "carrier_presence";
/**
* Indicates that the entry is Video Telephony (VT) capable on the
* current carrier. An allowed bitmask of {@link #CARRIER_PRESENCE}.
+ *
+ * @deprecated Same as {@link DataColumns#CARRIER_PRESENCE}.
+ *
*/
+ @Deprecated
public static final int CARRIER_PRESENCE_VT_CAPABLE = 0x01;
/**
diff --git a/core/java/com/android/internal/net/NetworkUtilsInternal.java b/core/java/com/android/internal/net/NetworkUtilsInternal.java
new file mode 100644
index 0000000..571d7e7
--- /dev/null
+++ b/core/java/com/android/internal/net/NetworkUtilsInternal.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 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.internal.net;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import android.annotation.NonNull;
+import android.system.Os;
+
+/** @hide */
+public class NetworkUtilsInternal {
+
+ private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6};
+
+ /**
+ * Allow/Disallow creating AF_INET/AF_INET6 sockets and DNS lookups for current process.
+ *
+ * @param allowNetworking whether to allow or disallow creating AF_INET/AF_INET6 sockets
+ * and DNS lookups.
+ */
+ public static native void setAllowNetworkingForProcess(boolean allowNetworking);
+
+ /**
+ * Returns true if the hostname is weakly validated.
+ * @param hostname Name of host to validate.
+ * @return True if it's a valid-ish hostname.
+ *
+ * @hide
+ */
+ public static boolean isWeaklyValidatedHostname(@NonNull String hostname) {
+ // TODO(b/34953048): Use a validation method that permits more accurate,
+ // but still inexpensive, checking of likely valid DNS hostnames.
+ final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$";
+ if (!hostname.matches(weakHostnameRegex)) {
+ return false;
+ }
+
+ for (int address_family : ADDRESS_FAMILIES) {
+ if (Os.inet_pton(address_family, hostname) != null) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Safely multiple a value by a rational.
+ * <p>
+ * Internally it uses integer-based math whenever possible, but switches
+ * over to double-based math if values would overflow.
+ * @hide
+ */
+ public static long multiplySafeByRational(long value, long num, long den) {
+ if (den == 0) {
+ throw new ArithmeticException("Invalid Denominator");
+ }
+ long x = value;
+ long y = num;
+
+ // Logic shamelessly borrowed from Math.multiplyExact()
+ long r = x * y;
+ long ax = Math.abs(x);
+ long ay = Math.abs(y);
+ if (((ax | ay) >>> 31 != 0)) {
+ // Some bits greater than 2^31 that might cause overflow
+ // Check the result using the divide operator
+ // and check for the special case of Long.MIN_VALUE * -1
+ if (((y != 0) && (r / y != x))
+ || (x == Long.MIN_VALUE && y == -1)) {
+ // Use double math to avoid overflowing
+ return (long) (((double) num / den) * value);
+ }
+ }
+ return r / den;
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index e595db3..c416814 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -78,13 +78,16 @@
boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();
if (useSystemSuspend) {
- // Get both kernel and native wakelock stats from SystemSuspend
- updateVersion(staleStats);
- if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
- Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
- return null;
+ // static read/write lock protection for sKernelWakelockUpdateVersion
+ synchronized (KernelWakelockReader.class) {
+ // Get both kernel and native wakelock stats from SystemSuspend
+ updateVersion(staleStats);
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend");
+ return null;
+ }
+ return removeOldStats(staleStats);
}
- return removeOldStats(staleStats);
} else {
Arrays.fill(mKernelWakelockBuffer, (byte) 0);
int len = 0;
@@ -141,14 +144,17 @@
}
}
- updateVersion(staleStats);
- // Get native wakelock stats from SystemSuspend
- if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
- Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+ // static read/write lock protection for sKernelWakelockUpdateVersion
+ synchronized (KernelWakelockReader.class) {
+ updateVersion(staleStats);
+ // Get native wakelock stats from SystemSuspend
+ if (getWakelockStatsFromSystemSuspend(staleStats) == null) {
+ Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
+ }
+ // Get kernel wakelock stats
+ parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
+ return removeOldStats(staleStats);
}
- // Get kernel wakelock stats
- parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
- return removeOldStats(staleStats);
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 15d4525..0381a75 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -24,7 +24,6 @@
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
-import android.net.NetworkUtils;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
@@ -35,6 +34,8 @@
import android.system.Os;
import android.util.Log;
+import com.android.internal.net.NetworkUtilsInternal;
+
import dalvik.annotation.optimization.FastNative;
import dalvik.system.ZygoteHooks;
@@ -352,7 +353,7 @@
// If no GIDs were specified, don't make any permissions changes based on groups.
if (gids != null && gids.length > 0) {
- NetworkUtils.setAllowNetworkingForProcess(containsInetGid(gids));
+ NetworkUtilsInternal.setAllowNetworkingForProcess(containsInetGid(gids));
}
}
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 8b9acd3..a045451 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -2,3 +2,4 @@
per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file Protocol* = etancohen@google.com, lorenzo@google.com
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file DataClass* = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 2a9c0b4..0e8d61a 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -64,7 +64,8 @@
}
@Override
- public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) {
+ public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos,
+ int uid) {
// default no-op
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 1968146..b4572fd 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -182,6 +182,7 @@
"android_content_res_Configuration.cpp",
"android_security_Scrypt.cpp",
"com_android_internal_content_om_OverlayConfig.cpp",
+ "com_android_internal_net_NetworkUtilsInternal.cpp",
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
"com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7b708ef..3198cb1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -187,6 +187,7 @@
extern int register_android_security_Scrypt(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
+extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
@@ -1520,6 +1521,7 @@
REG_JNI(register_android_os_SharedMemory),
REG_JNI(register_android_os_incremental_IncrementalManager),
REG_JNI(register_com_android_internal_content_om_OverlayConfig),
+ REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_os_ZygoteInit),
diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp
index e79c95e..2a16dce 100644
--- a/core/jni/android_media_AudioDeviceAttributes.cpp
+++ b/core/jni/android_media_AudioDeviceAttributes.cpp
@@ -31,7 +31,7 @@
const AudioDeviceTypeAddr *devTypeAddr) {
jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
jint jNativeType = (jint)devTypeAddr->mType;
- ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
+ ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress()));
*jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
jNativeType, jAddress.get());
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7493e39..20e64ff 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -172,6 +172,8 @@
jmethodID postRecordConfigEventFromNative;
} gAudioPolicyEventHandlerMethods;
+static struct { jmethodID add; } gListMethods;
+
//
// JNI Initialization for OpenSLES routing
//
@@ -292,25 +294,30 @@
return old;
}
-#define check_AudioSystem_Command(status) _check_AudioSystem_Command(__func__, (status))
+#define check_AudioSystem_Command(...) _check_AudioSystem_Command(__func__, __VA_ARGS__)
-static int _check_AudioSystem_Command(const char* caller, status_t status)
-{
- ALOGE_IF(status, "Command failed for %s: %d", caller, status);
+static int _check_AudioSystem_Command(const char *caller, status_t status,
+ std::vector<status_t> ignoredErrors = {}) {
+ int jniStatus = kAudioStatusOk;
switch (status) {
case DEAD_OBJECT:
- return kAudioStatusMediaServerDied;
+ jniStatus = kAudioStatusMediaServerDied;
+ break;
case NO_ERROR:
- return kAudioStatusOk;
+ break;
default:
+ if (std::find(begin(ignoredErrors), end(ignoredErrors), status) == end(ignoredErrors)) {
+ jniStatus = kAudioStatusError;
+ }
break;
}
- return kAudioStatusError;
+ ALOGE_IF(jniStatus != kAudioStatusOk, "Command failed for %s: %d", caller, status);
+ return jniStatus;
}
static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes,
jobjectArray deviceAddresses,
- Vector<AudioDeviceTypeAddr> &audioDeviceTypeAddrVector) {
+ AudioDeviceTypeAddrVector &audioDeviceTypeAddrVector) {
if (deviceTypes == nullptr || deviceAddresses == nullptr) {
return (jint)AUDIO_JAVA_BAD_VALUE;
}
@@ -337,7 +344,7 @@
}
const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL);
AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address);
- audioDeviceTypeAddrVector.add(dev);
+ audioDeviceTypeAddrVector.push_back(dev);
env->ReleaseStringUTFChars((jstring)addrJobj, address);
}
env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
@@ -2063,7 +2070,7 @@
static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz,
jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) {
- Vector<AudioDeviceTypeAddr> deviceVector;
+ AudioDeviceTypeAddrVector deviceVector;
jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
if (results != NO_ERROR) {
return results;
@@ -2081,7 +2088,7 @@
static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz,
jint userId, jintArray deviceTypes,
jobjectArray deviceAddresses) {
- Vector<AudioDeviceTypeAddr> deviceVector;
+ AudioDeviceTypeAddrVector deviceVector;
jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
if (results != NO_ERROR) {
return results;
@@ -2362,48 +2369,124 @@
return AudioSystem::isCallScreenModeSupported();
}
-static jint
-android_media_AudioSystem_setPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
- jint strategy, jint deviceType, jstring deviceAddress) {
-
- const char *c_address = env->GetStringUTFChars(deviceAddress, NULL);
+static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jint role,
+ jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
int status = check_AudioSystem_Command(
- AudioSystem::setPreferredDeviceForStrategy((product_strategy_t)strategy,
- AudioDeviceTypeAddr((audio_devices_t)
- deviceType,
- c_address)));
- env->ReleaseStringUTFChars(deviceAddress, c_address);
+ AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy,
+ (device_role_t)role, nDevices));
return (jint) status;
}
-static jint
-android_media_AudioSystem_removePreferredDeviceForStrategy(JNIEnv *env, jobject thiz, jint strategy)
-{
- return (jint) check_AudioSystem_Command(
- AudioSystem::removePreferredDeviceForStrategy((product_strategy_t) strategy));
+static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jint role) {
+ return (jint)
+ check_AudioSystem_Command(AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)
+ strategy,
+ (device_role_t)
+ role),
+ {NAME_NOT_FOUND});
}
-static jint
-android_media_AudioSystem_getPreferredDeviceForStrategy(JNIEnv *env, jobject thiz,
- jint strategy, jobjectArray jDeviceArray)
-{
- if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) {
- ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__);
- return (jint)AUDIO_JAVA_BAD_VALUE;
- }
-
- AudioDeviceTypeAddr elDevice;
+static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject thiz,
+ jint strategy, jint role,
+ jobject jDevices) {
+ AudioDeviceTypeAddrVector nDevices;
status_t status = check_AudioSystem_Command(
- AudioSystem::getPreferredDeviceForStrategy((product_strategy_t) strategy, elDevice));
+ AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy,
+ (device_role_t)role, nDevices));
if (status != NO_ERROR) {
return (jint) status;
}
- jobject jAudioDeviceAttributes = NULL;
- jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &elDevice);
- if (jStatus == AUDIO_JAVA_SUCCESS) {
- env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAttributes);
+ for (const auto &device : nDevices) {
+ jobject jAudioDeviceAttributes = NULL;
+ jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+ env->DeleteLocalRef(jAudioDeviceAttributes);
}
- return jStatus;
+ return AUDIO_JAVA_SUCCESS;
+}
+
+static jint android_media_AudioSystem_setDevicesRoleForCapturePreset(
+ JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
+ int status = check_AudioSystem_Command(
+ AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ return (jint)status;
+}
+
+static jint android_media_AudioSystem_addDevicesRoleForCapturePreset(
+ JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
+ int status = check_AudioSystem_Command(
+ AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ return (jint)status;
+}
+
+static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset(
+ JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+ jobjectArray jDeviceAddresses) {
+ AudioDeviceTypeAddrVector nDevices;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+ if (results != NO_ERROR) {
+ return results;
+ }
+ int status = check_AudioSystem_Command(
+ AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ return (jint)status;
+}
+
+static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz,
+ jint capturePreset,
+ jint role) {
+ return (jint)check_AudioSystem_Command(
+ AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role));
+}
+
+static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz,
+ jint capturePreset,
+ jint role,
+ jobject jDevices) {
+ AudioDeviceTypeAddrVector nDevices;
+ status_t status = check_AudioSystem_Command(
+ AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset,
+ (device_role_t)role, nDevices));
+ if (status != NO_ERROR) {
+ return (jint)status;
+ }
+ for (const auto &device : nDevices) {
+ jobject jAudioDeviceAttributes = NULL;
+ jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+ env->DeleteLocalRef(jAudioDeviceAttributes);
+ }
+ return AUDIO_JAVA_SUCCESS;
}
static jint
@@ -2551,12 +2634,22 @@
{"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
{"isCallScreeningModeSupported", "()Z",
(void *)android_media_AudioSystem_isCallScreeningModeSupported},
- {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I",
- (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
- {"removePreferredDeviceForStrategy", "(I)I",
- (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
- {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAttributes;)I",
- (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
+ {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setDevicesRoleForStrategy},
+ {"removeDevicesRoleForStrategy", "(II)I",
+ (void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
+ {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
+ (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy},
+ {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset},
+ {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset},
+ {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+ (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset},
+ {"clearDevicesRoleForCapturePreset", "(II)I",
+ (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset},
+ {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
+ (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset},
{"getDevicesForAttributes",
"(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
(void *)android_media_AudioSystem_getDevicesForAttributes},
@@ -2758,6 +2851,9 @@
gMidAudioRecordRoutingProxy_release =
android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
+ jclass listClass = FindClassOrDie(env, "java/util/List");
+ gListMethods.add = GetMethodIDOrDie(env, listClass, "add", "(Ljava/lang/Object;)Z");
+
AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 8d4c4e5..2155246 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2008, The Android Open Source Project
+ * Copyright 2020, 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.
@@ -28,7 +28,6 @@
#include <netinet/udp.h>
#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
-#include <android_runtime/AndroidRuntime.h>
#include <cutils/properties.h>
#include <nativehelper/JNIPlatformHelp.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -226,11 +225,6 @@
class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass);
}
-static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz,
- jboolean hasConnectivity) {
- setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
-}
-
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
if (javaFd == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -288,7 +282,6 @@
{ "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
{ "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel },
{ "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork },
- { "setAllowNetworkingForProcess", "(Z)V", (void *)android_net_utils_setAllowNetworkingForProcess },
};
// clang-format on
diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
new file mode 100644
index 0000000..10fc18d
--- /dev/null
+++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#include "NetdClient.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+namespace android {
+static void android_net_utils_setAllowNetworkingForProcess(JNIEnv *env, jobject thiz,
+ jboolean hasConnectivity) {
+ setAllowNetworkingForProcess(hasConnectivity == JNI_TRUE);
+}
+
+static const JNINativeMethod gNetworkUtilMethods[] = {
+ {"setAllowNetworkingForProcess", "(Z)V",
+ (void *)android_net_utils_setAllowNetworkingForProcess},
+};
+
+int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv *env) {
+ return RegisterMethodsOrDie(env, "com/android/internal/net/NetworkUtilsInternal",
+ gNetworkUtilMethods, NELEM(gNetworkUtilMethods));
+}
+
+} // namespace android
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
index 83ff84d..56f7ea8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java
@@ -309,7 +309,7 @@
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index aab84e3..64da837 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -330,7 +330,7 @@
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 176507c..403da18 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -366,10 +366,10 @@
for (Authorization a : response.metadata.authorizations) {
switch (a.keyParameter.tag) {
case KeymasterDefs.KM_TAG_ALGORITHM:
- keymasterAlgorithm = a.keyParameter.integer;
+ keymasterAlgorithm = a.keyParameter.value.getAlgorithm();
break;
case KeymasterDefs.KM_TAG_DIGEST:
- if (keymasterDigest == -1) keymasterDigest = a.keyParameter.integer;
+ if (keymasterDigest == -1) keymasterDigest = a.keyParameter.value.getDigest();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 9d3b970..74503e1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -102,7 +102,8 @@
insideSecureHardware =
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
securityLevel = a.securityLevel;
- origin = KeyProperties.Origin.fromKeymaster(a.keyParameter.integer);
+ origin = KeyProperties.Origin.fromKeymaster(
+ a.keyParameter.value.getOrigin());
break;
case KeymasterDefs.KM_TAG_KEY_SIZE:
long keySizeUnsigned = KeyStore2ParameterUtils.getUnsignedInt(a);
@@ -113,45 +114,51 @@
keySize = (int) keySizeUnsigned;
break;
case KeymasterDefs.KM_TAG_PURPOSE:
- purposes |= KeyProperties.Purpose.fromKeymaster(a.keyParameter.integer);
+ purposes |= KeyProperties.Purpose.fromKeymaster(
+ a.keyParameter.value.getKeyPurpose());
break;
case KeymasterDefs.KM_TAG_PADDING:
+ int paddingMode = a.keyParameter.value.getPaddingMode();
try {
- if (a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
- || a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PSS) {
+ if (paddingMode == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
+ || paddingMode == KeymasterDefs.KM_PAD_RSA_PSS) {
@KeyProperties.SignaturePaddingEnum String padding =
KeyProperties.SignaturePadding.fromKeymaster(
- a.keyParameter.integer);
+ paddingMode);
signaturePaddingsList.add(padding);
} else {
@KeyProperties.EncryptionPaddingEnum String jcaPadding =
KeyProperties.EncryptionPadding.fromKeymaster(
- a.keyParameter.integer);
+ paddingMode);
encryptionPaddingsList.add(jcaPadding);
}
} catch (IllegalArgumentException e) {
throw new ProviderException("Unsupported padding: "
- + a.keyParameter.integer);
+ + paddingMode);
}
break;
case KeymasterDefs.KM_TAG_DIGEST:
- digestsList.add(KeyProperties.Digest.fromKeymaster(a.keyParameter.integer));
+ digestsList.add(KeyProperties.Digest.fromKeymaster(
+ a.keyParameter.value.getDigest()));
break;
case KeymasterDefs.KM_TAG_BLOCK_MODE:
blockModesList.add(
- KeyProperties.BlockMode.fromKeymaster(a.keyParameter.integer)
+ KeyProperties.BlockMode.fromKeymaster(
+ a.keyParameter.value.getBlockMode())
);
break;
case KeymasterDefs.KM_TAG_USER_AUTH_TYPE:
+ int authenticatorType = a.keyParameter.value.getHardwareAuthenticatorType();
if (KeyStore2ParameterUtils.isSecureHardware(a.securityLevel)) {
- keymasterHwEnforcedUserAuthenticators = a.keyParameter.integer;
+ keymasterHwEnforcedUserAuthenticators = authenticatorType;
} else {
- keymasterSwEnforcedUserAuthenticators = a.keyParameter.integer;
+ keymasterSwEnforcedUserAuthenticators = authenticatorType;
}
break;
case KeymasterDefs.KM_TAG_USER_SECURE_ID:
keymasterSecureUserIds.add(
- KeymasterArguments.toUint64(a.keyParameter.longInteger));
+ KeymasterArguments.toUint64(
+ a.keyParameter.value.getLongInteger()));
break;
case KeymasterDefs.KM_TAG_ACTIVE_DATETIME:
keyValidityStart = KeyStore2ParameterUtils.getDate(a);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
index fd3d289..5c048a1 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
@@ -300,7 +300,7 @@
if (parameters != null) {
for (KeyParameter p : parameters) {
if (p.tag == KeymasterDefs.KM_TAG_NONCE) {
- returnedIv = p.blob;
+ returnedIv = p.value.getBlob();
break;
}
}
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index ae2e475..4c8ab8d 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -19,7 +19,9 @@
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricManager;
import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyParameterValue;
import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.keymint.Tag;
import android.security.GateKeeper;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
@@ -50,7 +52,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.boolValue = true;
+ p.value = KeyParameterValue.boolValue(true);
return p;
}
@@ -62,14 +64,40 @@
* @hide
*/
static @NonNull KeyParameter makeEnum(int tag, int v) {
- int type = KeymasterDefs.getTagType(tag);
- if (type != KeymasterDefs.KM_ENUM && type != KeymasterDefs.KM_ENUM_REP) {
- throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
+ KeyParameter kp = new KeyParameter();
+ kp.tag = tag;
+ switch (tag) {
+ case Tag.PURPOSE:
+ kp.value = KeyParameterValue.keyPurpose(v);
+ break;
+ case Tag.ALGORITHM:
+ kp.value = KeyParameterValue.algorithm(v);
+ break;
+ case Tag.BLOCK_MODE:
+ kp.value = KeyParameterValue.blockMode(v);
+ break;
+ case Tag.DIGEST:
+ kp.value = KeyParameterValue.digest(v);
+ break;
+ case Tag.EC_CURVE:
+ kp.value = KeyParameterValue.ecCurve(v);
+ break;
+ case Tag.ORIGIN:
+ kp.value = KeyParameterValue.origin(v);
+ break;
+ case Tag.PADDING:
+ kp.value = KeyParameterValue.paddingMode(v);
+ break;
+ case Tag.USER_AUTH_TYPE:
+ kp.value = KeyParameterValue.hardwareAuthenticatorType(v);
+ break;
+ case Tag.HARDWARE_TYPE:
+ kp.value = KeyParameterValue.securityLevel(v);
+ break;
+ default:
+ throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag);
}
- KeyParameter p = new KeyParameter();
- p.tag = tag;
- p.integer = v;
- return p;
+ return kp;
}
/**
@@ -86,7 +114,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.integer = v;
+ p.value = KeyParameterValue.integer(v);
return p;
}
@@ -104,7 +132,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.longInteger = v;
+ p.value = KeyParameterValue.longInteger(v);
return p;
}
@@ -121,7 +149,7 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.blob = b;
+ p.value = KeyParameterValue.blob(b);
return p;
}
@@ -138,9 +166,10 @@
}
KeyParameter p = new KeyParameter();
p.tag = tag;
- p.longInteger = date.getTime();
- if (p.longInteger < 0) {
- throw new IllegalArgumentException("Date tag value out of range: " + p.longInteger);
+ p.value = KeyParameterValue.dateTime(date.getTime());
+ if (p.value.getDateTime() < 0) {
+ throw new IllegalArgumentException("Date tag value out of range: "
+ + p.value.getDateTime());
}
return p;
}
@@ -160,18 +189,18 @@
throw new IllegalArgumentException("Not an int tag: " + param.keyParameter.tag);
}
// KM_UINT is 32 bits wide so we must suppress sign extension.
- return ((long) param.keyParameter.integer) & 0xffffffffL;
+ return ((long) param.keyParameter.value.getInteger()) & 0xffffffffL;
}
static @NonNull Date getDate(@NonNull Authorization param) {
if (KeymasterDefs.getTagType(param.keyParameter.tag) != KeymasterDefs.KM_DATE) {
throw new IllegalArgumentException("Not a date tag: " + param.keyParameter.tag);
}
- if (param.keyParameter.longInteger < 0) {
+ if (param.keyParameter.value.getDateTime() < 0) {
throw new IllegalArgumentException("Date Value too large: "
- + param.keyParameter.longInteger);
+ + param.keyParameter.value.getDateTime());
}
- return new Date(param.keyParameter.longInteger);
+ return new Date(param.keyParameter.value.getDateTime());
}
static void forEachSetFlag(int flags, Consumer<Integer> consumer) {
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 3b11854..f87a3d2 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -57,7 +57,7 @@
for (Authorization p : key.getAuthorizations()) {
switch(p.keyParameter.tag) {
case KeymasterDefs.KM_TAG_USER_SECURE_ID:
- keySids.add(p.keyParameter.longInteger);
+ keySids.add(p.keyParameter.value.getLongInteger());
break;
default:
break;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index a545b3d..bec80a7 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -670,7 +670,7 @@
}
auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
- if (UNLIKELY(!entry_flags)) {
+ if (UNLIKELY(!entry_flags.has_value())) {
return base::unexpected(entry_flags.error());
}
type_flags |= entry_flags.value();
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 4010e4e..bce70e2 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -897,12 +897,12 @@
// Decode the UTF-16 length. This is not used if we're not
// converting to UTF-16 from UTF-8.
const base::expected<size_t, IOError> u16len = decodeLength(&str);
- if (UNLIKELY(!u16len)) {
+ if (UNLIKELY(!u16len.has_value())) {
return base::unexpected(u16len.error());
}
const base::expected<size_t, IOError> u8len = decodeLength(&str);
- if (UNLIKELY(!u8len)) {
+ if (UNLIKELY(!u8len.has_value())) {
return base::unexpected(u8len.error());
}
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index a34aa72..87fb2c0 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -56,7 +56,8 @@
.package_len = package_name.size(),
};
- if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8()) {
+ if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8();
+ type_str.ok()) {
name.type = type_str->data();
name.type_len = type_str->size();
} else if (UNLIKELY(IsIOError(type_str))) {
@@ -64,7 +65,8 @@
}
if (name.type == nullptr) {
- if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16()) {
+ if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16();
+ type16_str.ok()) {
name.type16 = type16_str->data();
name.type_len = type16_str->size();
} else if (!type16_str.has_value()) {
@@ -72,7 +74,8 @@
}
}
- if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8()) {
+ if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8();
+ entry_str.ok()) {
name.entry = entry_str->data();
name.entry_len = entry_str->size();
} else if (UNLIKELY(IsIOError(entry_str))) {
@@ -80,7 +83,8 @@
}
if (name.entry == nullptr) {
- if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16()) {
+ if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16();
+ entry16_str.ok()) {
name.entry16 = entry16_str->data();
name.entry_len = entry16_str->size();
} else if (!entry16_str.has_value()) {
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 0ab62c1..7caac89 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -72,6 +72,11 @@
private final @Role int mRole;
/**
+ * The internal audio device type
+ */
+ private final int mNativeType;
+
+ /**
* @hide
* Constructor from a valid {@link AudioDeviceInfo}
* @param deviceInfo the connected audio device from which to obtain the device-identifying
@@ -83,6 +88,7 @@
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
mType = deviceInfo.getType();
mAddress = deviceInfo.getAddress();
+ mNativeType = deviceInfo.getInternalType();
}
/**
@@ -101,9 +107,12 @@
}
if (role == ROLE_OUTPUT) {
AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type);
- }
- if (role == ROLE_INPUT) {
+ mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
+ } else if (role == ROLE_INPUT) {
AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
+ mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
+ } else {
+ mNativeType = AudioSystem.DEVICE_NONE;
}
mRole = role;
@@ -111,10 +120,17 @@
mAddress = address;
}
- /*package*/ AudioDeviceAttributes(int nativeType, @NonNull String address) {
+ /**
+ * @hide
+ * Constructor from internal device type and address
+ * @param type the internal device type, as defined in {@link AudioSystem}
+ * @param address the address of the device, or an empty string for devices without one
+ */
+ public AudioDeviceAttributes(int nativeType, @NonNull String address) {
mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
mAddress = address;
+ mNativeType = nativeType;
}
/**
@@ -147,6 +163,15 @@
return mAddress;
}
+ /**
+ * @hide
+ * Returns the internal device type of a device
+ * @return the internal device type
+ */
+ public int getInternalType() {
+ return mNativeType;
+ }
+
@Override
public int hashCode() {
return Objects.hash(mRole, mType, mAddress);
@@ -172,10 +197,8 @@
public String toString() {
return new String("AudioDeviceAttributes:"
+ " role:" + roleToString(mRole)
- + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(
- AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType))
- : AudioSystem.getInputDeviceName(
- AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType)))
+ + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType)
+ : AudioSystem.getInputDeviceName(mNativeType))
+ " addr:" + mAddress);
}
@@ -189,12 +212,14 @@
dest.writeInt(mRole);
dest.writeInt(mType);
dest.writeString(mAddress);
+ dest.writeInt(mNativeType);
}
private AudioDeviceAttributes(@NonNull Parcel in) {
mRole = in.readInt();
mType = in.readInt();
mAddress = in.readString();
+ mNativeType = in.readInt();
}
public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR =
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index ff4a256..f79fc92 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -350,6 +350,14 @@
}
/**
+ * @hide
+ * @return the internal device tyoe
+ */
+ public int getInternalType() {
+ return mPort.type();
+ }
+
+ /**
* @return The internal device ID.
*/
public int getId() {
@@ -512,10 +520,21 @@
return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, TYPE_UNKNOWN);
}
+ /** @hide */
+ public static int convertDeviceTypeToInternalInputDevice(int deviceType) {
+ return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+ }
+
private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
+ /**
+ * EXT_TO_INT_INPUT_DEVICE_MAPPING aims at mapping external device type to internal input device
+ * type.
+ */
+ private static final SparseIntArray EXT_TO_INT_INPUT_DEVICE_MAPPING;
+
static {
INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, TYPE_BUILTIN_EARPIECE);
@@ -600,6 +619,32 @@
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
+
+ // privileges mapping to input device
+ EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_WIRED_HEADSET, AudioSystem.DEVICE_IN_WIRED_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_HDMI, AudioSystem.DEVICE_IN_HDMI);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_IN_TELEPHONY_RX);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_USB_ACCESSORY, AudioSystem.DEVICE_IN_USB_ACCESSORY);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_DEVICE, AudioSystem.DEVICE_IN_USB_DEVICE);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_HEADSET, AudioSystem.DEVICE_IN_USB_HEADSET);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_ANALOG, AudioSystem.DEVICE_IN_LINE);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_DIGITAL, AudioSystem.DEVICE_IN_SPDIF);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_IN_IP);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_IN_BUS);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+ TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX);
+ EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET);
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 107d656..7dff0c2 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -75,6 +75,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -1598,21 +1599,14 @@
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
@NonNull AudioDeviceAttributes device) {
- Objects.requireNonNull(strategy);
- Objects.requireNonNull(device);
- try {
- final int status =
- getService().setPreferredDeviceForStrategy(strategy.getId(), device);
- return status == AudioSystem.SUCCESS;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return setPreferredDevicesForStrategy(strategy, Arrays.asList(device));
}
/**
* @hide
- * Removes the preferred audio device previously set with
- * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}.
+ * Removes the preferred audio device(s) previously set with
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}.
* @param strategy the audio strategy whose routing will be affected
* @return true if the operation was successful, false otherwise (invalid strategy, or no
* device set for example)
@@ -1623,7 +1617,7 @@
Objects.requireNonNull(strategy);
try {
final int status =
- getService().removePreferredDeviceForStrategy(strategy.getId());
+ getService().removePreferredDevicesForStrategy(strategy.getId());
return status == AudioSystem.SUCCESS;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1633,18 +1627,74 @@
/**
* @hide
* Return the preferred device for an audio strategy, previously set with
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+ * @param strategy the strategy to query
+ * @return the preferred device for that strategy, if multiple devices are set as preferred
+ * devices, the first one in the list will be returned. Null will be returned if none was
+ * ever set or if the strategy is invalid
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Nullable
+ public AudioDeviceAttributes getPreferredDeviceForStrategy(
+ @NonNull AudioProductStrategy strategy) {
+ List<AudioDeviceAttributes> devices = getPreferredDevicesForStrategy(strategy);
+ return devices.isEmpty() ? null : devices.get(0);
+ }
+
+ /**
+ * @hide
+ * Set the preferred devices for a given strategy, i.e. the audio routing to be used by
+ * this audio strategy. Note that the devices may not be available at the time the preferred
+ * devices is set, but it will be used once made available.
+ * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
+ * this preference for this strategy.</p>
+ * Note that the list of devices is not a list ranked by preference, but a list of one or more
+ * devices used simultaneously to output the same audio signal.
+ * @param strategy the audio strategy whose routing will be affected
+ * @param devices a non-empty list of the audio devices to route to when available
+ * @return true if the operation was successful, false otherwise
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ Objects.requireNonNull(strategy);
+ Objects.requireNonNull(devices);
+ if (devices.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Tried to set preferred devices for strategy with a empty list");
+ }
+ for (AudioDeviceAttributes device : devices) {
+ Objects.requireNonNull(device);
+ }
+ try {
+ final int status =
+ getService().setPreferredDevicesForStrategy(strategy.getId(), devices);
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Return the preferred devices for an audio strategy, previously set with
* {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
* @param strategy the strategy to query
* @return the preferred device for that strategy, or null if none was ever set or if the
* strategy is invalid
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @Nullable AudioDeviceAttributes getPreferredDeviceForStrategy(
+ @NonNull
+ public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(
@NonNull AudioProductStrategy strategy) {
Objects.requireNonNull(strategy);
try {
- return getService().getPreferredDeviceForStrategy(strategy.getId());
+ return getService().getPreferredDevicesForStrategy(strategy.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1656,6 +1706,7 @@
* strategy.
* <p>Note that this listener will only be invoked whenever
* {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
* {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
* preferred device. It will not be invoked directly after registration with
* {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)}
@@ -1663,8 +1714,10 @@
* @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
* @see #removePreferredDeviceForStrategy(AudioProductStrategy)
* @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+ * @deprecated use #OnPreferredDevicesForStrategyChangedListener
*/
@SystemApi
+ @Deprecated
public interface OnPreferredDeviceForStrategyChangedListener {
/**
* Called on the listener to indicate that the preferred audio device for the given
@@ -1679,6 +1732,70 @@
/**
* @hide
+ * Interface to be notified of changes in the preferred audio devices set for a given audio
+ * strategy.
+ * <p>Note that this listener will only be invoked whenever
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
+ * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
+ * preferred device(s). It will not be invoked directly after registration with
+ * {@link #addOnPreferredDevicesForStrategyChangedListener(
+ * Executor, OnPreferredDevicesForStrategyChangedListener)}
+ * to indicate which strategies had preferred devices at the time of registration.</p>
+ * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+ * @see #setPreferredDevicesForStrategy(AudioProductStrategy, List)
+ * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
+ * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
+ * @see #getPreferredDevicesForStrategy(AudioProductStrategy)
+ */
+ @SystemApi
+ public interface OnPreferredDevicesForStrategyChangedListener {
+ /**
+ * Called on the listener to indicate that the preferred audio devices for the given
+ * strategy has changed.
+ * @param strategy the {@link AudioProductStrategy} whose preferred device changed
+ * @param devices a list of newly set preferred audio devices
+ */
+ void onPreferredDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
+ @NonNull List<AudioDeviceAttributes> devices);
+ }
+
+ /**
+ * @hide
+ * Adds a listener for being notified of changes to the strategy-preferred audio device.
+ * @param executor
+ * @param listener
+ * @throws SecurityException if the caller doesn't hold the required permission
+ * @deprecated use {@link #addOnPreferredDevicesForStrategyChangedListener(
+ * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Deprecated
+ public void addOnPreferredDeviceForStrategyChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+ throws SecurityException {
+ // No-op, the method is deprecated.
+ }
+
+ /**
+ * @hide
+ * Removes a previously added listener of changes to the strategy-preferred audio device.
+ * @param listener
+ * @deprecated use {@link #removeOnPreferredDevicesForStrategyChangedListener(
+ * AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @Deprecated
+ public void removeOnPreferredDeviceForStrategyChangedListener(
+ @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+ // No-op, the method is deprecated.
+ }
+
+ /**
+ * @hide
* Adds a listener for being notified of changes to the strategy-preferred audio device.
* @param executor
* @param listener
@@ -1686,16 +1803,16 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void addOnPreferredDeviceForStrategyChangedListener(
+ public void addOnPreferredDevicesForStrategyChangedListener(
@NonNull @CallbackExecutor Executor executor,
- @NonNull OnPreferredDeviceForStrategyChangedListener listener)
+ @NonNull OnPreferredDevicesForStrategyChangedListener listener)
throws SecurityException {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
synchronized (mPrefDevListenerLock) {
if (hasPrefDevListener(listener)) {
throw new IllegalArgumentException(
- "attempt to call addOnPreferredDeviceForStrategyChangedListener() "
+ "attempt to call addOnPreferredDevicesForStrategyChangedListener() "
+ "on a previously registered listener");
}
// lazy initialization of the list of strategy-preferred device listener
@@ -1707,10 +1824,10 @@
if (oldCbCount == 0 && mPrefDevListeners.size() > 0) {
// register binder for callbacks
if (mPrefDevDispatcherStub == null) {
- mPrefDevDispatcherStub = new StrategyPreferredDeviceDispatcherStub();
+ mPrefDevDispatcherStub = new StrategyPreferredDevicesDispatcherStub();
}
try {
- getService().registerStrategyPreferredDeviceDispatcher(mPrefDevDispatcherStub);
+ getService().registerStrategyPreferredDevicesDispatcher(mPrefDevDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1725,8 +1842,8 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void removeOnPreferredDeviceForStrategyChangedListener(
- @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
+ public void removeOnPreferredDevicesForStrategyChangedListener(
+ @NonNull OnPreferredDevicesForStrategyChangedListener listener) {
Objects.requireNonNull(listener);
synchronized (mPrefDevListenerLock) {
if (!removePrefDevListener(listener)) {
@@ -1737,7 +1854,7 @@
if (mPrefDevListeners.size() == 0) {
// unregister binder for callbacks
try {
- getService().unregisterStrategyPreferredDeviceDispatcher(
+ getService().unregisterStrategyPreferredDevicesDispatcher(
mPrefDevDispatcherStub);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1759,23 +1876,23 @@
private @Nullable ArrayList<PrefDevListenerInfo> mPrefDevListeners;
private static class PrefDevListenerInfo {
- final @NonNull OnPreferredDeviceForStrategyChangedListener mListener;
+ final @NonNull OnPreferredDevicesForStrategyChangedListener mListener;
final @NonNull Executor mExecutor;
- PrefDevListenerInfo(OnPreferredDeviceForStrategyChangedListener listener, Executor exe) {
+ PrefDevListenerInfo(OnPreferredDevicesForStrategyChangedListener listener, Executor exe) {
mListener = listener;
mExecutor = exe;
}
}
@GuardedBy("mPrefDevListenerLock")
- private StrategyPreferredDeviceDispatcherStub mPrefDevDispatcherStub;
+ private StrategyPreferredDevicesDispatcherStub mPrefDevDispatcherStub;
- private final class StrategyPreferredDeviceDispatcherStub
- extends IStrategyPreferredDeviceDispatcher.Stub {
+ private final class StrategyPreferredDevicesDispatcherStub
+ extends IStrategyPreferredDevicesDispatcher.Stub {
@Override
- public void dispatchPrefDeviceChanged(int strategyId,
- @Nullable AudioDeviceAttributes device) {
+ public void dispatchPrefDevicesChanged(int strategyId,
+ @NonNull List<AudioDeviceAttributes> devices) {
// make a shallow copy of listeners so callback is not executed under lock
final ArrayList<PrefDevListenerInfo> prefDevListeners;
synchronized (mPrefDevListenerLock) {
@@ -1790,7 +1907,7 @@
try {
for (PrefDevListenerInfo info : prefDevListeners) {
info.mExecutor.execute(() ->
- info.mListener.onPreferredDeviceForStrategyChanged(strategy, device));
+ info.mListener.onPreferredDevicesForStrategyChanged(strategy, devices));
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1800,7 +1917,7 @@
@GuardedBy("mPrefDevListenerLock")
private @Nullable PrefDevListenerInfo getPrefDevListenerInfo(
- OnPreferredDeviceForStrategyChangedListener listener) {
+ OnPreferredDevicesForStrategyChangedListener listener) {
if (mPrefDevListeners == null) {
return null;
}
@@ -1813,7 +1930,7 @@
}
@GuardedBy("mPrefDevListenerLock")
- private boolean hasPrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+ private boolean hasPrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
return getPrefDevListenerInfo(listener) != null;
}
@@ -1821,7 +1938,7 @@
/**
* @return true if the listener was removed from the list
*/
- private boolean removePrefDevListener(OnPreferredDeviceForStrategyChangedListener listener) {
+ private boolean removePrefDevListener(OnPreferredDevicesForStrategyChangedListener listener) {
final PrefDevListenerInfo infoToRemove = getPrefDevListenerInfo(listener);
if (infoToRemove != null) {
mPrefDevListeners.remove(infoToRemove);
@@ -1831,6 +1948,349 @@
}
//====================================================================
+ // Audio Capture Preset routing
+
+ /**
+ * @hide
+ * Set the preferred device for a given capture preset, i.e. the audio routing to be used by
+ * this capture preset. Note that the device may not be available at the time the preferred
+ * device is set, but it will be used once made available.
+ * <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference
+ * for this capture preset.</p>
+ * @param capturePreset the audio capture preset whose routing will be affected
+ * @param device the audio device to route to when available
+ * @return true if the operation was successful, false otherwise
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean setPreferredDeviceForCapturePreset(int capturePreset,
+ @NonNull AudioDeviceAttributes device) {
+ return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
+ }
+
+ /**
+ * @hide
+ * Remove all the preferred audio devices previously set
+ * @param capturePreset the audio capture preset whose routing will be affected
+ * @return true if the operation was successful, false otherwise (invalid capture preset, or no
+ * device set for example)
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
+ if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+ return false;
+ }
+ try {
+ final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset);
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Return the preferred devices for an audio capture preset, previously set with
+ * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)}
+ * @param capturePreset the capture preset to query
+ * @return a list that contains preferred devices for that capture preset.
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+ if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+ return new ArrayList<AudioDeviceAttributes>();
+ }
+ try {
+ return getService().getPreferredDevicesForCapturePreset(capturePreset);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private boolean setPreferredDevicesForCapturePreset(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ Objects.requireNonNull(devices);
+ if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+ return false;
+ }
+ if (devices.size() != 1) {
+ throw new IllegalArgumentException(
+ "Only support setting one preferred devices for capture preset");
+ }
+ for (AudioDeviceAttributes device : devices) {
+ Objects.requireNonNull(device);
+ }
+ try {
+ final int status =
+ getService().setPreferredDevicesForCapturePreset(capturePreset, devices);
+ return status == AudioSystem.SUCCESS;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Interface to be notified of changes in the preferred audio devices set for a given capture
+ * preset.
+ * <p>Note that this listener will only be invoked whenever
+ * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or
+ * {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in
+ * preferred device. It will not be invoked directly after registration with
+ * {@link #addOnPreferredDevicesForCapturePresetChangedListener(
+ * Executor, OnPreferredDevicesForCapturePresetChangedListener)}
+ * to indicate which strategies had preferred devices at the time of registration.</p>
+ * @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+ * @see #clearPreferredDevicesForCapturePreset(int)
+ * @see #getPreferredDevicesForCapturePreset(int)
+ */
+ @SystemApi
+ public interface OnPreferredDevicesForCapturePresetChangedListener {
+ /**
+ * Called on the listener to indicate that the preferred audio devices for the given
+ * capture preset has changed.
+ * @param capturePreset the capture preset whose preferred device changed
+ * @param devices a list of newly set preferred audio devices
+ */
+ void onPreferredDevicesForCapturePresetChanged(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices);
+ }
+
+ /**
+ * @hide
+ * Adds a listener for being notified of changes to the capture-preset-preferred audio device.
+ * @param executor
+ * @param listener
+ * @throws SecurityException if the caller doesn't hold the required permission
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void addOnPreferredDevicesForCapturePresetChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
+ throws SecurityException {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ int status = addOnDevRoleForCapturePresetChangedListener(
+ executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+ if (status == AudioSystem.ERROR) {
+ // This must not happen
+ throw new RuntimeException("Unknown error happened");
+ }
+ if (status == AudioSystem.BAD_VALUE) {
+ throw new IllegalArgumentException(
+ "attempt to call addOnPreferredDevicesForCapturePresetChangedListener() "
+ + "on a previously registered listener");
+ }
+ }
+
+ /**
+ * @hide
+ * Removes a previously added listener of changes to the capture-preset-preferred audio device.
+ * @param listener
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void removeOnPreferredDevicesForCapturePresetChangedListener(
+ @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
+ Objects.requireNonNull(listener);
+ int status = removeOnDevRoleForCapturePresetChangedListener(
+ listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+ if (status == AudioSystem.ERROR) {
+ // This must not happen
+ throw new RuntimeException("Unknown error happened");
+ }
+ if (status == AudioSystem.BAD_VALUE) {
+ throw new IllegalArgumentException(
+ "attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() "
+ + "on an unregistered listener");
+ }
+ }
+
+ private <T> int addOnDevRoleForCapturePresetChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull T listener, int deviceRole) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ DevRoleListeners<T> devRoleListeners =
+ (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+ if (devRoleListeners == null) {
+ return AudioSystem.ERROR;
+ }
+ synchronized (devRoleListeners.mDevRoleListenersLock) {
+ if (devRoleListeners.hasDevRoleListener(listener)) {
+ return AudioSystem.BAD_VALUE;
+ }
+ // lazy initialization of the list of device role listener
+ if (devRoleListeners.mListenerInfos == null) {
+ devRoleListeners.mListenerInfos = new ArrayList<>();
+ }
+ final int oldCbCount = devRoleListeners.mListenerInfos.size();
+ devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener));
+ if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) {
+ // register binder for callbacks
+ synchronized (mDevRoleForCapturePresetListenersLock) {
+ int deviceRoleListenerStatus = mDeviceRoleListenersStatus;
+ mDeviceRoleListenersStatus |= (1 << deviceRole);
+ if (deviceRoleListenerStatus != 0) {
+ // There are already device role changed listeners active.
+ return AudioSystem.SUCCESS;
+ }
+ if (mDevicesRoleForCapturePresetDispatcherStub == null) {
+ mDevicesRoleForCapturePresetDispatcherStub =
+ new CapturePresetDevicesRoleDispatcherStub();
+ }
+ try {
+ getService().registerCapturePresetDevicesRoleDispatcher(
+ mDevicesRoleForCapturePresetDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ return AudioSystem.SUCCESS;
+ }
+
+ private <T> int removeOnDevRoleForCapturePresetChangedListener(
+ @NonNull T listener, int deviceRole) {
+ Objects.requireNonNull(listener);
+ DevRoleListeners<T> devRoleListeners =
+ (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+ if (devRoleListeners == null) {
+ return AudioSystem.ERROR;
+ }
+ synchronized (devRoleListeners.mDevRoleListenersLock) {
+ if (!devRoleListeners.removeDevRoleListener(listener)) {
+ return AudioSystem.BAD_VALUE;
+ }
+ if (devRoleListeners.mListenerInfos.size() == 0) {
+ // unregister binder for callbacks
+ synchronized (mDevRoleForCapturePresetListenersLock) {
+ mDeviceRoleListenersStatus ^= (1 << deviceRole);
+ if (mDeviceRoleListenersStatus != 0) {
+ // There are some other device role changed listeners active.
+ return AudioSystem.SUCCESS;
+ }
+ try {
+ getService().unregisterCapturePresetDevicesRoleDispatcher(
+ mDevicesRoleForCapturePresetDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+ return AudioSystem.SUCCESS;
+ }
+
+ private final Map<Integer, Object> mDevRoleForCapturePresetListeners = new HashMap<>(){{
+ put(AudioSystem.DEVICE_ROLE_PREFERRED,
+ new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>());
+ }};
+
+ private class DevRoleListenerInfo<T> {
+ final @NonNull Executor mExecutor;
+ final @NonNull T mListener;
+ DevRoleListenerInfo(Executor executor, T listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+ }
+
+ private class DevRoleListeners<T> {
+ private final Object mDevRoleListenersLock = new Object();
+ @GuardedBy("mDevRoleListenersLock")
+ private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos;
+
+ @GuardedBy("mDevRoleListenersLock")
+ private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) {
+ if (mListenerInfos == null) {
+ return null;
+ }
+ for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) {
+ if (listenerInfo.mListener == listener) {
+ return listenerInfo;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mDevRoleListenersLock")
+ private boolean hasDevRoleListener(T listener) {
+ return getDevRoleListenerInfo(listener) != null;
+ }
+
+ @GuardedBy("mDevRoleListenersLock")
+ private boolean removeDevRoleListener(T listener) {
+ final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener);
+ if (infoToRemove != null) {
+ mListenerInfos.remove(infoToRemove);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private final Object mDevRoleForCapturePresetListenersLock = new Object();
+ /**
+ * Record if there is a listener added for device role change. If there is a listener added for
+ * a specified device role change, the bit at position `1 << device_role` is set.
+ */
+ @GuardedBy("mDevRoleForCapturePresetListenersLock")
+ private int mDeviceRoleListenersStatus = 0;
+ @GuardedBy("mDevRoleForCapturePresetListenersLock")
+ private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub;
+
+ private final class CapturePresetDevicesRoleDispatcherStub
+ extends ICapturePresetDevicesRoleDispatcher.Stub {
+
+ @Override
+ public void dispatchDevicesRoleChanged(
+ int capturePreset, int role, List<AudioDeviceAttributes> devices) {
+ final Object listenersObj = mDevRoleForCapturePresetListeners.get(role);
+ if (listenersObj == null) {
+ return;
+ }
+ switch (role) {
+ case AudioSystem.DEVICE_ROLE_PREFERRED: {
+ final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>
+ listeners =
+ (DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>)
+ listenersObj;
+ final ArrayList<DevRoleListenerInfo<
+ OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners;
+ synchronized (listeners.mDevRoleListenersLock) {
+ if (listeners.mListenerInfos.isEmpty()) {
+ return;
+ }
+ prefDevListeners = (ArrayList<DevRoleListenerInfo<
+ OnPreferredDevicesForCapturePresetChangedListener>>)
+ listeners.mListenerInfos.clone();
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ for (DevRoleListenerInfo<
+ OnPreferredDevicesForCapturePresetChangedListener> info :
+ prefDevListeners) {
+ info.mExecutor.execute(() ->
+ info.mListener.onPreferredDevicesForCapturePresetChanged(
+ capturePreset, devices));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+
+ //====================================================================
// Offload query
/**
* Returns whether offloaded playback of an audio format is supported on the device.
@@ -5664,6 +6124,29 @@
}
/**
+ * Returns an {@link AudioDeviceInfo} corresponding to the specified {@link AudioPort} ID.
+ * @param portId The audio port ID to look up for.
+ * @param flags A set of bitflags specifying the criteria to test.
+ * @see #GET_DEVICES_OUTPUTS
+ * @see #GET_DEVICES_INPUTS
+ * @see #GET_DEVICES_ALL
+ * @return An AudioDeviceInfo or null if no device with matching port ID is found.
+ * @hide
+ */
+ public static AudioDeviceInfo getDeviceForPortId(int portId, int flags) {
+ if (portId == 0) {
+ return null;
+ }
+ AudioDeviceInfo[] devices = getDevicesStatic(flags);
+ for (AudioDeviceInfo device : devices) {
+ if (device.getId() == portId) {
+ return device;
+ }
+ }
+ return null;
+ }
+
+ /**
* Registers an {@link AudioDeviceCallback} object to receive notifications of changes
* to the set of connected audio devices.
* @param callback The {@link AudioDeviceCallback} object to receive connect/disconnect
@@ -6206,6 +6689,297 @@
}
}
+ /**
+ * Selects the audio device that should be used for communication use cases, for instance voice
+ * or video calls. This method can be used by voice or video chat applications to select a
+ * different audio device than the one selected by default by the platform.
+ * <p>The device selection is expressed as an {@link AudioDeviceInfo}, of role sink
+ * ({@link AudioDeviceInfo#isSink()} is <code>true</code>) and of one of the following types:
+ * <ul>
+ * <li> {@link AudioDeviceInfo#TYPE_BUILTIN_EARPIECE}
+ * <li> {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}
+ * <li> {@link AudioDeviceInfo#TYPE_WIRED_HEADSET}
+ * <li> {@link AudioDeviceInfo#TYPE_BLUETOOTH_SCO}
+ * <li> {@link AudioDeviceInfo#TYPE_USB_HEADSET}
+ * <li> {@link AudioDeviceInfo#TYPE_BLE_HEADSET}
+ * </ul>
+ * The selection is active as long as the requesting application lives, until
+ * {@link #clearDeviceForCommunication} is called or until the device is disconnected.
+ * It is therefore important for applications to clear the request when a call ends or the
+ * application is paused.
+ * <p>In case of simultaneous requests by multiple applications the priority is given to the
+ * application currently controlling the audio mode (see {@link #setMode(int)}). This is the
+ * latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
+ * {@link #MODE_IN_CALL}. Note that <code>MODE_IN_CALL</code> can only be selected by the main
+ * telephony application with permission
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
+ * <p> If the requested devices is not currently available, the request will be rejected and
+ * the method will return false.
+ * <p>This API replaces the following deprecated APIs:
+ * <ul>
+ * <li> {@link #startBluetoothSco()}
+ * <li> {@link #stopBluetoothSco()}
+ * <li> {@link #setSpeakerphoneOn(boolean)}
+ * </ul>
+ * <h4>Example</h4>
+ * <p>The example below shows how to enable and disable speakerphone mode.
+ * <pre class="prettyprint">
+ * // Get an AudioManager instance
+ * AudioManager audioManager = Context.getSystemService(AudioManager.class);
+ * try {
+ * AudioDeviceInfo speakerDevice = null;
+ * AudioDeviceInfo[] devices = audioManager.getDevices(GET_DEVICES_OUTPUTS);
+ * for (AudioDeviceInfo device : devices) {
+ * if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+ * speakerDevice = device;
+ * break;
+ * }
+ * }
+ * if (speakerDevice != null) {
+ * // Turn speakerphone ON.
+ * boolean result = audioManager.setDeviceForCommunication(speakerDevice);
+ * if (!result) {
+ * // Handle error.
+ * }
+ * // Turn speakerphone OFF.
+ * audioManager.clearDeviceForCommunication();
+ * }
+ * } catch (IllegalArgumentException e) {
+ * // Handle exception.
+ * }
+ * </pre>
+ * @param device the requested audio device.
+ * @return <code>true</code> if the request was accepted, <code>false</code> otherwise.
+ * @throws IllegalArgumentException If an invalid device is specified.
+ */
+ public boolean setDeviceForCommunication(@NonNull AudioDeviceInfo device) {
+ Objects.requireNonNull(device);
+ try {
+ if (device.getId() == 0) {
+ throw new IllegalArgumentException("In valid device: " + device);
+ }
+ return getService().setDeviceForCommunication(mICallBack, device.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Cancels previous communication device selection made with
+ * {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+ */
+ public void clearDeviceForCommunication() {
+ try {
+ getService().setDeviceForCommunication(mICallBack, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns currently selected audio device for communication.
+ * <p>This API replaces the following deprecated APIs:
+ * <ul>
+ * <li> {@link #isBluetoothScoOn()}
+ * <li> {@link #isSpeakerphoneOn()}
+ * </ul>
+ * @return an {@link AudioDeviceInfo} indicating which audio device is
+ * currently selected or communication use cases or null if default selection
+ * is used.
+ */
+ @Nullable
+ public AudioDeviceInfo getDeviceForCommunication() {
+ try {
+ return getDeviceForPortId(
+ getService().getDeviceForCommunication(), GET_DEVICES_OUTPUTS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.
+ * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
+ * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
+ * The method will return null if no device of the provided type is connected.
+ * If more than one device of the provided type is connected, an object corresponding to the
+ * first device encountered in the enumeration list will be returned.
+ * @param deviceType The device device for which an <code>AudioDeviceInfo</code>
+ * object is queried.
+ * @return An AudioDeviceInfo object or null if no device with the requested type is connected.
+ * @throws IllegalArgumentException If an invalid device type is specified.
+ */
+ @TestApi
+ @Nullable
+ public static AudioDeviceInfo getDeviceInfoFromType(
+ @AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
+ AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_OUTPUTS);
+ for (AudioDeviceInfo device : devices) {
+ if (device.getType() == deviceType) {
+ return device;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Listener registered by client to be notified upon communication audio device change.
+ * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+ */
+ public interface OnCommunicationDeviceChangedListener {
+ /**
+ * Callback method called upon communication audio device change.
+ * @param device the audio device selected for communication use cases
+ */
+ void onCommunicationDeviceChanged(@Nullable AudioDeviceInfo device);
+ }
+
+ /**
+ * Adds a listener for being notified of changes to the communication audio device.
+ * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+ * @param executor
+ * @param listener
+ */
+ public void addOnCommunicationDeviceChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnCommunicationDeviceChangedListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ synchronized (mCommDevListenerLock) {
+ if (hasCommDevListener(listener)) {
+ throw new IllegalArgumentException(
+ "attempt to call addOnCommunicationDeviceChangedListener() "
+ + "on a previously registered listener");
+ }
+ // lazy initialization of the list of strategy-preferred device listener
+ if (mCommDevListeners == null) {
+ mCommDevListeners = new ArrayList<>();
+ }
+ final int oldCbCount = mCommDevListeners.size();
+ mCommDevListeners.add(new CommDevListenerInfo(listener, executor));
+ if (oldCbCount == 0 && mCommDevListeners.size() > 0) {
+ // register binder for callbacks
+ if (mCommDevDispatcherStub == null) {
+ mCommDevDispatcherStub = new CommunicationDeviceDispatcherStub();
+ }
+ try {
+ getService().registerCommunicationDeviceDispatcher(mCommDevDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a previously added listener of changes to the communication audio device.
+ * See {@link #setDeviceForCommunication(AudioDeviceInfo)}.
+ * @param listener
+ */
+ public void removeOnCommunicationDeviceChangedListener(
+ @NonNull OnCommunicationDeviceChangedListener listener) {
+ Objects.requireNonNull(listener);
+ synchronized (mCommDevListenerLock) {
+ if (!removeCommDevListener(listener)) {
+ throw new IllegalArgumentException(
+ "attempt to call removeOnCommunicationDeviceChangedListener() "
+ + "on an unregistered listener");
+ }
+ if (mCommDevListeners.size() == 0) {
+ // unregister binder for callbacks
+ try {
+ getService().unregisterCommunicationDeviceDispatcher(
+ mCommDevDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ mCommDevDispatcherStub = null;
+ mCommDevListeners = null;
+ }
+ }
+ }
+ }
+
+ private final Object mCommDevListenerLock = new Object();
+ /**
+ * List of listeners for preferred device for strategy and their associated Executor.
+ * List is lazy-initialized on first registration
+ */
+ @GuardedBy("mCommDevListenerLock")
+ private @Nullable ArrayList<CommDevListenerInfo> mCommDevListeners;
+
+ private static class CommDevListenerInfo {
+ final @NonNull OnCommunicationDeviceChangedListener mListener;
+ final @NonNull Executor mExecutor;
+
+ CommDevListenerInfo(OnCommunicationDeviceChangedListener listener, Executor exe) {
+ mListener = listener;
+ mExecutor = exe;
+ }
+ }
+
+ @GuardedBy("mCommDevListenerLock")
+ private CommunicationDeviceDispatcherStub mCommDevDispatcherStub;
+
+ private final class CommunicationDeviceDispatcherStub
+ extends ICommunicationDeviceDispatcher.Stub {
+
+ @Override
+ public void dispatchCommunicationDeviceChanged(int portId) {
+ // make a shallow copy of listeners so callback is not executed under lock
+ final ArrayList<CommDevListenerInfo> commDevListeners;
+ synchronized (mCommDevListenerLock) {
+ if (mCommDevListeners == null || mCommDevListeners.size() == 0) {
+ return;
+ }
+ commDevListeners = (ArrayList<CommDevListenerInfo>) mCommDevListeners.clone();
+ }
+ AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ for (CommDevListenerInfo info : commDevListeners) {
+ info.mExecutor.execute(() ->
+ info.mListener.onCommunicationDeviceChanged(device));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @GuardedBy("mCommDevListenerLock")
+ private @Nullable CommDevListenerInfo getCommDevListenerInfo(
+ OnCommunicationDeviceChangedListener listener) {
+ if (mCommDevListeners == null) {
+ return null;
+ }
+ for (CommDevListenerInfo info : mCommDevListeners) {
+ if (info.mListener == listener) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mCommDevListenerLock")
+ private boolean hasCommDevListener(OnCommunicationDeviceChangedListener listener) {
+ return getCommDevListenerInfo(listener) != null;
+ }
+
+ @GuardedBy("mCommDevListenerLock")
+ /**
+ * @return true if the listener was removed from the list
+ */
+ private boolean removeCommDevListener(OnCommunicationDeviceChangedListener listener) {
+ final CommDevListenerInfo infoToRemove = getCommDevListenerInfo(listener);
+ if (infoToRemove != null) {
+ mCommDevListeners.remove(infoToRemove);
+ return true;
+ }
+ return false;
+ }
+
//---------------------------------------------------------
// Inner classes
//--------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f1eb53d..18c8a72 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -28,11 +28,13 @@
import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.util.Pair;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -1371,6 +1373,11 @@
/** @hide */ public static final int FOR_VIBRATE_RINGING = 7;
private static final int NUM_FORCE_USE = 8;
+ // Device role in audio policy
+ public static final int DEVICE_ROLE_NONE = 0;
+ public static final int DEVICE_ROLE_PREFERRED = 1;
+ public static final int DEVICE_ROLE_DISABLED = 2;
+
/** @hide */
public static String forceUseUsageToString(int usage) {
switch (usage) {
@@ -1691,47 +1698,186 @@
/**
* @hide
- * Sets the preferred device to use for a given audio strategy in the audio policy engine
+ * Set device as role for product strategy.
* @param strategy the id of the strategy to configure
- * @param device the device type and address to route to when available
+ * @param role the role of the devices
+ * @param devices the list of devices to be set as role for the given strategy
* @return {@link #SUCCESS} if successfully set
*/
- public static int setPreferredDeviceForStrategy(
- int strategy, @NonNull AudioDeviceAttributes device) {
- return setPreferredDeviceForStrategy(strategy,
- AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
- device.getAddress());
+ public static int setDevicesRoleForStrategy(
+ int strategy, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ int[] types = new int[devices.size()];
+ String[] addresses = new String[devices.size()];
+ for (int i = 0; i < devices.size(); ++i) {
+ types[i] = devices.get(i).getInternalType();
+ addresses[i] = devices.get(i).getAddress();
+ }
+ return setDevicesRoleForStrategy(strategy, role, types, addresses);
}
- /**
- * @hide
- * Set device routing per product strategy.
- * @param strategy the id of the strategy to configure
- * @param deviceType the native device type, NOT AudioDeviceInfo types
- * @param deviceAddress the address of the device
- * @return {@link #SUCCESS} if successfully set
- */
- private static native int setPreferredDeviceForStrategy(
- int strategy, int deviceType, String deviceAddress);
/**
* @hide
- * Remove preferred routing for the strategy
+ * Set device as role for product strategy.
* @param strategy the id of the strategy to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int setDevicesRoleForStrategy(
+ int strategy, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Remove devices as role for the strategy
+ * @param strategy the id of the strategy to configure
+ * @param role the role of the devices
* @return {@link #SUCCESS} if successfully removed
*/
- public static native int removePreferredDeviceForStrategy(int strategy);
+ public static native int removeDevicesRoleForStrategy(int strategy, int role);
/**
* @hide
- * Query previously set preferred device for a strategy
+ * Query previously set devices as role for a strategy
* @param strategy the id of the strategy to query for
- * @param device an array of size 1 that will contain the preferred device, or null if
- * none was set
+ * @param role the role of the devices
+ * @param devices a list that will contain the devices of role
* @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
* and written to the array
*/
- public static native int getPreferredDeviceForStrategy(int strategy,
- AudioDeviceAttributes[] device);
+ public static native int getDevicesForRoleAndStrategy(
+ int strategy, int role, @NonNull List<AudioDeviceAttributes> devices);
+
+ // use case routing by capture preset
+
+ private static Pair<int[], String[]> populateInputDevicesTypeAndAddress(
+ @NonNull List<AudioDeviceAttributes> devices) {
+ int[] types = new int[devices.size()];
+ String[] addresses = new String[devices.size()];
+ for (int i = 0; i < devices.size(); ++i) {
+ types[i] = devices.get(i).getInternalType();
+ if (types[i] == AudioSystem.DEVICE_NONE) {
+ types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
+ devices.get(i).getType());
+ }
+ addresses[i] = devices.get(i).getAddress();
+ }
+ return new Pair<int[], String[]>(types, addresses);
+ }
+
+ /**
+ * @hide
+ * Set devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param devices the list of devices to be set as role for the given capture preset
+ * @return {@link #SUCCESS} if successfully set
+ */
+ public static int setDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+ return setDevicesRoleForCapturePreset(
+ capturePreset, role, typeAddresses.first, typeAddresses.second);
+ }
+
+ /**
+ * @hide
+ * Set devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int setDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Add devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param devices the list of devices to be added as role for the given capture preset
+ * @return {@link #SUCCESS} if successfully add
+ */
+ public static int addDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+ return addDevicesRoleForCapturePreset(
+ capturePreset, role, typeAddresses.first, typeAddresses.second);
+ }
+
+ /**
+ * @hide
+ * Add devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int addDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Remove devices as role for the capture preset
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param devices the devices to be removed
+ * @return {@link #SUCCESS} if successfully removed
+ */
+ public static int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ if (devices.isEmpty()) {
+ return BAD_VALUE;
+ }
+ Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+ return removeDevicesRoleForCapturePreset(
+ capturePreset, role, typeAddresses.first, typeAddresses.second);
+ }
+
+ /**
+ * @hide
+ * Remove devices as role for capture preset.
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param types all device types
+ * @param addresses all device addresses
+ * @return {@link #SUCCESS} if successfully set
+ */
+ private static native int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+ /**
+ * @hide
+ * Remove all devices as role for the capture preset
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @return {@link #SUCCESS} if successfully removed
+ */
+ public static native int clearDevicesRoleForCapturePreset(int capturePreset, int role);
+
+ /**
+ * @hide
+ * Query previously set devices as role for a capture preset
+ * @param capturePreset the capture preset to query for
+ * @param role the role of the devices
+ * @param devices a list that will contain the devices of role
+ * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
+ * and written to the array
+ */
+ public static native int getDevicesForRoleAndCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices);
// Items shared with audio service
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 78806eb..ebaa316 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -26,10 +26,12 @@
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.ICommunicationDeviceDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController;
import android.media.IVolumeController;
import android.media.PlayerBase;
@@ -279,11 +281,11 @@
boolean isCallScreeningModeSupported();
- int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAttributes device);
+ int setPreferredDevicesForStrategy(in int strategy, in List<AudioDeviceAttributes> device);
- int removePreferredDeviceForStrategy(in int strategy);
+ int removePreferredDevicesForStrategy(in int strategy);
- AudioDeviceAttributes getPreferredDeviceForStrategy(in int strategy);
+ List<AudioDeviceAttributes> getPreferredDevicesForStrategy(in int strategy);
List<AudioDeviceAttributes> getDevicesForAttributes(in AudioAttributes attributes);
@@ -291,10 +293,10 @@
int getAllowedCapturePolicy();
- void registerStrategyPreferredDeviceDispatcher(IStrategyPreferredDeviceDispatcher dispatcher);
+ void registerStrategyPreferredDevicesDispatcher(IStrategyPreferredDevicesDispatcher dispatcher);
- oneway void unregisterStrategyPreferredDeviceDispatcher(
- IStrategyPreferredDeviceDispatcher dispatcher);
+ oneway void unregisterStrategyPreferredDevicesDispatcher(
+ IStrategyPreferredDevicesDispatcher dispatcher);
oneway void setRttEnabled(in boolean rttEnabled);
@@ -307,4 +309,25 @@
// code via IAudioManager.h need to be added to the top section.
oneway void setMultiAudioFocusEnabled(in boolean enabled);
+
+ int setPreferredDevicesForCapturePreset(
+ in int capturePreset, in List<AudioDeviceAttributes> devices);
+
+ int clearPreferredDevicesForCapturePreset(in int capturePreset);
+
+ List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(in int capturePreset);
+
+ void registerCapturePresetDevicesRoleDispatcher(ICapturePresetDevicesRoleDispatcher dispatcher);
+
+ oneway void unregisterCapturePresetDevicesRoleDispatcher(
+ ICapturePresetDevicesRoleDispatcher dispatcher);
+
+ boolean setDeviceForCommunication(IBinder cb, int portId);
+
+ int getDeviceForCommunication();
+
+ void registerCommunicationDeviceDispatcher(ICommunicationDeviceDispatcher dispatcher);
+
+ oneway void unregisterCommunicationDeviceDispatcher(
+ ICommunicationDeviceDispatcher dispatcher);
}
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
similarity index 74%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
index b1f99e6..5e03e63 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
@@ -19,12 +19,13 @@
import android.media.AudioDeviceAttributes;
/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal devices role for capture preset updates.
*
* {@hide}
*/
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface ICapturePresetDevicesRoleDispatcher {
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+ void dispatchDevicesRoleChanged(
+ int capturePreset, int role, in List<AudioDeviceAttributes> devices);
}
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/ICommunicationDeviceDispatcher.aidl
similarity index 72%
copy from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
copy to media/java/android/media/ICommunicationDeviceDispatcher.aidl
index b1f99e6..429f934 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/ICommunicationDeviceDispatcher.aidl
@@ -16,15 +16,13 @@
package android.media;
-import android.media.AudioDeviceAttributes;
-
/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal audio communication device updates.
*
* {@hide}
*/
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface ICommunicationDeviceDispatcher {
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+ void dispatchCommunicationDeviceChanged(int portId);
}
diff --git a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
similarity index 82%
rename from media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
rename to media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
index b1f99e6..db674c3 100644
--- a/media/java/android/media/IStrategyPreferredDeviceDispatcher.aidl
+++ b/media/java/android/media/IStrategyPreferredDevicesDispatcher.aidl
@@ -19,12 +19,12 @@
import android.media.AudioDeviceAttributes;
/**
- * AIDL for AudioService to signal audio strategy-preferred device updates.
+ * AIDL for AudioService to signal audio strategy-preferred devices updates.
*
* {@hide}
*/
-oneway interface IStrategyPreferredDeviceDispatcher {
+oneway interface IStrategyPreferredDevicesDispatcher {
- void dispatchPrefDeviceChanged(int strategyId, in AudioDeviceAttributes device);
+ void dispatchPrefDevicesChanged(int strategyId, in List<AudioDeviceAttributes> devices);
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 73ef315..c61a2eb 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -404,6 +404,32 @@
}
}
+ /**
+ * @hide
+ * @param source An audio source to test
+ * @return true if the source is a valid one
+ */
+ public static boolean isValidAudioSource(int source) {
+ switch(source) {
+ case AudioSource.MIC:
+ case AudioSource.VOICE_UPLINK:
+ case AudioSource.VOICE_DOWNLINK:
+ case AudioSource.VOICE_CALL:
+ case AudioSource.CAMCORDER:
+ case AudioSource.VOICE_RECOGNITION:
+ case AudioSource.VOICE_COMMUNICATION:
+ case AudioSource.REMOTE_SUBMIX:
+ case AudioSource.UNPROCESSED:
+ case AudioSource.VOICE_PERFORMANCE:
+ case AudioSource.ECHO_REFERENCE:
+ case AudioSource.RADIO_TUNER:
+ case AudioSource.HOTWORD:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/** @hide */
public static final String toLogFriendlyAudioSource(int source) {
switch(source) {
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index cbc9ab7..cf06fad 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -6,3 +6,4 @@
olly@google.com
andrewlewis@google.com
sungsoo@google.com
+jmtrivi@google.com
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index d55e9d0..0d53ab1 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -333,7 +333,7 @@
if (deviceType != AUDIO_DEVICE_NONE) {
device.mType = (audio_devices_t)deviceType;
ScopedUtfChars address(env, deviceAddress);
- device.mAddress = address.c_str();
+ device.setAddress(address.c_str());
}
// create the native AudioEffect object
diff --git a/native/android/OWNERS b/native/android/OWNERS
index 266764a..ac5a895 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,4 +1,3 @@
-set noparent
-
+per-file libandroid_net.map.txt, net.c = set noparent
per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2e1fbb7..01ef69b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1803,7 +1803,8 @@
private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
@Override
- public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos) {
+ public void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos,
+ int uid) {
int deviceType = Integer.parseInt(label);
sendDataActivityBroadcast(deviceType, active, tsNanos);
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 636da6f..d30adf1 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -396,22 +396,12 @@
* Notify our observers of a change in the data activity state of the interface
*/
private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
- int uid, boolean fromRadio) {
+ int uid) {
final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type);
int powerState = isActive
? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
: DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
if (isMobile) {
- if (!fromRadio) {
- if (mMobileActivityFromRadio) {
- // If this call is not coming from a report from the radio itself, but we
- // have previously received reports from the radio, then we will take the
- // power state to just be whatever the radio last reported.
- powerState = mLastPowerStateFromRadio;
- }
- } else {
- mMobileActivityFromRadio = true;
- }
if (mLastPowerStateFromRadio != powerState) {
mLastPowerStateFromRadio = powerState;
try {
@@ -431,15 +421,9 @@
}
}
- if (!isMobile || fromRadio || !mMobileActivityFromRadio) {
- // Report the change in data activity. We don't do this if this is a change
- // on the mobile network, that is not coming from the radio itself, and we
- // have previously seen change reports from the radio. In that case only
- // the radio is the authority for the current state.
- final boolean active = isActive;
- invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
- Integer.toString(type), active, tsNanos));
- }
+ final boolean active = isActive;
+ invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
+ Integer.toString(type), active, tsNanos, uid));
boolean report = false;
synchronized (mIdleTimerLock) {
@@ -671,7 +655,7 @@
timestampNanos = timestamp;
}
mDaemonHandler.post(() ->
- notifyInterfaceClassActivity(label, isActive, timestampNanos, uid, false));
+ notifyInterfaceClassActivity(label, isActive, timestampNanos, uid));
}
@Override
@@ -1157,7 +1141,7 @@
mNetworkActive = false;
}
mDaemonHandler.post(() -> notifyInterfaceClassActivity(type, true,
- SystemClock.elapsedRealtimeNanos(), -1, false));
+ SystemClock.elapsedRealtimeNanos(), -1));
}
}
@@ -1181,7 +1165,7 @@
}
mActiveIdleTimers.remove(iface);
mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type, false,
- SystemClock.elapsedRealtimeNanos(), -1, false));
+ SystemClock.elapsedRealtimeNanos(), -1));
}
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 5e85409..74e3851 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -57,12 +57,12 @@
*
* <pre>The internal structure of the VCN Management subsystem is as follows:
*
- * +------------------------+ 1:1 +--------------------------------+
- * | VcnManagementService | ------------ Creates -------------> | TelephonySubscriptionManager |
- * | | | |
- * | Manages configs and | | Tracks subscriptions, carrier |
- * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps |
- * +------------------------+ carrier privilege changes +--------------------------------+
+ * +-------------------------+ 1:1 +--------------------------------+
+ * | VcnManagementService | ------------ Creates ------------> | TelephonySubscriptionManager |
+ * | | | |
+ * | Manages configs and | | Tracks subscriptions, carrier |
+ * | Vcn instance lifecycles | <--- Notifies of subscription & -- | privilege changes, caches maps |
+ * +-------------------------+ carrier privilege changes +--------------------------------+
* | 1:N ^
* | |
* | +-------------------------------+
@@ -74,19 +74,19 @@
* | mode state changes
* v |
* +-----------------------------------------------------------------------+
- * | VcnInstance |
+ * | Vcn |
* | |
- * | Manages tunnel lifecycles based on fulfillable NetworkRequest(s) |
- * | and overall safe-mode |
+ * | Manages GatewayConnection lifecycles based on fulfillable |
+ * | NetworkRequest(s) and overall safe-mode |
* +-----------------------------------------------------------------------+
* | 1:N ^
* Creates to fulfill |
- * NetworkRequest(s), tears Notifies of VcnTunnel
+ * NetworkRequest(s), tears Notifies of VcnGatewayConnection
* down when no longer needed teardown (e.g. Network reaped)
* | and safe-mode timer changes
* v |
* +-----------------------------------------------------------------------+
- * | VcnTunnel |
+ * | VcnGatewayConnection |
* | |
* | Manages a single (IKEv2) tunnel session and NetworkAgent, |
* | handles mobility events, (IPsec) Tunnel setup and safe-mode timers |
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 074d3fe..26f5c4c 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -16,6 +16,7 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
@@ -25,19 +26,26 @@
import android.content.Context;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.ICommunicationDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
+import android.media.audiopolicy.AudioProductStrategy;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -45,8 +53,10 @@
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -70,11 +80,8 @@
private final @NonNull Context mContext;
/** Forced device usage for communications sent to AudioSystem */
- private int mForcedUseForComm;
- /**
- * Externally reported force device usage state returned by getters: always consistent
- * with requests by setters */
- private int mForcedUseForCommExt;
+ private AudioDeviceAttributes mPreferredDeviceforComm;
+ private int mCommunicationStrategyId = -1;
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
@@ -130,11 +137,23 @@
init();
}
+ private void initCommunicationStrategyId() {
+ List<AudioProductStrategy> strategies = AudioProductStrategy.getAudioProductStrategies();
+ for (AudioProductStrategy strategy : strategies) {
+ if (strategy.getAudioAttributesForLegacyStreamType(AudioSystem.STREAM_VOICE_CALL)
+ != null) {
+ mCommunicationStrategyId = strategy.getId();
+ return;
+ }
+ }
+ mCommunicationStrategyId = -1;
+ }
+
private void init() {
setupMessaging(mContext);
- mForcedUseForComm = AudioSystem.FORCE_NONE;
- mForcedUseForCommExt = mForcedUseForComm;
+ mPreferredDeviceforComm = null;
+ initCommunicationStrategyId();
}
/*package*/ Context getContext() {
@@ -157,15 +176,6 @@
}
/*package*/ void onAudioServerDied() {
- // Restore forced usage for communications and record
- synchronized (mDeviceStateLock) {
- AudioSystem.setParameters(
- "BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
- onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm,
- false /*fromA2dp*/, "onAudioServerDied");
- onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm,
- false /*fromA2dp*/, "onAudioServerDied");
- }
// restore devices
sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
@@ -217,85 +227,210 @@
* Turns speakerphone on/off
* @param on
* @param eventSource for logging purposes
- * @return true if speakerphone state changed
*/
- /*package*/ boolean setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
- synchronized (mDeviceStateLock) {
- if (!addSpeakerphoneClient(cb, pid, on)) {
- return false;
+ /*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
+ }
+
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = null;
+ if (on) {
+ device = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
+ } else {
+ CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+ if (client == null || !client.requestsSpeakerphone()) {
+ return;
+ }
+ }
+ setCommunicationRouteForClient(
+ cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
}
- if (on) {
- // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
- // are mutually exclusive.
- // See symmetrical operation for startBluetoothScoForClient_Sync().
- mBtHelper.stopBluetoothScoForPid(pid);
- }
- final boolean wasOn = isSpeakerphoneOn();
- updateSpeakerphoneOn(eventSource);
- return (wasOn != isSpeakerphoneOn());
}
}
/**
- * Turns speakerphone off for a given pid and update speakerphone state.
- * @param pid
+ * Select device for use for communication use cases.
+ * @param cb Client binder for death detection
+ * @param pid Client pid
+ * @param device Device selected or null to unselect.
+ * @param eventSource for logging purposes
*/
+ /*package*/ boolean setDeviceForCommunication(
+ IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setDeviceForCommunication, device: " + device + ", pid: " + pid);
+ }
+
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes deviceAttr = null;
+ if (device != null) {
+ deviceAttr = new AudioDeviceAttributes(device);
+ } else {
+ CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+ if (client == null) {
+ return false;
+ }
+ }
+ setCommunicationRouteForClient(
+ cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ }
+ }
+ return true;
+ }
+
@GuardedBy("mDeviceStateLock")
- private void setSpeakerphoneOffForPid(int pid) {
- SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
+ /*package*/ void setCommunicationRouteForClient(
+ IBinder cb, int pid, AudioDeviceAttributes device,
+ int scoAudioMode, String eventSource) {
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
+ }
+ AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "setCommunicationRouteForClient for pid: " + pid
+ + " device: " + device
+ + " from API: " + eventSource)).printLog(TAG));
+
+ final boolean wasBtScoRequested = isBluetoothScoRequested();
+ final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
+ CommunicationRouteClient client;
+
+
+ // Save previous client route in case of failure to start BT SCO audio
+ AudioDeviceAttributes prevClientDevice = null;
+ client = getCommunicationRouteClientForPid(pid);
+ if (client != null) {
+ prevClientDevice = client.getDevice();
+ }
+
+ if (device != null) {
+ client = addCommunicationRouteClient(cb, pid, device);
+ if (client == null) {
+ Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
+ + pid + " and device: " + device);
+ }
+ } else {
+ client = removeCommunicationRouteClient(cb, true);
+ }
if (client == null) {
return;
}
- client.unregisterDeathRecipient();
- mSpeakerphoneClients.remove(client);
- final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
- .append(pid).append(")").toString();
- updateSpeakerphoneOn(eventSource);
- }
- @GuardedBy("mDeviceStateLock")
- private void updateSpeakerphoneOn(String eventSource) {
- if (isSpeakerphoneOnRequested()) {
- if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
- setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+ boolean isBtScoRequested = isBluetoothScoRequested();
+ if (isBtScoRequested && !wasBtScoRequested) {
+ if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
+ Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+ + pid);
+ // clean up or restore previous client selection
+ if (prevClientDevice != null) {
+ addCommunicationRouteClient(cb, pid, prevClientDevice);
+ } else {
+ removeCommunicationRouteClient(cb, true);
+ }
+ postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
- mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
- } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
- if (mBtHelper.isBluetoothScoOn()) {
- mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
- setForceUse_Async(
- AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
- } else {
- mForcedUseForComm = AudioSystem.FORCE_NONE;
+ } else if (!isBtScoRequested && wasBtScoRequested) {
+ mBtHelper.stopBluetoothSco(eventSource);
+ }
+
+ if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
+ try {
+ mContext.sendBroadcastAsUser(
+ new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+ } catch (Exception e) {
+ Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
}
}
- mForcedUseForCommExt = mForcedUseForComm;
- setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
+
+ sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
}
/**
- * Returns if speakerphone is requested ON or OFF.
- * If the current audio mode owner is in the speakerphone client list, use this preference.
+ * Returns the device currently requested for communication use case.
+ * If the current audio mode owner is in the communication route client list,
+ * use this preference.
* Otherwise use first client's preference (first client corresponds to latest request).
- * Speakerphone is requested OFF if no client is in the list.
- * @return true if speakerphone is requested ON, false otherwise
+ * null is returned if no client is in the list.
+ * @return AudioDeviceAttributes the requested device for communication.
*/
+
@GuardedBy("mDeviceStateLock")
- private boolean isSpeakerphoneOnRequested() {
- if (mSpeakerphoneClients.isEmpty()) {
- return false;
- }
- for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+ private AudioDeviceAttributes requestedCommunicationDevice() {
+ AudioDeviceAttributes device = null;
+ for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getPid() == mModeOwnerPid) {
- return cl.isOn();
+ device = cl.getDevice();
}
}
- return mSpeakerphoneClients.get(0).isOn();
+ if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
+ device = mCommunicationRouteClients.get(0).getDevice();
+ }
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "requestedCommunicationDevice, device: "
+ + device + " mode owner pid: " + mModeOwnerPid);
+ }
+ return device;
}
- /*package*/ boolean isSpeakerphoneOn() {
+ /**
+ * Returns the device currently requested for communication use case.
+ * @return AudioDeviceInfo the requested device for communication.
+ */
+ AudioDeviceInfo getDeviceForCommunication() {
synchronized (mDeviceStateLock) {
- return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ if (device == null) {
+ return null;
+ }
+ return AudioManager.getDeviceInfoFromType(device.getType());
+ }
+ }
+
+ /**
+ * Helper method on top of requestedCommunicationDevice() indicating if
+ * speakerphone ON is currently requested or not.
+ * @return true if speakerphone ON requested, false otherwise.
+ */
+
+ private boolean isSpeakerphoneRequested() {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ return device != null
+ && device.getType()
+ == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+ }
+ }
+
+ /**
+ * Indicates if active route selection for communication is speakerphone.
+ * @return true if speakerphone is active, false otherwise.
+ */
+ /*package*/ boolean isSpeakerphoneOn() {
+ AudioDeviceAttributes device = getPreferredDeviceForComm();
+ if (device == null) {
+ return false;
+ }
+ return device.getInternalType() == AudioSystem.DEVICE_OUT_SPEAKER;
+ }
+
+ /**
+ * Helper method on top of requestedCommunicationDevice() indicating if
+ * Bluetooth SCO ON is currently requested or not.
+ * @return true if Bluetooth SCO ON is requested, false otherwise.
+ */
+ /*package*/ boolean isBluetoothScoRequested() {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ return device != null
+ && device.getType()
+ == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
}
}
@@ -346,7 +481,6 @@
}
}
-
/*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
@@ -429,42 +563,32 @@
sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
}
- // never called by system components
- /*package*/ void setBluetoothScoOnByApp(boolean on) {
- synchronized (mDeviceStateLock) {
- mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
- }
- }
- /*package*/ boolean isBluetoothScoOnForApp() {
- synchronized (mDeviceStateLock) {
- return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
- }
- }
+ /**
+ * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
+ */
+ private boolean mBluetoothScoOn;
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
- //Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
- synchronized (mDeviceStateLock) {
- if (on) {
- // do not accept SCO ON if SCO audio is not connected
- if (!mBtHelper.isBluetoothScoOn()) {
- mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
- return;
- }
- mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
- } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
- mForcedUseForComm = isSpeakerphoneOnRequested()
- ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
- }
- mForcedUseForCommExt = mForcedUseForComm;
- AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
}
- // Un-mute ringtone stream volume
- mAudioService.postUpdateRingerModeServiceInt();
+ synchronized (mDeviceStateLock) {
+ mBluetoothScoOn = on;
+ sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
+ }
+ }
+
+ /**
+ * Indicates if active route selection for communication is Bluetooth SCO.
+ * @return true if Bluetooth SCO is active , false otherwise.
+ */
+ /*package*/ boolean isBluetoothScoOn() {
+ AudioDeviceAttributes device = getPreferredDeviceForComm();
+ if (device == null) {
+ return false;
+ }
+ return AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType());
}
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
@@ -507,42 +631,125 @@
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
}
- @GuardedBy("mSetModeLock")
- /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
+ /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
@NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
- // are mutually exclusive.
- // See symmetrical operation for setSpeakerphoneOn(true).
- setSpeakerphoneOffForPid(Binder.getCallingPid());
- mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
+ }
+
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ AudioDeviceAttributes device =
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
+ setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
+ }
}
}
- @GuardedBy("mSetModeLock")
- /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ /*package*/ void stopBluetoothScoForClient(
+ IBinder cb, int pid, @NonNull String eventSource) {
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
+ }
+
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+ if (client == null || !client.requestsBluetoothSco()) {
+ return;
+ }
+ setCommunicationRouteForClient(
+ cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ }
}
}
- /*package*/ int setPreferredDeviceForStrategySync(int strategy,
- @NonNull AudioDeviceAttributes device) {
- return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device);
+ /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return mDeviceInventory.setPreferredDevicesForStrategySync(strategy, devices);
}
- /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
- return mDeviceInventory.removePreferredDeviceForStrategySync(strategy);
+ /*package*/ void postSetPreferredDevicesForStrategy(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ sendILMsgNoDelay(MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy, devices);
}
- /*package*/ void registerStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
- mDeviceInventory.registerStrategyPreferredDeviceDispatcher(dispatcher);
+ /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
+ return mDeviceInventory.removePreferredDevicesForStrategySync(strategy);
}
- /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
- mDeviceInventory.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+ /*package*/ void postRemovePreferredDevicesForStrategy(int strategy) {
+ sendIMsgNoDelay(MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_REPLACE, strategy);
+ }
+
+ /*package*/ void registerStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+ mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
+ }
+
+ /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
+ mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
+ }
+
+ /*package*/ int setPreferredDevicesForCapturePresetSync(int capturePreset,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return mDeviceInventory.setPreferredDevicesForCapturePresetSync(capturePreset, devices);
+ }
+
+ /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+ return mDeviceInventory.clearPreferredDevicesForCapturePresetSync(capturePreset);
+ }
+
+ /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+ }
+
+ /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
+ }
+
+ /*package*/ void registerCommunicationDeviceDispatcher(
+ @NonNull ICommunicationDeviceDispatcher dispatcher) {
+ mCommDevDispatchers.register(dispatcher);
+ }
+
+ /*package*/ void unregisterCommunicationDeviceDispatcher(
+ @NonNull ICommunicationDeviceDispatcher dispatcher) {
+ mCommDevDispatchers.unregister(dispatcher);
+ }
+
+ // Monitoring of communication device
+ final RemoteCallbackList<ICommunicationDeviceDispatcher> mCommDevDispatchers =
+ new RemoteCallbackList<ICommunicationDeviceDispatcher>();
+
+ // portId of the device currently selected for communication: avoids broadcasting changes
+ // when same communication route is applied
+ @GuardedBy("mDeviceStateLock")
+ int mCurCommunicationPortId = -1;
+
+ @GuardedBy("mDeviceStateLock")
+ private void dispatchCommunicationDevice() {
+ AudioDeviceInfo device = getDeviceForCommunication();
+ int portId = (getDeviceForCommunication() == null) ? 0 : device.getId();
+ if (portId == mCurCommunicationPortId) {
+ return;
+ }
+ mCurCommunicationPortId = portId;
+
+ final int nbDispatchers = mCommDevDispatchers.beginBroadcast();
+ for (int i = 0; i < nbDispatchers; i++) {
+ try {
+ mCommDevDispatchers.getBroadcastItem(i)
+ .dispatchCommunicationDeviceChanged(portId);
+ } catch (RemoteException e) {
+ }
+ }
+ mCommDevDispatchers.finishBroadcast();
}
//---------------------------------------------------------------------
@@ -675,22 +882,29 @@
hearingAidProfile);
}
- /*package*/ void postScoClientDied(Object obj) {
- sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
+ /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
+ sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
}
- /*package*/ void postSpeakerphoneClientDied(Object obj) {
- sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
- }
-
- /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy,
- AudioDeviceAttributes device)
+ /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
+ List<AudioDeviceAttributes> devices)
{
- sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
+ sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy, devices);
}
- /*package*/ void postSaveRemovePreferredDeviceForStrategy(int strategy) {
- sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
+ /*package*/ void postSaveRemovePreferredDevicesForStrategy(int strategy) {
+ sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
+ }
+
+ /*package*/ void postSaveSetPreferredDevicesForCapturePreset(
+ int capturePreset, List<AudioDeviceAttributes> devices) {
+ sendILMsgNoDelay(
+ MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset, devices);
+ }
+
+ /*package*/ void postSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+ sendIMsgNoDelay(
+ MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
}
//---------------------------------------------------------------------
@@ -789,15 +1003,17 @@
mDeviceInventory.dump(pw, prefix);
- pw.println("\n" + prefix + "mForcedUseForComm: "
- + AudioSystem.forceUseConfigToString(mForcedUseForComm));
- pw.println(prefix + "mForcedUseForCommExt: "
- + AudioSystem.forceUseConfigToString(mForcedUseForCommExt));
- pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid);
- pw.println(prefix + "Speakerphone clients:");
- mSpeakerphoneClients.forEach((cl) -> {
- pw.println(" " + prefix + "pid: " + cl.getPid() + " on: "
- + cl.isOn() + " cb: " + cl.getBinder()); });
+ pw.println("\n" + prefix + "Communication route clients:");
+ mCommunicationRouteClients.forEach((cl) -> {
+ pw.println(" " + prefix + "pid: " + cl.getPid() + " device: "
+ + cl.getDevice() + " cb: " + cl.getBinder()); });
+
+ pw.println("\n" + prefix + "mPreferredDeviceforComm: "
+ + mPreferredDeviceforComm);
+ pw.println(prefix + "mCommunicationStrategyId: "
+ + mCommunicationStrategyId);
+
+ pw.println("\n" + prefix + "mModeOwnerPid: " + mModeOwnerPid);
mBtHelper.dump(pw, prefix);
}
@@ -820,6 +1036,11 @@
.set(MediaMetrics.Property.FORCE_USE_MODE,
AudioSystem.forceUseConfigToString(config))
.record();
+
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onSetForceUse(useCase<" + useCase + ">, config<" + config + ">, fromA2dp<"
+ + fromA2dp + ">, eventSource<" + eventSource + ">)");
+ }
AudioSystem.setForceUse(useCase, config);
}
@@ -885,9 +1106,13 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_DEVICES:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onRestoreDevices();
- mBtHelper.onAudioServerDiedRestoreA2dp();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ initCommunicationStrategyId();
+ mDeviceInventory.onRestoreDevices();
+ mBtHelper.onAudioServerDiedRestoreA2dp();
+ onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
+ }
}
break;
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -977,25 +1202,24 @@
if (mModeOwnerPid != msg.arg1) {
mModeOwnerPid = msg.arg1;
if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
- updateSpeakerphoneOn("setNewModeOwner");
- }
- if (mModeOwnerPid != 0) {
- mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
+ onUpdateCommunicationRoute("setNewModeOwner");
}
}
}
}
break;
- case MSG_L_SCOCLIENT_DIED:
+ case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- mBtHelper.scoClientDied(msg.obj);
+ onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
}
}
break;
- case MSG_L_SPEAKERPHONE_CLIENT_DIED:
- synchronized (mDeviceStateLock) {
- speakerphoneClientDied(msg.obj);
+ case MSG_L_UPDATE_COMMUNICATION_ROUTE:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ onUpdateCommunicationRoute((String) msg.obj);
+ }
}
break;
case MSG_TOGGLE_HDMI:
@@ -1084,18 +1308,41 @@
info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
}
} break;
- case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: {
+ case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
final int strategy = msg.arg1;
- final AudioDeviceAttributes device = (AudioDeviceAttributes) msg.obj;
- mDeviceInventory.onSaveSetPreferredDevice(strategy, device);
+ final List<AudioDeviceAttributes> devices =
+ (List<AudioDeviceAttributes>) msg.obj;
+ mDeviceInventory.onSaveSetPreferredDevices(strategy, devices);
} break;
- case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: {
+ case MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
final int strategy = msg.arg1;
- mDeviceInventory.onSaveRemovePreferredDevice(strategy);
+ mDeviceInventory.onSaveRemovePreferredDevices(strategy);
+ } break;
+ case MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY: {
+ final int strategy = msg.arg1;
+ final List<AudioDeviceAttributes> devices =
+ (List<AudioDeviceAttributes>) msg.obj;
+ setPreferredDevicesForStrategySync(strategy, devices);
+
+ } break;
+ case MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY: {
+ final int strategy = msg.arg1;
+ removePreferredDevicesForStrategySync(strategy);
} break;
case MSG_CHECK_MUTE_MUSIC:
checkMessagesMuteMusic(0);
break;
+ case MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+ final int capturePreset = msg.arg1;
+ final List<AudioDeviceAttributes> devices =
+ (List<AudioDeviceAttributes>) msg.obj;
+ mDeviceInventory.onSaveSetPreferredDevicesForCapturePreset(
+ capturePreset, devices);
+ } break;
+ case MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+ final int capturePreset = msg.arg1;
+ mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
+ } break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1163,15 +1410,19 @@
// process external command to (dis)connect a hearing aid device
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
- // a ScoClient died in BtHelper
- private static final int MSG_L_SCOCLIENT_DIED = 32;
- private static final int MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY = 33;
- private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34;
+ private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32;
+ private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33;
- private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
- private static final int MSG_CHECK_MUTE_MUSIC = 36;
- private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
+ private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34;
+ private static final int MSG_CHECK_MUTE_MUSIC = 35;
+ private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
+ private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
+ private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
+
+ private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
+ private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
+ private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41;
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
@@ -1325,14 +1576,20 @@
}
}
- private class SpeakerphoneClient implements IBinder.DeathRecipient {
+ // List of applications requesting a specific route for communication.
+ @GuardedBy("mDeviceStateLock")
+ private final @NonNull LinkedList<CommunicationRouteClient> mCommunicationRouteClients =
+ new LinkedList<CommunicationRouteClient>();
+
+ private class CommunicationRouteClient implements IBinder.DeathRecipient {
private final IBinder mCb;
private final int mPid;
- private final boolean mOn;
- SpeakerphoneClient(IBinder cb, int pid, boolean on) {
+ private AudioDeviceAttributes mDevice;
+
+ CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) {
mCb = cb;
mPid = pid;
- mOn = on;
+ mDevice = device;
}
public boolean registerDeathRecipient() {
@@ -1341,7 +1598,7 @@
mCb.linkToDeath(this, 0);
status = true;
} catch (RemoteException e) {
- Log.w(TAG, "SpeakerphoneClient could not link to " + mCb + " binder death");
+ Log.w(TAG, "CommunicationRouteClient could not link to " + mCb + " binder death");
}
return status;
}
@@ -1350,13 +1607,13 @@
try {
mCb.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
- Log.w(TAG, "SpeakerphoneClient could not not unregistered to binder");
+ Log.w(TAG, "CommunicationRouteClient could not not unregistered to binder");
}
}
@Override
public void binderDied() {
- postSpeakerphoneClientDied(this);
+ postCommunicationRouteClientDied(this);
}
IBinder getBinder() {
@@ -1367,29 +1624,103 @@
return mPid;
}
- boolean isOn() {
- return mOn;
+ AudioDeviceAttributes getDevice() {
+ return mDevice;
+ }
+
+ boolean requestsBluetoothSco() {
+ return mDevice != null
+ && mDevice.getType()
+ == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
+ }
+
+ boolean requestsSpeakerphone() {
+ return mDevice != null
+ && mDevice.getType()
+ == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
}
}
+ // @GuardedBy("mSetModeLock")
@GuardedBy("mDeviceStateLock")
- private void speakerphoneClientDied(Object obj) {
- if (obj == null) {
+ private void onCommunicationRouteClientDied(CommunicationRouteClient client) {
+ if (client == null) {
return;
}
Log.w(TAG, "Speaker client died");
- if (removeSpeakerphoneClient(((SpeakerphoneClient) obj).getBinder(), false) != null) {
- updateSpeakerphoneOn("speakerphoneClientDied");
+ if (removeCommunicationRouteClient(client.getBinder(), false)
+ != null) {
+ onUpdateCommunicationRoute("onCommunicationRouteClientDied");
}
}
- private SpeakerphoneClient removeSpeakerphoneClient(IBinder cb, boolean unregister) {
- for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+ /**
+ * Determines which forced usage for communication should be sent to audio policy manager
+ * as a function of current SCO audio activation state and active communication route requests.
+ * SCO audio state has the highest priority as it can result from external activation by
+ * telephony service.
+ * @return selected forced usage for communication.
+ */
+ @GuardedBy("mDeviceStateLock")
+ @Nullable private AudioDeviceAttributes getPreferredDeviceForComm() {
+ boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
+ if (btSCoOn) {
+ // Use the SCO device known to BtHelper so that it matches exactly
+ // what has been communicated to audio policy manager. The device
+ // returned by requestedCommunicationDevice() can be a dummy SCO device if legacy
+ // APIs are used to start SCO audio.
+ AudioDeviceAttributes device = mBtHelper.getHeadsetAudioDevice();
+ if (device != null) {
+ return device;
+ }
+ }
+ AudioDeviceAttributes device = requestedCommunicationDevice();
+ if (device == null
+ || AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(device.getInternalType())) {
+ // Do not indicate BT SCO selection if SCO is requested but SCO is not ON
+ return null;
+ }
+ return device;
+ }
+
+ /**
+ * Configures audio policy manager and audio HAL according to active communication route.
+ * Always called from message Handler.
+ */
+ // @GuardedBy("mSetModeLock")
+ @GuardedBy("mDeviceStateLock")
+ private void onUpdateCommunicationRoute(String eventSource) {
+ mPreferredDeviceforComm = getPreferredDeviceForComm();
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onUpdateCommunicationRoute, mPreferredDeviceforComm: "
+ + mPreferredDeviceforComm + " eventSource: " + eventSource);
+ }
+
+ if (mPreferredDeviceforComm == null
+ || !AudioSystem.DEVICE_OUT_ALL_SCO_SET.contains(
+ mPreferredDeviceforComm.getInternalType())) {
+ AudioSystem.setParameters("BT_SCO=off");
+ } else {
+ AudioSystem.setParameters("BT_SCO=on");
+ }
+ if (mPreferredDeviceforComm == null) {
+ postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
+ } else {
+ postSetPreferredDevicesForStrategy(
+ mCommunicationStrategyId, Arrays.asList(mPreferredDeviceforComm));
+ }
+ mAudioService.postUpdateRingerModeServiceInt();
+ dispatchCommunicationDevice();
+ }
+
+ private CommunicationRouteClient removeCommunicationRouteClient(
+ IBinder cb, boolean unregister) {
+ for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getBinder() == cb) {
if (unregister) {
cl.unregisterDeathRecipient();
}
- mSpeakerphoneClients.remove(cl);
+ mCommunicationRouteClients.remove(cl);
return cl;
}
}
@@ -1397,30 +1728,25 @@
}
@GuardedBy("mDeviceStateLock")
- private boolean addSpeakerphoneClient(IBinder cb, int pid, boolean on) {
+ private CommunicationRouteClient addCommunicationRouteClient(
+ IBinder cb, int pid, AudioDeviceAttributes device) {
// always insert new request at first position
- removeSpeakerphoneClient(cb, true);
- SpeakerphoneClient client = new SpeakerphoneClient(cb, pid, on);
+ removeCommunicationRouteClient(cb, true);
+ CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
if (client.registerDeathRecipient()) {
- mSpeakerphoneClients.add(0, client);
- return true;
+ mCommunicationRouteClients.add(0, client);
+ return client;
}
- return false;
+ return null;
}
@GuardedBy("mDeviceStateLock")
- private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
- for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+ private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
+ for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getPid() == pid) {
return cl;
}
}
return null;
}
-
- // List of clients requesting speakerPhone ON
- @GuardedBy("mDeviceStateLock")
- private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
- new ArrayList<SpeakerphoneClient>();
-
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 02a846e..82586b8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -16,7 +16,6 @@
package com.android.server.audio;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
@@ -32,7 +31,8 @@
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.IAudioRoutesObserver;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
import android.os.Binder;
import android.os.RemoteCallbackList;
@@ -51,6 +51,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Set;
/**
@@ -137,7 +138,12 @@
private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
// List of preferred devices for strategies
- private final ArrayMap<Integer, AudioDeviceAttributes> mPreferredDevices = new ArrayMap<>();
+ private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
+ new ArrayMap<>();
+
+ // List of preferred devices of capture preset
+ private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset =
+ new ArrayMap<>();
// the wrapper for AudioSystem static methods, allows us to spy AudioSystem
private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -150,8 +156,12 @@
new RemoteCallbackList<IAudioRoutesObserver>();
// Monitoring of strategy-preferred device
- final RemoteCallbackList<IStrategyPreferredDeviceDispatcher> mPrefDevDispatchers =
- new RemoteCallbackList<IStrategyPreferredDeviceDispatcher>();
+ final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
+ new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
+
+ // Monitoring of devices for role and capture preset
+ final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
+ new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker;
@@ -242,6 +252,9 @@
pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
+ " (" + AudioSystem.getDeviceName(keyType)
+ ") addr:" + valueAddress); });
+ mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
+ pw.println(" " + prefix + "capturePreset:" + capturePreset
+ + " devices:" + devices); });
}
//------------------------------------------------------------
@@ -265,8 +278,12 @@
}
}
synchronized (mPreferredDevices) {
- mPreferredDevices.forEach((strategy, device) -> {
- mAudioSystem.setPreferredDeviceForStrategy(strategy, device); });
+ mPreferredDevices.forEach((strategy, devices) -> {
+ mAudioSystem.setDevicesRoleForStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
+ }
+ synchronized (mPreferredDevicesForCapturePreset) {
+ // TODO: call audiosystem to restore
}
}
@@ -600,52 +617,108 @@
mmi.record();
}
- /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) {
- mPreferredDevices.put(strategy, device);
- dispatchPreferredDevice(strategy, device);
+ /*package*/ void onSaveSetPreferredDevices(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ mPreferredDevices.put(strategy, devices);
+ dispatchPreferredDevice(strategy, devices);
}
- /*package*/ void onSaveRemovePreferredDevice(int strategy) {
+ /*package*/ void onSaveRemovePreferredDevices(int strategy) {
mPreferredDevices.remove(strategy);
- dispatchPreferredDevice(strategy, null);
+ dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
+ }
+
+ /*package*/ void onSaveSetPreferredDevicesForCapturePreset(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ mPreferredDevicesForCapturePreset.put(capturePreset, devices);
+ dispatchDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ }
+
+ /*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+ mPreferredDevicesForCapturePreset.remove(capturePreset);
+ dispatchDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED,
+ new ArrayList<AudioDeviceAttributes>());
}
//------------------------------------------------------------
//
- /*package*/ int setPreferredDeviceForStrategySync(int strategy,
- @NonNull AudioDeviceAttributes device) {
+ /*package*/ int setPreferredDevicesForStrategySync(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
final long identity = Binder.clearCallingIdentity();
- final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device);
+
+ AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "setPreferredDevicesForStrategySync, strategy: " + strategy
+ + " devices: " + devices)).printLog(TAG));
+ final int status = mAudioSystem.setDevicesRoleForStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
- mDeviceBroker.postSaveSetPreferredDeviceForStrategy(strategy, device);
+ mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices);
}
return status;
}
- /*package*/ int removePreferredDeviceForStrategySync(int strategy) {
+ /*package*/ int removePreferredDevicesForStrategySync(int strategy) {
final long identity = Binder.clearCallingIdentity();
- final int status = mAudioSystem.removePreferredDeviceForStrategy(strategy);
+ final int status = mAudioSystem.removeDevicesRoleForStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED);
Binder.restoreCallingIdentity(identity);
if (status == AudioSystem.SUCCESS) {
- mDeviceBroker.postSaveRemovePreferredDeviceForStrategy(strategy);
+ mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
}
return status;
}
- /*package*/ void registerStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+ /*package*/ void registerStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
mPrefDevDispatchers.register(dispatcher);
}
- /*package*/ void unregisterStrategyPreferredDeviceDispatcher(
- @NonNull IStrategyPreferredDeviceDispatcher dispatcher) {
+ /*package*/ void unregisterStrategyPreferredDevicesDispatcher(
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
mPrefDevDispatchers.unregister(dispatcher);
}
+ /*package*/ int setPreferredDevicesForCapturePresetSync(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ final long identity = Binder.clearCallingIdentity();
+ final int status = mAudioSystem.setDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ Binder.restoreCallingIdentity(identity);
+
+ if (status == AudioSystem.SUCCESS) {
+ mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
+ }
+ return status;
+ }
+
+ /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+ final long identity = Binder.clearCallingIdentity();
+ final int status = mAudioSystem.clearDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
+ Binder.restoreCallingIdentity(identity);
+
+ if (status == AudioSystem.SUCCESS) {
+ mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
+ }
+ return status;
+ }
+
+ /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDevRoleCapturePresetDispatchers.register(dispatcher);
+ }
+
+ /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+ mDevRoleCapturePresetDispatchers.unregister(dispatcher);
+ }
+
/**
* Implements the communication with AudioSystem to (dis)connect a device in the native layers
* @param connect true if connection
@@ -1288,17 +1361,32 @@
}
}
- private void dispatchPreferredDevice(int strategy, @Nullable AudioDeviceAttributes device) {
+ private void dispatchPreferredDevice(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; i++) {
try {
- mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDeviceChanged(strategy, device);
+ mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
+ strategy, devices);
} catch (RemoteException e) {
}
}
mPrefDevDispatchers.finishBroadcast();
}
+ private void dispatchDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
+ for (int i = 0; i < nbDispatchers; ++i) {
+ try {
+ mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
+ capturePreset, role, devices);
+ } catch (RemoteException e) {
+ }
+ }
+ mDevRoleCapturePresetDispatchers.finishBroadcast();
+ }
+
//----------------------------------------------------------
// 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 a06f9dd..024dca7 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -84,10 +84,12 @@
import android.media.IAudioRoutesObserver;
import android.media.IAudioServerStateDispatcher;
import android.media.IAudioService;
+import android.media.ICapturePresetDevicesRoleDispatcher;
+import android.media.ICommunicationDeviceDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
-import android.media.IStrategyPreferredDeviceDispatcher;
+import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController;
import android.media.MediaExtractor;
import android.media.MediaFormat;
@@ -171,6 +173,7 @@
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
/**
* The implementation of the audio service for volume, audio focus, device management...
@@ -205,6 +208,9 @@
/** debug calls to devices APIs */
protected static final boolean DEBUG_DEVICES = false;
+ /** Debug communication route */
+ protected static final boolean DEBUG_COMM_RTE = false;
+
/** How long to delay before persisting a change in volume/ringer mode. */
private static final int PERSIST_DELAY = 500;
@@ -1850,22 +1856,28 @@
///////////////////////////////////////////////////////////////////////////
// IPC methods
///////////////////////////////////////////////////////////////////////////
- /** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */
- public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAttributes device) {
- if (device == null) {
+ /**
+ * @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
+ * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
+ * List<AudioDeviceAttributes>)
+ */
+ public int setPreferredDevicesForStrategy(int strategy, List<AudioDeviceAttributes> devices) {
+ if (devices == null) {
return AudioSystem.ERROR;
}
enforceModifyAudioRoutingPermission();
final String logString = String.format(
"setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
- Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
+ Binder.getCallingUid(), Binder.getCallingPid(), strategy,
+ devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
- if (device.getRole() == AudioDeviceAttributes.ROLE_INPUT) {
+ if (devices.stream().anyMatch(device ->
+ device.getRole() == AudioDeviceAttributes.ROLE_INPUT)) {
Log.e(TAG, "Unsupported input routing in " + logString);
return AudioSystem.ERROR;
}
- final int status = mDeviceBroker.setPreferredDeviceForStrategySync(strategy, device);
+ final int status = mDeviceBroker.setPreferredDevicesForStrategySync(strategy, devices);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in %s)", status, logString));
}
@@ -1874,53 +1886,149 @@
}
/** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */
- public int removePreferredDeviceForStrategy(int strategy) {
+ public int removePreferredDevicesForStrategy(int strategy) {
enforceModifyAudioRoutingPermission();
final String logString =
String.format("removePreferredDeviceForStrategy strat:%d", strategy);
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
- final int status = mDeviceBroker.removePreferredDeviceForStrategySync(strategy);
+ final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in %s)", status, logString));
}
return status;
}
- /** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */
- public AudioDeviceAttributes getPreferredDeviceForStrategy(int strategy) {
+ /**
+ * @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy)
+ * @see AudioManager#getPreferredDevicesForStrategy(AudioProductStrategy)
+ */
+ public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(int strategy) {
enforceModifyAudioRoutingPermission();
- AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
+ List<AudioDeviceAttributes> devices = new ArrayList<>();
final long identity = Binder.clearCallingIdentity();
- final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices);
+ final int status = AudioSystem.getDevicesForRoleAndStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
Binder.restoreCallingIdentity(identity);
if (status != AudioSystem.SUCCESS) {
Log.e(TAG, String.format("Error %d in getPreferredDeviceForStrategy(%d)",
status, strategy));
- return null;
+ return new ArrayList<AudioDeviceAttributes>();
} else {
- return devices[0];
+ return devices;
}
}
- /** @see AudioManager#addOnPreferredDeviceForStrategyChangedListener(Executor, AudioManager.OnPreferredDeviceForStrategyChangedListener) */
- public void registerStrategyPreferredDeviceDispatcher(
- @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+ /** @see AudioManager#addOnPreferredDevicesForStrategyChangedListener(
+ * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)
+ */
+ public void registerStrategyPreferredDevicesDispatcher(
+ @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerStrategyPreferredDeviceDispatcher(dispatcher);
+ mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
}
- /** @see AudioManager#removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener) */
- public void unregisterStrategyPreferredDeviceDispatcher(
- @Nullable IStrategyPreferredDeviceDispatcher dispatcher) {
+ /** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
+ * AudioManager.OnPreferredDevicesForStrategyChangedListener)
+ */
+ public void unregisterStrategyPreferredDevicesDispatcher(
+ @Nullable IStrategyPreferredDevicesDispatcher dispatcher) {
if (dispatcher == null) {
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.unregisterStrategyPreferredDeviceDispatcher(dispatcher);
+ mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
+ }
+
+ /**
+ * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+ */
+ public int setPreferredDevicesForCapturePreset(
+ int capturePreset, List<AudioDeviceAttributes> devices) {
+ if (devices == null) {
+ return AudioSystem.ERROR;
+ }
+ enforceModifyAudioRoutingPermission();
+ final String logString = String.format(
+ "setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s",
+ Binder.getCallingUid(), Binder.getCallingPid(), capturePreset,
+ devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
+ sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+ if (devices.stream().anyMatch(device ->
+ device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) {
+ Log.e(TAG, "Unsupported output routing in " + logString);
+ return AudioSystem.ERROR;
+ }
+
+ final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
+ capturePreset, devices);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in %s)", status, logString));
+ }
+
+ return status;
+ }
+
+ /** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */
+ public int clearPreferredDevicesForCapturePreset(int capturePreset) {
+ enforceModifyAudioRoutingPermission();
+ final String logString = String.format(
+ "removePreferredDeviceForCapturePreset source:%d", capturePreset);
+ sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+
+ final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in %s", status, logString));
+ }
+ return status;
+ }
+
+ /**
+ * @see AudioManager#getPreferredDevicesForCapturePreset(int)
+ */
+ public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+ enforceModifyAudioRoutingPermission();
+ List<AudioDeviceAttributes> devices = new ArrayList<>();
+ final long identity = Binder.clearCallingIdentity();
+ final int status = AudioSystem.getDevicesForRoleAndCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ Binder.restoreCallingIdentity(identity);
+ if (status != AudioSystem.SUCCESS) {
+ Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)",
+ status, capturePreset));
+ return new ArrayList<AudioDeviceAttributes>();
+ } else {
+ return devices;
+ }
+ }
+
+ /**
+ * @see AudioManager#addOnPreferredDevicesForCapturePresetChangedListener(
+ * Executor, OnPreferredDevicesForCapturePresetChangedListener)
+ */
+ public void registerCapturePresetDevicesRoleDispatcher(
+ @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+ if (dispatcher == null) {
+ return;
+ }
+ enforceModifyAudioRoutingPermission();
+ mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+ }
+
+ /**
+ * @see AudioManager#removeOnPreferredDevicesForCapturePresetChangedListener(
+ * AudioManager.OnPreferredDevicesForCapturePresetChangedListener)
+ */
+ public void unregisterCapturePresetDevicesRoleDispatcher(
+ @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+ if (dispatcher == null) {
+ return;
+ }
+ enforceModifyAudioRoutingPermission();
+ mDeviceBroker.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
}
/** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
@@ -3585,7 +3693,7 @@
final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
|| ringerMode == AudioManager.RINGER_MODE_SILENT;
final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
- && isBluetoothScoOn();
+ && mDeviceBroker.isBluetoothScoOn();
// Ask audio policy engine to force use Bluetooth SCO channel if needed
final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
+ "/" + Binder.getCallingPid();
@@ -4161,6 +4269,115 @@
restoreDeviceVolumeBehavior();
}
+ private static final int[] VALID_COMMUNICATION_DEVICE_TYPES = {
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+ AudioDeviceInfo.TYPE_WIRED_HEADSET,
+ AudioDeviceInfo.TYPE_USB_HEADSET,
+ AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+ AudioDeviceInfo.TYPE_HEARING_AID,
+ AudioDeviceInfo.TYPE_BLE_HEADSET,
+ AudioDeviceInfo.TYPE_USB_DEVICE,
+ AudioDeviceInfo.TYPE_BLE_SPEAKER,
+ AudioDeviceInfo.TYPE_LINE_ANALOG,
+ AudioDeviceInfo.TYPE_HDMI,
+ AudioDeviceInfo.TYPE_AUX_LINE
+ };
+
+ private boolean isValidCommunicationDevice(AudioDeviceInfo device) {
+ for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
+ if (device.getType() == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** @see AudioManager#setDeviceForCommunication(int) */
+ public boolean setDeviceForCommunication(IBinder cb, int portId) {
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+
+ AudioDeviceInfo device = null;
+ if (portId != 0) {
+ device = AudioManager.getDeviceForPortId(portId, AudioManager.GET_DEVICES_OUTPUTS);
+ if (device == null) {
+ throw new IllegalArgumentException("invalid portID " + portId);
+ }
+ if (!isValidCommunicationDevice(device)) {
+ throw new IllegalArgumentException("invalid device type " + device.getType());
+ }
+ }
+ final String eventSource = new StringBuilder("setDeviceForCommunication(")
+ .append(") from u/pid:").append(uid).append("/")
+ .append(pid).toString();
+
+ int deviceType = AudioSystem.DEVICE_OUT_DEFAULT;
+ String deviceAddress = null;
+ if (device != null) {
+ deviceType = device.getPort().type();
+ deviceAddress = device.getAddress();
+ } else {
+ AudioDeviceInfo curDevice = mDeviceBroker.getDeviceForCommunication();
+ if (curDevice != null) {
+ deviceType = curDevice.getPort().type();
+ deviceAddress = curDevice.getAddress();
+ }
+ }
+ // do not log metrics if clearing communication device while no communication device
+ // was selected
+ if (deviceType != AudioSystem.DEVICE_OUT_DEFAULT) {
+ new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
+ + MediaMetrics.SEPARATOR + "setDeviceForCommunication")
+ .set(MediaMetrics.Property.DEVICE,
+ AudioSystem.getDeviceName(deviceType))
+ .set(MediaMetrics.Property.ADDRESS, deviceAddress)
+ .set(MediaMetrics.Property.STATE, device != null
+ ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
+ .record();
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ boolean status =
+ mDeviceBroker.setDeviceForCommunication(cb, pid, device, eventSource);
+ Binder.restoreCallingIdentity(ident);
+ return status;
+ }
+
+ /** @see AudioManager#getDeviceForCommunication() */
+ public int getDeviceForCommunication() {
+ final long ident = Binder.clearCallingIdentity();
+ AudioDeviceInfo device = mDeviceBroker.getDeviceForCommunication();
+ Binder.restoreCallingIdentity(ident);
+ if (device == null) {
+ return 0;
+ }
+ return device.getId();
+ }
+
+ /** @see AudioManager#addOnCommunicationDeviceChangedListener(
+ * Executor, AudioManager.OnCommunicationDeviceChangedListener)
+ */
+ public void registerCommunicationDeviceDispatcher(
+ @Nullable ICommunicationDeviceDispatcher dispatcher) {
+ if (dispatcher == null) {
+ return;
+ }
+ mDeviceBroker.registerCommunicationDeviceDispatcher(dispatcher);
+ }
+
+ /** @see AudioManager#removeOnCommunicationDeviceChangedListener(
+ * AudioManager.OnCommunicationDeviceChangedListener)
+ */
+ public void unregisterCommunicationDeviceDispatcher(
+ @Nullable ICommunicationDeviceDispatcher dispatcher) {
+ if (dispatcher == null) {
+ return;
+ }
+ mDeviceBroker.unregisterCommunicationDeviceDispatcher(dispatcher);
+ }
+
/** @see AudioManager#setSpeakerphoneOn(boolean) */
public void setSpeakerphoneOn(IBinder cb, boolean on) {
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
@@ -4170,10 +4387,10 @@
// for logging only
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
+
final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
- final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
+ MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
.setUid(uid)
@@ -4181,17 +4398,9 @@
.set(MediaMetrics.Property.STATE, on
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
.record();
-
- if (stateChanged) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendBroadcastAsUser(
- new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
+ final long ident = Binder.clearCallingIdentity();
+ mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+ Binder.restoreCallingIdentity(ident);
}
/** @see AudioManager#isSpeakerphoneOn() */
@@ -4199,6 +4408,11 @@
return mDeviceBroker.isSpeakerphoneOn();
}
+
+ /** BT SCO audio state seen by apps using the deprecated API setBluetoothScoOn().
+ * @see isBluetoothScoOn() */
+ private boolean mBtScoOnByApp;
+
/** @see AudioManager#setBluetoothScoOn(boolean) */
public void setBluetoothScoOn(boolean on) {
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
@@ -4207,7 +4421,7 @@
// Only enable calls from system components
if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
- mDeviceBroker.setBluetoothScoOnByApp(on);
+ mBtScoOnByApp = on;
return;
}
@@ -4233,7 +4447,7 @@
* Note that it doesn't report internal state, but state seen by apps (which may have
* called setBluetoothScoOn() */
public boolean isBluetoothScoOn() {
- return mDeviceBroker.isBluetoothScoOnForApp();
+ return mBtScoOnByApp || mDeviceBroker.isBluetoothScoOn();
}
// TODO investigate internal users due to deprecation of SDK API
@@ -4280,7 +4494,7 @@
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(scoAudioMode))
.record();
- startBluetoothScoInt(cb, scoAudioMode, eventSource);
+ startBluetoothScoInt(cb, pid, scoAudioMode, eventSource);
}
@@ -4299,10 +4513,10 @@
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
.record();
- startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
+ startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
}
- void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) {
+ void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) {
MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
.set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -4313,9 +4527,9 @@
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
return;
}
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
- }
+ final long ident = Binder.clearCallingIdentity();
+ mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+ Binder.restoreCallingIdentity(ident);
mmi.record();
}
@@ -4330,9 +4544,9 @@
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
- }
+ final long ident = Binder.clearCallingIdentity();
+ mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+ Binder.restoreCallingIdentity(ident);
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
.setUid(uid)
.setPid(pid)
@@ -4758,8 +4972,7 @@
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
if (isInCommunication()) {
- if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
- == AudioSystem.FORCE_BT_SCO) {
+ if (mDeviceBroker.isBluetoothScoOn()) {
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
@@ -4795,8 +5008,7 @@
}
default:
if (isInCommunication()) {
- if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
- == AudioSystem.FORCE_BT_SCO) {
+ if (mDeviceBroker.isBluetoothScoOn()) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else {
@@ -7566,6 +7778,7 @@
pw.print(" mHasVibrator="); pw.println(mHasVibrator);
pw.print(" mVolumePolicy="); pw.println(mVolumePolicy);
pw.print(" mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
+ pw.print(" mBtScoOnByApp="); pw.println(mBtScoOnByApp);
pw.print(" mIsSingleVolume="); pw.println(mIsSingleVolume);
pw.print(" mUseFixedVolume="); pw.println(mUseFixedVolume);
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index e60243f..ae64990 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -20,6 +20,8 @@
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
+import java.util.List;
+
/**
* Provides an adapter to access functionality of the android.media.AudioSystem class for device
* related functionality.
@@ -77,22 +79,59 @@
}
/**
- * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAttributes)}
+ * Same as {@link AudioSystem#setDevicesRoleForStrategy(int, int, List)}
* @param strategy
- * @param device
+ * @param role
+ * @param devices
* @return
*/
- public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAttributes device) {
- return AudioSystem.setPreferredDeviceForStrategy(strategy, device);
+ public int setDevicesRoleForStrategy(int strategy, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices);
}
/**
- * Same as {@link AudioSystem#removePreferredDeviceForStrategy(int)}
+ * Same as {@link AudioSystem#removeDevicesRoleForStrategy(int, int)}
* @param strategy
+ * @param role
* @return
*/
- public int removePreferredDeviceForStrategy(int strategy) {
- return AudioSystem.removePreferredDeviceForStrategy(strategy);
+ public int removeDevicesRoleForStrategy(int strategy, int role) {
+ return AudioSystem.removeDevicesRoleForStrategy(strategy, role);
+ }
+
+ /**
+ * Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List))
+ * @param capturePreset
+ * @param role
+ * @param devices
+ * @return
+ */
+ public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices);
+ }
+
+ /**
+ * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int)}
+ * @param capturePreset
+ * @param role
+ * @param devicesToRemove
+ * @return
+ */
+ public int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+ return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove);
+ }
+
+ /**
+ * Same as {@link AudioSystem#}
+ * @param capturePreset
+ * @param role
+ * @return
+ */
+ public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
+ return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role);
}
/**
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 7616557..c9a1fcf 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -27,11 +27,10 @@
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
+import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -39,9 +38,7 @@
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.List;
-import java.util.NoSuchElementException;
import java.util.Objects;
/**
@@ -58,10 +55,6 @@
mDeviceBroker = broker;
}
- // List of clients having issued a SCO start request
- @GuardedBy("BtHelper.this")
- private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
-
// BluetoothHeadset API to control SCO connection
private @Nullable BluetoothHeadset mBluetoothHeadset;
@@ -301,6 +294,8 @@
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
+
+ Log.i(TAG, "receiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
setBtScoActiveDevice(btDevice);
@@ -308,20 +303,16 @@
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- // broadcast intent if the connection was initated by AudioService
- if (!mScoClients.isEmpty()
- && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
- || mScoAudioState == SCO_STATE_ACTIVATE_REQ
- || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
- || mScoAudioState == SCO_STATE_DEACTIVATING)) {
- broadcast = true;
- }
+ Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
switch (btState) {
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ } else if (mDeviceBroker.isBluetoothScoRequested()) {
+ // broadcast intent if the connection was initated by AudioService
+ broadcast = true;
}
mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
break;
@@ -333,21 +324,21 @@
// notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
// 2) If audio was connected then disconnected via Bluetooth APIs and
// we still have pending activation requests by apps: this is indicated by
- // state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty.
+ // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
|| (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
- && !mScoClients.isEmpty())) {
+ && mDeviceBroker.isBluetoothScoRequested())) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
&& connectBluetoothScoAudioHelper(mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- broadcast = false;
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+ broadcast = true;
break;
}
}
- // Tear down SCO if disconnected from external
- if (mScoAudioState == SCO_STATE_DEACTIVATING) {
- clearAllScoClients(0, false);
+ if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+ broadcast = true;
}
mScoAudioState = SCO_STATE_INACTIVE;
break;
@@ -356,11 +347,8 @@
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
- broadcast = false;
break;
default:
- // do not broadcast CONNECTING or invalid state
- broadcast = false;
break;
}
if (broadcast) {
@@ -386,81 +374,19 @@
== BluetoothHeadset.STATE_AUDIO_CONNECTED;
}
- /**
- * Disconnect all SCO connections started by {@link AudioManager} except those started by
- * {@param exceptPid}
- *
- * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
- */
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
- checkScoAudioState();
- if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
- return;
- }
- clearAllScoClients(exceptPid, true);
- }
-
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
+ /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
@NonNull String eventSource) {
- ScoClient client = getScoClient(cb, true);
- // The calling identity must be cleared before calling ScoClient.incCount().
- // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
- // and this must be done on behalf of system server to make sure permissions are granted.
- // The caller identity must be cleared after getScoClient() because it is needed if a new
- // client is created.
- final long ident = Binder.clearCallingIdentity();
- try {
- AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
- client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
- } catch (NullPointerException e) {
- Log.e(TAG, "Null ScoClient", e);
- }
- Binder.restoreCallingIdentity(ident);
- }
-
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
- @NonNull String eventSource) {
- ScoClient client = getScoClient(cb, false);
- // The calling identity must be cleared before calling ScoClient.decCount().
- // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
- // and this must be done on behalf of system server to make sure permissions are granted.
- final long ident = Binder.clearCallingIdentity();
- if (client != null) {
- stopAndRemoveClient(client, eventSource);
- }
- Binder.restoreCallingIdentity(ident);
- }
-
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
- ScoClient client = getScoClientForPid(pid);
- if (client == null) {
- return;
- }
- final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
- .append(pid).append(")").toString();
- stopAndRemoveClient(client, eventSource);
- }
-
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- // @GuardedBy("BtHelper.this")
- private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
- client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
- SCO_MODE_VIRTUAL_CALL);
- // If a disconnection is pending, the client will be removed when clearAllScoClients()
- // is called form receiveBtEvent()
- if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
- && mScoAudioState != SCO_STATE_DEACTIVATING) {
- client.remove(false /*stop */, true /*unregister*/);
- }
+ return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
+ }
+
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
+ AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+ return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
}
/*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
@@ -507,7 +433,6 @@
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
- clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
AudioSystem.setParameters("A2dpSuspended=false");
@@ -606,46 +531,64 @@
mDeviceBroker.postBroadcastScoConnectionState(state);
}
- private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
- if (btDevice == null) {
- return true;
+ @Nullable AudioDeviceAttributes getHeadsetAudioDevice() {
+ if (mBluetoothHeadsetDevice == null) {
+ return null;
}
+ return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
+ }
+
+ private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
String address = btDevice.getAddress();
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ address = "";
+ }
BluetoothClass btClass = btDevice.getBluetoothClass();
- int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- int[] outDeviceTypes = {
- AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
- AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
- AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
- };
+ int nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
if (btClass != null) {
switch (btClass.getDeviceClass()) {
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET };
+ nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
break;
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT };
+ nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
break;
}
}
- if (!BluetoothAdapter.checkBluetoothAddress(address)) {
- address = "";
+ if (AudioService.DEBUG_DEVICES) {
+ Log.i(TAG, "btHeadsetDeviceToAudioDevice btDevice: " + btDevice
+ + " btClass: " + (btClass == null ? "Unknown" : btClass)
+ + " nativeType: " + nativeType + " address: " + address);
}
+ return new AudioDeviceAttributes(nativeType, address);
+ }
+
+ private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
+ if (btDevice == null) {
+ return true;
+ }
+ int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
String btDeviceName = getName(btDevice);
boolean result = false;
if (isActive) {
- result |= mDeviceBroker.handleDeviceConnection(
- isActive, outDeviceTypes[0], address, btDeviceName);
+ result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(),
+ audioDevice.getAddress(), btDeviceName);
} else {
+ int[] outDeviceTypes = {
+ AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
+ AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+ AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+ };
for (int outDeviceType : outDeviceTypes) {
result |= mDeviceBroker.handleDeviceConnection(
- isActive, outDeviceType, address, btDeviceName);
+ isActive, outDeviceType, audioDevice.getAddress(), btDeviceName);
}
}
// handleDeviceConnection() && result to make sure the method get executed
result = mDeviceBroker.handleDeviceConnection(
- isActive, inDevice, address, btDeviceName) && result;
+ isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result;
return result;
}
@@ -733,195 +676,122 @@
};
//----------------------------------------------------------------------
+
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ synchronized void scoClientDied(Object obj) {
- final ScoClient client = (ScoClient) obj;
- client.remove(true /*stop*/, false /*unregister*/);
- Log.w(TAG, "SCO client died");
- }
-
- private class ScoClient implements IBinder.DeathRecipient {
- private IBinder mCb; // To be notified of client's death
- private int mCreatorPid;
-
- ScoClient(IBinder cb) {
- mCb = cb;
- mCreatorPid = Binder.getCallingPid();
- }
-
- public void registerDeathRecipient() {
- try {
- mCb.linkToDeath(this, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
- }
- }
-
- public void unregisterDeathRecipient() {
- try {
- mCb.unlinkToDeath(this, 0);
- } catch (NoSuchElementException e) {
- Log.w(TAG, "ScoClient could not not unregistered to binder");
- }
- }
-
- @Override
- public void binderDied() {
- // process this from DeviceBroker's message queue to take the right locks since
- // this event can impact SCO mode and requires querying audio mode stack
- mDeviceBroker.postScoClientDied(this);
- }
-
- IBinder getBinder() {
- return mCb;
- }
-
- int getPid() {
- return mCreatorPid;
- }
-
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- @GuardedBy("BtHelper.this")
- private boolean requestScoState(int state, int scoAudioMode) {
- checkScoAudioState();
- if (mScoClients.size() != 1) {
- Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
- + ", num SCO clients=" + mScoClients.size());
- return true;
- }
- if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
- // Make sure that the state transitions to CONNECTING even if we cannot initiate
- // the connection.
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
- // Accept SCO audio activation only in NORMAL audio mode or if the mode is
- // currently controlled by the same client process.
- final int modeOwnerPid = mDeviceBroker.getModeOwnerPid();
- if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
- Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
- + modeOwnerPid + " != creatorPid " + mCreatorPid);
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("BtHelper.this")
+ private boolean requestScoState(int state, int scoAudioMode) {
+ checkScoAudioState();
+ if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ // Make sure that the state transitions to CONNECTING even if we cannot initiate
+ // the connection.
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+ switch (mScoAudioState) {
+ case SCO_STATE_INACTIVE:
+ mScoAudioMode = scoAudioMode;
+ if (scoAudioMode == SCO_MODE_UNDEFINED) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+ if (mBluetoothHeadsetDevice != null) {
+ mScoAudioMode = Settings.Global.getInt(
+ mDeviceBroker.getContentResolver(),
+ "bluetooth_sco_channel_"
+ + mBluetoothHeadsetDevice.getAddress(),
+ SCO_MODE_VIRTUAL_CALL);
+ if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
+ mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+ }
+ }
+ }
+ if (mBluetoothHeadset == null) {
+ if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ } else {
+ Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ + " connection, mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
+ }
+ break;
+ }
+ if (mBluetoothHeadsetDevice == null) {
+ Log.w(TAG, "requestScoState: no active device while connecting,"
+ + " mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
+ }
+ if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ } else {
+ Log.w(TAG, "requestScoState: connect to "
+ + mBluetoothHeadsetDevice
+ + " failed, mScoAudioMode=" + mScoAudioMode);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
+ }
+ break;
+ case SCO_STATE_DEACTIVATING:
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ break;
+ case SCO_STATE_ACTIVE_INTERNAL:
+ Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
+ break;
+ default:
+ Log.w(TAG, "requestScoState: failed to connect in state "
+ + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
return false;
- }
- switch (mScoAudioState) {
- case SCO_STATE_INACTIVE:
- mScoAudioMode = scoAudioMode;
- if (scoAudioMode == SCO_MODE_UNDEFINED) {
- mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
- if (mBluetoothHeadsetDevice != null) {
- mScoAudioMode = Settings.Global.getInt(
- mDeviceBroker.getContentResolver(),
- "bluetooth_sco_channel_"
- + mBluetoothHeadsetDevice.getAddress(),
- SCO_MODE_VIRTUAL_CALL);
- if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
- mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
- }
- }
- }
- if (mBluetoothHeadset == null) {
- if (getBluetoothHeadset()) {
- mScoAudioState = SCO_STATE_ACTIVATE_REQ;
- } else {
- Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
- + " connection, mScoAudioMode=" + mScoAudioMode);
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- break;
- }
- if (mBluetoothHeadsetDevice == null) {
- Log.w(TAG, "requestScoState: no active device while connecting,"
- + " mScoAudioMode=" + mScoAudioMode);
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
- mBluetoothHeadsetDevice, mScoAudioMode)) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ }
+ } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+ switch (mScoAudioState) {
+ case SCO_STATE_ACTIVE_INTERNAL:
+ if (mBluetoothHeadset == null) {
+ if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
} else {
- Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
- + " failed, mScoAudioMode=" + mScoAudioMode);
+ Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ + " disconnection, mScoAudioMode=" + mScoAudioMode);
+ mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
return false;
}
break;
- case SCO_STATE_DEACTIVATING:
- mScoAudioState = SCO_STATE_ACTIVATE_REQ;
- break;
- case SCO_STATE_DEACTIVATE_REQ:
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
- break;
- case SCO_STATE_ACTIVE_INTERNAL:
- Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
- break;
- default:
- Log.w(TAG, "requestScoState: failed to connect in state "
- + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
- switch (mScoAudioState) {
- case SCO_STATE_ACTIVE_INTERNAL:
- if (mBluetoothHeadset == null) {
- if (getBluetoothHeadset()) {
- mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
- } else {
- Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
- + " disconnection, mScoAudioMode=" + mScoAudioMode);
- mScoAudioState = SCO_STATE_INACTIVE;
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
- break;
- }
- if (mBluetoothHeadsetDevice == null) {
- mScoAudioState = SCO_STATE_INACTIVE;
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- break;
- }
- if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
- mBluetoothHeadsetDevice, mScoAudioMode)) {
- mScoAudioState = SCO_STATE_DEACTIVATING;
- } else {
- mScoAudioState = SCO_STATE_INACTIVE;
- broadcastScoConnectionState(
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- }
- break;
- case SCO_STATE_ACTIVATE_REQ:
+ }
+ if (mBluetoothHeadsetDevice == null) {
mScoAudioState = SCO_STATE_INACTIVE;
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
break;
- default:
- Log.w(TAG, "requestScoState: failed to disconnect in state "
- + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- return false;
- }
+ }
+ if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
+ mScoAudioState = SCO_STATE_DEACTIVATING;
+ } else {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ break;
+ case SCO_STATE_ACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ break;
+ default:
+ Log.w(TAG, "requestScoState: failed to disconnect in state "
+ + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ return false;
}
- return true;
}
-
- @GuardedBy("BtHelper.this")
- void remove(boolean stop, boolean unregister) {
- if (unregister) {
- unregisterDeathRecipient();
- }
- if (stop) {
- requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
- SCO_MODE_VIRTUAL_CALL);
- }
- mScoClients.remove(this);
- }
+ return true;
}
//-----------------------------------------------------
@@ -974,49 +844,6 @@
}
}
-
- @GuardedBy("BtHelper.this")
- private ScoClient getScoClient(IBinder cb, boolean create) {
- for (ScoClient existingClient : mScoClients) {
- if (existingClient.getBinder() == cb) {
- return existingClient;
- }
- }
- if (create) {
- ScoClient newClient = new ScoClient(cb);
- newClient.registerDeathRecipient();
- mScoClients.add(newClient);
- return newClient;
- }
- return null;
- }
-
- @GuardedBy("BtHelper.this")
- private ScoClient getScoClientForPid(int pid) {
- for (ScoClient cl : mScoClients) {
- if (cl.getPid() == pid) {
- return cl;
- }
- }
- return null;
- }
-
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- @GuardedBy("BtHelper.this")
- private void clearAllScoClients(int exceptPid, boolean stopSco) {
- final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
- for (ScoClient cl : mScoClients) {
- if (cl.getPid() != exceptPid) {
- clients.add(cl);
- }
- }
- for (ScoClient cl : clients) {
- cl.remove(stopSco, true /*unregister*/);
- }
-
- }
-
private boolean getBluetoothHeadset() {
boolean result = false;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1062,10 +889,6 @@
pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
- pw.println(prefix + "Sco clients:");
- mScoClients.forEach((cl) -> {
- pw.println(" " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); });
-
pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
pw.println(prefix + "mA2dp: " + mA2dp);
pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index ff31931..a8aa9aa 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -143,6 +143,9 @@
* @return {@code true} if the change should be enabled for the package.
*/
boolean isEnabled(ApplicationInfo app) {
+ if (app == null) {
+ return defaultValue();
+ }
if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) {
return mPackageOverrides.get(app.packageName);
}
@@ -156,6 +159,15 @@
}
/**
+ * Returns the default value for the change id, assuming there are no overrides.
+ *
+ * @return {@code false} if it's a default disabled change, {@code true} otherwise.
+ */
+ boolean defaultValue() {
+ return !getDisabled();
+ }
+
+ /**
* Checks whether a change has an override for a package.
* @param packageName name of the package
* @return true if there is such override
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index d80c58b..8511118 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -392,6 +392,14 @@
return alreadyKnown;
}
+ boolean defaultChangeIdValue(long changeId) {
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ return true;
+ }
+ return c.defaultValue();
+ }
+
@VisibleForTesting
void clearChanges() {
synchronized (mChanges) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 2c19a2d..aa85f7f 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -60,7 +60,6 @@
private final CompatConfig mCompatConfig;
private static int sMinTargetSdk = Build.VERSION_CODES.Q;
- private static int sMaxTargetSdk = Build.VERSION_CODES.R;
public PlatformCompat(Context context) {
mContext = context;
@@ -107,17 +106,25 @@
}
/**
- * Internal version of the above method. Does not perform costly permission check.
+ * Internal version of the above method, without logging. Does not perform costly permission
+ * check.
+ * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property.
+ */
+ public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) {
+ return mCompatConfig.isChangeEnabled(changeId, appInfo);
+ }
+
+ /**
+ * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. Does not perform costly
+ * permission check.
*/
public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) {
- if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
+ boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo);
+ if (appInfo != null) {
reportChange(changeId, appInfo.uid,
- ChangeReporter.STATE_ENABLED);
- return true;
+ enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED);
}
- reportChange(changeId, appInfo.uid,
- ChangeReporter.STATE_DISABLED);
- return false;
+ return enabled;
}
@Override
@@ -125,9 +132,6 @@
@UserIdInt int userId) {
checkCompatChangeReadAndLogPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
- if (appInfo == null) {
- return true;
- }
return isChangeEnabled(changeId, appInfo);
}
@@ -136,7 +140,7 @@
checkCompatChangeReadAndLogPermission();
String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length == 0) {
- return true;
+ return mCompatConfig.defaultChangeIdValue(changeId);
}
boolean enabled = true;
for (String packageName : packages) {
@@ -379,8 +383,7 @@
return false;
}
if (change.getEnableSinceTargetSdk() > 0) {
- if (change.getEnableSinceTargetSdk() < sMinTargetSdk
- || change.getEnableSinceTargetSdk() > sMaxTargetSdk) {
+ if (change.getEnableSinceTargetSdk() < sMinTargetSdk) {
return false;
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 342a11b..c4beddd4 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -28,9 +28,9 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
-import static android.net.NetworkUtils.multiplySafeByRational;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
import static com.android.server.net.NetworkStatsService.TAG;
import android.net.NetworkIdentity;
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index f116a24..c6a55b4 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -323,9 +323,9 @@
* Copies the CE apex data directory for the given {@code userId} to a backup location, for use
* in case of rollback.
*
- * @return long inode for the snapshot directory if the snapshot was successful, or -1 if not
+ * @return boolean true if the snapshot was successful
*/
- public abstract long snapshotCeData(int userId, int rollbackId, String apexPackageName);
+ public abstract boolean snapshotCeData(int userId, int rollbackId, String apexPackageName);
/**
* Restores the snapshot of the CE apex data directory for the given {@code userId}.
@@ -826,7 +826,7 @@
}
@Override
- public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
+ public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
String apexModuleName;
synchronized (mLock) {
Preconditions.checkState(mPackageNameToApexModuleName != null,
@@ -835,13 +835,14 @@
}
if (apexModuleName == null) {
Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
- return -1;
+ return false;
}
try {
- return waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
+ waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
+ return true;
} catch (Exception e) {
Slog.e(TAG, e.getMessage(), e);
- return -1;
+ return false;
}
}
@@ -1124,7 +1125,7 @@
}
@Override
- public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
+ public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
throw new UnsupportedOperationException();
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 069a00f..f221802 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -290,7 +290,8 @@
private void updateEnabledState(@NonNull AndroidPackage pkg) {
// TODO(b/135203078): Do not use toAppInfo
- final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternal(
+ // TODO(b/167551701): Make changeId non-logging
+ final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternalNoLogging(
PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
if (enabled) {
mDisabledPackages.remove(pkg.getPackageName());
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 990055e..5c01e43 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -32,6 +32,8 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -49,10 +51,6 @@
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
/**
* The recovery system service is responsible for coordinating recovery related
@@ -84,9 +82,9 @@
private final Context mContext;
@GuardedBy("this")
- private final Map<String, IntentSender> mCallerPendingRequest = new HashMap<>();
+ private final ArrayMap<String, IntentSender> mCallerPendingRequest = new ArrayMap<>();
@GuardedBy("this")
- private final Set<String> mCallerPreparedForReboot = new HashSet<>();
+ private final ArraySet<String> mCallerPreparedForReboot = new ArraySet<>();
/**
* Need to prepare for resume on reboot.
@@ -121,7 +119,7 @@
@IntDef({ ROR_NEED_PREPARATION,
ROR_SKIP_PREPARATION_AND_NOTIFY,
ROR_SKIP_PREPARATION_NOT_NOTIFY })
- @interface ResumeOnRebootActionsOnRequest {}
+ private @interface ResumeOnRebootActionsOnRequest {}
/**
* The action to perform upon resume on reboot clear request for a given client.
@@ -129,7 +127,7 @@
@IntDef({ROR_NOT_REQUESTED,
ROR_REQUESTED_NEED_CLEAR,
ROR_REQUESTED_SKIP_CLEAR})
- @interface ResumeOnRebootActionsOnClear{}
+ private @interface ResumeOnRebootActionsOnClear{}
static class Injector {
protected final Context mContext;
@@ -342,9 +340,8 @@
!= PackageManager.PERMISSION_GRANTED
&& mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT)
!= PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller or self must have "
- + android.Manifest.permission.RECOVERY + " or "
- + android.Manifest.permission.REBOOT + " for resume on reboot.");
+ throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY
+ + " or " + android.Manifest.permission.REBOOT + " for resume on reboot.");
}
}
@@ -414,10 +411,14 @@
Slog.w(TAG, "onPreparedForReboot called when some clients have prepared.");
}
+ if (mCallerPendingRequest.isEmpty()) {
+ Slog.w(TAG, "onPreparedForReboot called but no client has requested.");
+ }
+
// Send intents to notify callers
- for (Map.Entry<String, IntentSender> entry : mCallerPendingRequest.entrySet()) {
- sendPreparedForRebootIntentIfNeeded(entry.getValue());
- mCallerPreparedForReboot.add(entry.getKey());
+ for (int i = 0; i < mCallerPendingRequest.size(); i++) {
+ sendPreparedForRebootIntentIfNeeded(mCallerPendingRequest.valueAt(i));
+ mCallerPreparedForReboot.add(mCallerPendingRequest.keyAt(i));
}
mCallerPendingRequest.clear();
}
@@ -475,9 +476,7 @@
return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
}
- @Override // Binder call
- public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
- enforcePermissionForResumeOnReboot();
+ private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
return false;
@@ -498,11 +497,29 @@
return true;
}
+ @Override // Binder call for the legacy rebootWithLskf
+ public boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+ return rebootWithLskfImpl(packageName, reason, true);
+ }
+
@Override // Binder call
- public synchronized boolean isLskfCaptured(String packageName) {
+ public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
enforcePermissionForResumeOnReboot();
- if (!mCallerPreparedForReboot.contains(packageName)) {
- Slog.i(TAG, "Reboot requested before prepare completed for caller " + packageName);
+ return rebootWithLskfImpl(packageName, reason, slotSwitch);
+ }
+
+ @Override // Binder call
+ public boolean isLskfCaptured(String packageName) {
+ enforcePermissionForResumeOnReboot();
+ boolean captured;
+ synchronized (this) {
+ captured = mCallerPreparedForReboot.contains(packageName);
+ }
+
+ if (!captured) {
+ Slog.i(TAG, "Reboot requested before prepare completed for caller "
+ + packageName);
return false;
}
return true;
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 524ae54..0213cc6 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -129,13 +129,8 @@
if (packageRollbackInfo.isApex()) {
// For APEX, only snapshot CE here
if ((flags & Installer.FLAG_STORAGE_CE) != 0) {
- long ceSnapshotInode = mApexManager.snapshotCeData(
+ return mApexManager.snapshotCeData(
userId, rollbackId, packageRollbackInfo.getPackageName());
- if (ceSnapshotInode > 0) {
- packageRollbackInfo.putCeSnapshotInode(userId, ceSnapshotInode);
- } else {
- return false;
- }
}
} else {
// APK
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index faf3f06..b20c493 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -195,7 +195,6 @@
import android.media.IAudioService;
import android.net.ConnectivityManager;
import android.net.IIpConnectivityMetrics;
-import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.Uri;
import android.net.metrics.IpConnectivityLog;
@@ -267,6 +266,7 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.statusbar.IStatusBarService;
@@ -15540,7 +15540,7 @@
return PRIVATE_DNS_SET_NO_ERROR;
case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
if (TextUtils.isEmpty(privateDnsHost)
- || !NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+ || !NetworkUtilsInternal.isWeaklyValidatedHostname(privateDnsHost)) {
throw new IllegalArgumentException(
String.format("Provided hostname %s is not valid", privateDnsHost));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..d825dfd
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index a9cef20..8d706cb 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -21,6 +21,8 @@
import android.media.AudioSystem;
import android.util.Log;
+import java.util.List;
+
/**
* Provides an adapter for AudioSystem that does nothing.
* Overridden methods can be configured.
@@ -66,13 +68,30 @@
}
@Override
- public int setPreferredDeviceForStrategy(int strategy,
- @NonNull AudioDeviceAttributes device) {
+ public int setDevicesRoleForStrategy(int strategy, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
return AudioSystem.AUDIO_STATUS_OK;
}
@Override
- public int removePreferredDeviceForStrategy(int strategy) {
+ public int removeDevicesRoleForStrategy(int strategy, int role) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int removeDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
+ public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
return AudioSystem.AUDIO_STATUS_OK;
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/OWNERS b/services/tests/servicestests/src/com/android/server/audio/OWNERS
new file mode 100644
index 0000000..894a1f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/audio/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index e588370..8c63bfc 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -190,6 +190,22 @@
}
@Test
+ public void testIsChangeEnabledForInvalidApp() throws Exception {
+ final long disabledChangeId = 1234L;
+ final long enabledChangeId = 1235L;
+ final long targetSdkChangeId = 1236L;
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addEnabledChangeWithId(enabledChangeId)
+ .addDisabledChangeWithId(disabledChangeId)
+ .addEnableSinceSdkChangeWithId(42, targetSdkChangeId)
+ .build();
+
+ assertThat(compatConfig.isChangeEnabled(enabledChangeId, null)).isTrue();
+ assertThat(compatConfig.isChangeEnabled(disabledChangeId, null)).isFalse();
+ assertThat(compatConfig.isChangeEnabled(targetSdkChangeId, null)).isTrue();
+ }
+
+ @Test
public void testPreventAddOverride() throws Exception {
final long changeId = 1234L;
CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 64014ba..1d3b643 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -107,18 +107,20 @@
mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
.addEnabledChangeWithId(1L)
.addDisabledChangeWithIdAndName(2L, "change2")
- .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
- .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L)
- .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
- .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+ .addEnableSinceSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc")
+ .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+ .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+ .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
.addLoggingOnlyChangeWithId(7L)
.build();
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
- new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
- new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""));
+ new CompatibilityChangeInfo(5L, "", /*enableAfter*/ -1,
+ /*enableSince*/ Build.VERSION_CODES.Q, false, false, ""),
+ new CompatibilityChangeInfo(6L, "", /*enableAfter*/ -1,
+ /*enableSince*/ Build.VERSION_CODES.R, false, false, ""));
}
@Test
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 11bb867..4e9e6a8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2046,7 +2046,13 @@
* via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
* and can choose to hide or show the video calling icon based on whether a contact supports
* video.
+ *
+ * @deprecated No longer used in framework code, however it may still be used by applications
+ * that have not updated their code. This config should still be set to {@code true} if
+ * {@link Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is set to {@code true} and
+ * {@link Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL} is set to {@code true}.
*/
+ @Deprecated
public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
/**
@@ -3862,13 +3868,51 @@
* <p>
* If this key's value is set to false, the procedure for RCS contact capability exchange
* via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and
- * {@link #KEY_USE_RCS_PRESENCE_BOOL} must also be set to false to ensure apps do not
- * improperly think that capability exchange via SIP PUBLISH is enabled.
+ * {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be set to false to ensure
+ * apps do not improperly think that capability exchange via SIP PUBLISH is enabled.
* <p> The default value for this key is {@code false}.
*/
public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL =
KEY_PREFIX + "enable_presence_publish_bool";
+ /**
+ * Flag indicating whether or not this carrier supports the exchange of phone numbers with
+ * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested
+ * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3
+ * for more information.
+ * <p>
+ * When presence is supported, the device uses the SIP SUBSCRIBE/NOTIFY procedure internally
+ * to retrieve the requested RCS capabilities. See
+ * {@link android.telephony.ims.RcsUceAdapter} for more information on how RCS capabilities
+ * can be retrieved from the carrier's network.
+ */
+ public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL =
+ KEY_PREFIX + "enable_presence_capability_exchange_bool";
+
+
+ /**
+ * Flag indicating whether or not the carrier expects the RCS UCE service to periodically
+ * refresh the RCS capabilities cache of the user's contacts as well as request the
+ * capabilities of call contacts when the SIM card is first inserted or when a new contact
+ * is added, removed, or modified. This corresponds to the RCC.07 A.19
+ * "DISABLE INITIAL ADDRESS BOOK SCAN" parameter.
+ * <p>
+ * If this flag is disabled, the capabilities cache will not be refreshed internally at all
+ * and will only be updated if the cached capabilities are stale when an application
+ * requests them.
+ */
+ public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL =
+ KEY_PREFIX + "rcs_bulk_capability_exchange_bool";
+
+ /**
+ * Flag indicating whether or not the carrier supports capability exchange with a list of
+ * contacts. When {@code true}, the device will batch together multiple requests and
+ * construct a RLMI document in the SIP SUBSCRIBE request (see RFC 4662). If {@code false},
+ * the request will be split up into one SIP SUBSCRIBE request per contact.
+ */
+ public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
+ KEY_PREFIX + "enable_presence_group_subscribe_bool";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -3876,6 +3920,9 @@
defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
+ defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
+ defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
+ defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 26fd99d..0e06444 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9338,10 +9338,9 @@
* @return true if mobile data is enabled.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
- android.Manifest.permission.MODIFY_PHONE_STATE,
- android.Manifest.permission.READ_PHONE_STATE})
+ android.Manifest.permission.MODIFY_PHONE_STATE})
public boolean isDataEnabled() {
- return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
+ return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
}
/**
@@ -9586,7 +9585,7 @@
@SystemApi
public boolean getDataEnabled(int subId) {
try {
- return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
+ return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
} catch (RuntimeException e) {
Log.e(TAG, "Error calling isDataEnabledForReason e:" + e);
}
@@ -12301,23 +12300,15 @@
@NonNull
public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
@EmergencyServiceCategories int categories) {
- Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>();
+ Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- emergencyNumberList = telephony.getEmergencyNumberList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (emergencyNumberList != null) {
- for (Integer subscriptionId : emergencyNumberList.keySet()) {
- List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId);
- for (EmergencyNumber number : numberList) {
- if (!number.isInEmergencyServiceCategories(categories)) {
- numberList.remove(number);
- }
- }
- }
- }
- return emergencyNumberList;
+ Map<Integer, List<EmergencyNumber>> emergencyNumberList =
+ telephony.getEmergencyNumberList(mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ emergencyNumberListForCategories =
+ filterEmergencyNumbersByCategories(emergencyNumberList, categories);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -12325,7 +12316,34 @@
Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex);
ex.rethrowAsRuntimeException();
}
- return emergencyNumberList;
+ return emergencyNumberListForCategories;
+ }
+
+ /**
+ * Filter emergency numbers with categories.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
+ Map<Integer, List<EmergencyNumber>> emergencyNumberList,
+ @EmergencyServiceCategories int categories) {
+ Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
+ if (emergencyNumberList != null) {
+ for (Integer subscriptionId : emergencyNumberList.keySet()) {
+ List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
+ subscriptionId);
+ List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
+ for (EmergencyNumber number : allNumbersForSub) {
+ if (number.isInEmergencyServiceCategories(categories)) {
+ numbersForCategoriesPerSub.add(number);
+ }
+ }
+ emergencyNumberListForCategories.put(
+ subscriptionId, numbersForCategoriesPerSub);
+ }
+ }
+ return emergencyNumberListForCategories;
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 0d8e5bc..bd623e0 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -30,7 +30,6 @@
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsController;
@@ -62,9 +61,10 @@
* been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
* <p>
* This intent will always be handled by the system, however the application should only send
- * this Intent if the carrier supports RCS contact discovery, which can be queried using the key
- * {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the RCS contact discovery
- * opt-in dialog will not be shown.
+ * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either
+ * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL}
+ * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true.
+ * Otherwise, the RCS contact discovery opt-in dialog will not be shown.
* <p>
* Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
* setting will be be shown for.
@@ -396,6 +396,7 @@
* rather the subscription is capable of this service over IMS.
* @see #isAvailable(int)
* @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+ * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
* @throws ImsException if the IMS service is not available when calling this method.
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index 77e9f12..bcc9072 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -29,6 +29,7 @@
import com.android.net.module.util.ArrayTrackRecord
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.isDevSdkInRange
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -173,10 +174,12 @@
@Test
fun testDeclareNetworkRequestUnfulfillable() {
val mockContext = mock(Context::class.java)
- val provider = createNetworkProvider(mockContext)
- // ConnectivityManager not required at creation time
- verifyNoMoreInteractions(mockContext)
doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE)
+ val provider = createNetworkProvider(mockContext)
+ // ConnectivityManager not required at creation time after R
+ if (!isDevSdkInRange(0, Build.VERSION_CODES.R)) {
+ verifyNoMoreInteractions(mockContext)
+ }
mCm.registerNetworkProvider(provider)
diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/tests/net/java/android/net/NetworkUtilsTest.java
index 3158cc8..7748288 100644
--- a/tests/net/java/android/net/NetworkUtilsTest.java
+++ b/tests/net/java/android/net/NetworkUtilsTest.java
@@ -16,24 +16,10 @@
package android.net;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.EPERM;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_STREAM;
-
import static junit.framework.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import android.system.ErrnoException;
-import android.system.Os;
-
import androidx.test.runner.AndroidJUnit4;
-import libcore.io.IoUtils;
-
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -139,50 +125,4 @@
assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536),
NetworkUtils.routedIPv6AddressCount(set));
}
-
- private static void expectSocketSuccess(String msg, int domain, int type) {
- try {
- IoUtils.closeQuietly(Os.socket(domain, type, 0));
- } catch (ErrnoException e) {
- fail(msg + e.getMessage());
- }
- }
-
- private static void expectSocketPemissionError(String msg, int domain, int type) {
- try {
- IoUtils.closeQuietly(Os.socket(domain, type, 0));
- fail(msg);
- } catch (ErrnoException e) {
- assertEquals(msg, e.errno, EPERM);
- }
- }
-
- private static void expectHasNetworking() {
- expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
- AF_UNIX, SOCK_STREAM);
- expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException",
- AF_INET, SOCK_DGRAM);
- expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException",
- AF_INET6, SOCK_DGRAM);
- }
-
- private static void expectNoNetworking() {
- expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
- AF_UNIX, SOCK_STREAM);
- expectSocketPemissionError(
- "Creating a AF_INET socket should have thrown ErrnoException(EPERM)",
- AF_INET, SOCK_DGRAM);
- expectSocketPemissionError(
- "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)",
- AF_INET6, SOCK_DGRAM);
- }
-
- @Test
- public void testSetAllowNetworkingForProcess() {
- expectHasNetworking();
- NetworkUtils.setAllowNetworkingForProcess(false);
- expectNoNetworking();
- NetworkUtils.setAllowNetworkingForProcess(true);
- expectHasNetworking();
- }
}
diff --git a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java b/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java
new file mode 100644
index 0000000..3cfecd5
--- /dev/null
+++ b/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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.internal.net;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.fail;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import libcore.io.IoUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@androidx.test.filters.SmallTest
+public class NetworkUtilsInternalTest {
+
+ private static void expectSocketSuccess(String msg, int domain, int type) {
+ try {
+ IoUtils.closeQuietly(Os.socket(domain, type, 0));
+ } catch (ErrnoException e) {
+ fail(msg + e.getMessage());
+ }
+ }
+
+ private static void expectSocketPemissionError(String msg, int domain, int type) {
+ try {
+ IoUtils.closeQuietly(Os.socket(domain, type, 0));
+ fail(msg);
+ } catch (ErrnoException e) {
+ assertEquals(msg, e.errno, EPERM);
+ }
+ }
+
+ private static void expectHasNetworking() {
+ expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+ AF_UNIX, SOCK_STREAM);
+ expectSocketSuccess("Creating a AF_INET socket shouldn't have thrown ErrnoException",
+ AF_INET, SOCK_DGRAM);
+ expectSocketSuccess("Creating a AF_INET6 socket shouldn't have thrown ErrnoException",
+ AF_INET6, SOCK_DGRAM);
+ }
+
+ private static void expectNoNetworking() {
+ expectSocketSuccess("Creating a UNIX socket should not have thrown ErrnoException",
+ AF_UNIX, SOCK_STREAM);
+ expectSocketPemissionError(
+ "Creating a AF_INET socket should have thrown ErrnoException(EPERM)",
+ AF_INET, SOCK_DGRAM);
+ expectSocketPemissionError(
+ "Creating a AF_INET6 socket should have thrown ErrnoException(EPERM)",
+ AF_INET6, SOCK_DGRAM);
+ }
+
+ @Test
+ public void testSetAllowNetworkingForProcess() {
+ expectHasNetworking();
+ NetworkUtilsInternal.setAllowNetworkingForProcess(false);
+ expectNoNetworking();
+ NetworkUtilsInternal.setAllowNetworkingForProcess(true);
+ expectHasNetworking();
+ }
+}
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index ea763d2..e150229 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -68,11 +68,12 @@
@SmallTest
public class NetworkManagementServiceTest {
private NetworkManagementService mNMService;
-
@Mock private Context mContext;
@Mock private IBatteryStats.Stub mBatteryStatsService;
@Mock private INetd.Stub mNetdService;
+ private static final int TEST_UID = 111;
+
@NonNull
@Captor
private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor;
@@ -165,14 +166,14 @@
/**
* Interface class activity.
*/
- unsolListener.onInterfaceClassActivityChanged(true, 1, 1234, 0);
- expectSoon(observer).interfaceClassDataActivityChanged("1", true, 1234);
+ unsolListener.onInterfaceClassActivityChanged(true, 1, 1234, TEST_UID);
+ expectSoon(observer).interfaceClassDataActivityChanged("1", true, 1234, TEST_UID);
- unsolListener.onInterfaceClassActivityChanged(false, 9, 5678, 0);
- expectSoon(observer).interfaceClassDataActivityChanged("9", false, 5678);
+ unsolListener.onInterfaceClassActivityChanged(false, 9, 5678, TEST_UID);
+ expectSoon(observer).interfaceClassDataActivityChanged("9", false, 5678, TEST_UID);
- unsolListener.onInterfaceClassActivityChanged(false, 9, 4321, 0);
- expectSoon(observer).interfaceClassDataActivityChanged("9", false, 4321);
+ unsolListener.onInterfaceClassActivityChanged(false, 9, 4321, TEST_UID);
+ expectSoon(observer).interfaceClassDataActivityChanged("9", false, 4321, TEST_UID);
/**
* IP address changes.
@@ -222,8 +223,6 @@
assertFalse(mNMService.isFirewallEnabled());
}
- private static final int TEST_UID = 111;
-
@Test
public void testNetworkRestrictedDefault() {
assertFalse(mNMService.isNetworkRestricted(TEST_UID));
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index fb0cfc0..89146f9 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -23,11 +23,11 @@
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.NetworkUtils.multiplySafeByRational;
import static android.os.Process.myUid;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertArrayEquals;
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index ef33c34..793a236 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -531,14 +531,14 @@
}
StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
- if (auto str = pool.stringAt(idx)) {
+ if (auto str = pool.stringAt(idx); str.ok()) {
return *str;
}
return StringPiece16();
}
std::string GetString(const android::ResStringPool& pool, size_t idx) {
- if (auto str = pool.string8At(idx)) {
+ if (auto str = pool.string8At(idx); str.ok()) {
return ModifiedUtf8ToUtf8(str->to_string());
}
return Utf16ToUtf8(GetString16(pool, idx));
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 8b89959..49138ab 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -31,9 +31,6 @@
"java/**/*.java",
"java/**/*.aidl",
],
- exclude_srcs: [
- ":framework-wifi-non-updatable-sources"
- ],
path: "java",
visibility: ["//visibility:private"],
}
@@ -47,20 +44,6 @@
}
filegroup {
- name: "framework-wifi-non-updatable-sources",
- srcs: [
- // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
- // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
- // to a separate package.
- "java/android/net/wifi/SoftApConfToXmlMigrationUtil.java",
- "java/android/net/wifi/WifiNetworkScoreCache.java",
- "java/android/net/wifi/WifiMigration.java",
- "java/android/net/wifi/nl80211/*.java",
- ":libwificond_ipc_aidl",
- ],
-}
-
-filegroup {
name: "framework-wifi-annotations",
srcs: ["java/android/net/wifi/WifiAnnotations.java"],
}
@@ -68,8 +51,12 @@
// list of tests that are allowed to access @hide APIs from framework-wifi
test_access_hidden_api_whitelist = [
"//frameworks/base/wifi/tests",
+ "//frameworks/base/wifi/non-updatable/tests",
"//frameworks/opt/net/wifi/tests/wifitests:__subpackages__",
+ "//packages/modules/Wifi/framework/tests",
+ "//packages/modules/Wifi/service/tests/wifitests:__subpackages__",
+
"//external/robolectric-shadows:__subpackages__",
"//frameworks/base/packages/SettingsLib/tests/integ",
"//external/sl4a:__subpackages__",
diff --git a/wifi/non-updatable/java/Android.bp b/wifi/non-updatable/java/Android.bp
new file mode 100644
index 0000000..b35b4be
--- /dev/null
+++ b/wifi/non-updatable/java/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2020 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.
+
+// This directory contains framework Wifi APIs that are not part of the Wifi module (i.e. not
+// updatable), and are generally only called by the Wifi module.
+
+// necessary since we only want the `path` property to only refer to these files
+filegroup {
+ name: "framework-wifi-non-updatable-sources-internal",
+ srcs: ["src/**/*.java"],
+ path: "src",
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "framework-wifi-non-updatable-sources",
+ srcs: [
+ // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
+ // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
+ // to a separate package.
+ ":framework-wifi-non-updatable-sources-internal",
+ ":libwificond_ipc_aidl",
+ ],
+}
diff --git a/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
similarity index 100%
rename from wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java
rename to wifi/non-updatable/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
similarity index 98%
rename from wifi/java/android/net/wifi/WifiMigration.java
rename to wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
index 5792d27..4fabc0b 100755
--- a/wifi/java/android/net/wifi/WifiMigration.java
+++ b/wifi/non-updatable/java/src/android/net/wifi/WifiMigration.java
@@ -139,8 +139,8 @@
/**
* Load data from legacy shared wifi config store file.
* <p>
- * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/
- * java/android/net/wifi/migration_samples}.
+ * Expected AOSP format is available in the sample files under {@code
+ * frameworks/base/wifi/non-updatable/migration_samples/}.
* </p>
* <p>
* Note:
@@ -218,8 +218,8 @@
/**
* Load data from legacy user wifi config store file.
* <p>
- * Expected AOSP format is available in the sample files under {@code /frameworks/base/wifi/
- * java/android/net/wifi/migration_samples}.
+ * Expected AOSP format is available in the sample files under {@code
+ * frameworks/base/wifi/non-updatable/migration_samples/}.
* </p>
* <p>
* Note:
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
similarity index 98%
rename from wifi/java/android/net/wifi/WifiNetworkScoreCache.java
rename to wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
index 378549d..3903658 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/non-updatable/java/src/android/net/wifi/WifiNetworkScoreCache.java
@@ -89,7 +89,7 @@
@Override public final void updateScores(List<ScoredNetwork> networks) {
if (networks == null || networks.isEmpty()) {
- return;
+ return;
}
if (DBG) {
Log.d(TAG, "updateScores list size=" + networks.size());
@@ -97,7 +97,7 @@
boolean changed = false;
- synchronized(mLock) {
+ synchronized (mLock) {
for (ScoredNetwork network : networks) {
String networkKey = buildNetworkKey(network);
if (networkKey == null) {
@@ -189,7 +189,7 @@
String key = buildNetworkKey(result);
if (key == null) return null;
- synchronized(mLock) {
+ synchronized (mLock) {
ScoredNetwork network = mCache.get(key);
return network;
}
diff --git a/wifi/java/android/net/wifi/nl80211/ChannelSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/ChannelSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/ChannelSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/DeviceWiphyCapabilities.java
diff --git a/wifi/java/android/net/wifi/nl80211/HiddenNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/HiddenNetwork.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/HiddenNetwork.java
diff --git a/wifi/java/android/net/wifi/nl80211/NativeScanResult.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/NativeScanResult.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeScanResult.java
diff --git a/wifi/java/android/net/wifi/nl80211/NativeWifiClient.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/NativeWifiClient.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/NativeWifiClient.java
diff --git a/wifi/java/android/net/wifi/nl80211/PnoNetwork.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/PnoNetwork.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoNetwork.java
diff --git a/wifi/java/android/net/wifi/nl80211/PnoSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/PnoSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/PnoSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/RadioChainInfo.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/RadioChainInfo.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/RadioChainInfo.java
diff --git a/wifi/java/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/SingleScanSettings.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/SingleScanSettings.java
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
similarity index 100%
rename from wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
rename to wifi/non-updatable/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
diff --git a/wifi/java/android/net/wifi/migration_samples/README.txt b/wifi/non-updatable/migration_samples/README.txt
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/README.txt
rename to wifi/non-updatable/migration_samples/README.txt
diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStore.xml
rename to wifi/non-updatable/migration_samples/Shared_WifiConfigStore.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml b/wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/Shared_WifiConfigStoreSoftAp.xml
rename to wifi/non-updatable/migration_samples/Shared_WifiConfigStoreSoftAp.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStore.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/User_WifiConfigStore.xml
rename to wifi/non-updatable/migration_samples/User_WifiConfigStore.xml
diff --git a/wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml b/wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
similarity index 100%
rename from wifi/java/android/net/wifi/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
rename to wifi/non-updatable/migration_samples/User_WifiConfigStoreNetworkSuggestions.xml
diff --git a/wifi/non-updatable/tests/Android.bp b/wifi/non-updatable/tests/Android.bp
new file mode 100644
index 0000000..3f5cacf
--- /dev/null
+++ b/wifi/non-updatable/tests/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 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.
+
+android_test {
+ name: "FrameworksWifiNonUpdatableApiTests",
+
+ defaults: ["framework-wifi-test-defaults"],
+
+ srcs: ["src/**/*.java"],
+
+ jacoco: {
+ include_filter: ["android.net.wifi.*"],
+ // TODO(b/147521214) need to exclude test classes
+ exclude_filter: [],
+ },
+
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "guava",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/wifi/non-updatable/tests/AndroidManifest.xml b/wifi/non-updatable/tests/AndroidManifest.xml
new file mode 100644
index 0000000..b4b6b2d
--- /dev/null
+++ b/wifi/non-updatable/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.net.wifi.test">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:label="WifiTestDummyLabel"
+ android:name="WifiTestDummyName"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.net.wifi.test"
+ android:label="Frameworks Wifi Non-updatable API Tests">
+ </instrumentation>
+
+</manifest>
diff --git a/wifi/non-updatable/tests/AndroidTest.xml b/wifi/non-updatable/tests/AndroidTest.xml
new file mode 100644
index 0000000..5f3fdd4
--- /dev/null
+++ b/wifi/non-updatable/tests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<configuration description="Runs Frameworks Wifi Non-updatable API Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="FrameworksWifiNonUpdatableApiTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="FrameworksWifiNonUpdatableApiTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.net.wifi.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/wifi/non-updatable/tests/README.md b/wifi/non-updatable/tests/README.md
new file mode 100644
index 0000000..ad535f4
--- /dev/null
+++ b/wifi/non-updatable/tests/README.md
@@ -0,0 +1,32 @@
+# Wifi Non-Updatable Framework Unit Tests
+This package contains unit tests for the non-updatable part (i.e. outside the Wifi module) of the
+Android Wifi framework APIs based on the
+[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html).
+The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/)
+libraries.
+
+## Running Tests
+The easiest way to run tests is simply run
+
+```
+atest android.net.wifi
+```
+
+To pick up changes in framework/base, you will need to:
+1. rebuild the framework library 'make -j32'
+2. sync over the updated library to the device 'adb sync'
+3. restart framework on the device 'adb shell stop' then 'adb shell start'
+
+To enable syncing data to the device for first time after clean reflash:
+1. adb disable-verity
+2. adb reboot
+3. adb remount
+
+## Adding Tests
+Tests can be added by adding classes to the src directory. JUnit4 style test cases can
+be written by simply annotating test methods with `org.junit.Test`.
+
+## Debugging Tests
+If you are trying to debug why tests are not doing what you expected, you can add android log
+statements and use logcat to view them. The beginning and end of every tests is automatically logged
+with the tag `TestRunner`.
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java b/wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
similarity index 99%
rename from wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index fdd11a3..c4967eb 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/non-updatable/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.net.wifi;
diff --git a/wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/DeviceWiphyCapabilitiesTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/NativeScanResultTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/PnoSettingsTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
similarity index 100%
rename from wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
rename to wifi/non-updatable/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java