summaryrefslogtreecommitdiff
path: root/android/media/audiopolicy
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2018-04-03 23:21:57 -0400
committerJustin Klaassen <justinklaassen@google.com>2018-04-03 23:21:57 -0400
commit4d01eeaffaa720e4458a118baa137a11614f00f7 (patch)
tree66751893566986236788e3c796a7cc5e90d05f52 /android/media/audiopolicy
parenta192cc2a132cb0ee8588e2df755563ec7008c179 (diff)
downloadandroid-28-4d01eeaffaa720e4458a118baa137a11614f00f7.tar.gz
Import Android SDK Platform P [4697573]
/google/data/ro/projects/android/fetch_artifact \ --bid 4697573 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4697573.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077
Diffstat (limited to 'android/media/audiopolicy')
-rw-r--r--android/media/audiopolicy/AudioMix.java18
-rw-r--r--android/media/audiopolicy/AudioMixingRule.java31
-rw-r--r--android/media/audiopolicy/AudioPolicy.java152
-rw-r--r--android/media/audiopolicy/AudioPolicyConfig.java53
4 files changed, 235 insertions, 19 deletions
diff --git a/android/media/audiopolicy/AudioMix.java b/android/media/audiopolicy/AudioMix.java
index adeb8348..fca0cc73 100644
--- a/android/media/audiopolicy/AudioMix.java
+++ b/android/media/audiopolicy/AudioMix.java
@@ -162,6 +162,24 @@ public class AudioMix {
}
/** @hide */
+ public boolean isAffectingUsage(int usage) {
+ return mRule.isAffectingUsage(usage);
+ }
+
+ /** @hide */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final AudioMix that = (AudioMix) o;
+ return (this.mRouteFlags == that.mRouteFlags)
+ && (this.mRule == that.mRule)
+ && (this.mMixType == that.mMixType)
+ && (this.mFormat == that.mFormat);
+ }
+
+ /** @hide */
@Override
public int hashCode() {
return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
diff --git a/android/media/audiopolicy/AudioMixingRule.java b/android/media/audiopolicy/AudioMixingRule.java
index 5f127421..749a45e3 100644
--- a/android/media/audiopolicy/AudioMixingRule.java
+++ b/android/media/audiopolicy/AudioMixingRule.java
@@ -135,11 +135,42 @@ public class AudioMixingRule {
}
}
+ boolean isAffectingUsage(int usage) {
+ for (AudioMixMatchCriterion criterion : mCriteria) {
+ if ((criterion.mRule & RULE_MATCH_ATTRIBUTE_USAGE) != 0
+ && criterion.mAttr != null
+ && criterion.mAttr.getUsage() == usage) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1,
+ ArrayList<AudioMixMatchCriterion> cr2) {
+ if (cr1 == null || cr2 == null) return false;
+ if (cr1 == cr2) return true;
+ if (cr1.size() != cr2.size()) return false;
+ //TODO iterate over rules to check they contain the same criterion
+ return (cr1.hashCode() == cr2.hashCode());
+ }
+
private final int mTargetMixType;
int getTargetMixType() { return mTargetMixType; }
private final ArrayList<AudioMixMatchCriterion> mCriteria;
ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
+ /** @hide */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final AudioMixingRule that = (AudioMixingRule) o;
+ return (this.mTargetMixType == that.mTargetMixType)
+ && (areCriteriaEquivalent(this.mCriteria, that.mCriteria));
+ }
+
@Override
public int hashCode() {
return Objects.hash(mTargetMixType, mCriteria);
diff --git a/android/media/audiopolicy/AudioPolicy.java b/android/media/audiopolicy/AudioPolicy.java
index 7e88c277..11107e2d 100644
--- a/android/media/audiopolicy/AudioPolicy.java
+++ b/android/media/audiopolicy/AudioPolicy.java
@@ -42,6 +42,7 @@ import android.util.Slog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.List;
/**
* @hide
@@ -89,6 +90,8 @@ public class AudioPolicy {
private AudioPolicyFocusListener mFocusListener;
+ private final AudioPolicyVolumeCallback mVolCb;
+
private Context mContext;
private AudioPolicyConfig mConfig;
@@ -99,12 +102,15 @@ public class AudioPolicy {
public boolean hasFocusListener() { return mFocusListener != null; }
/** @hide */
public boolean isFocusPolicy() { return mIsFocusPolicy; }
+ /** @hide */
+ public boolean isVolumeController() { return mVolCb != null; }
/**
* The parameter is guaranteed non-null through the Builder
*/
private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
- AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) {
+ AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy,
+ AudioPolicyVolumeCallback vc) {
mConfig = config;
mStatus = POLICY_STATUS_UNREGISTERED;
mContext = context;
@@ -120,6 +126,7 @@ public class AudioPolicy {
mFocusListener = fl;
mStatusListener = sl;
mIsFocusPolicy = isFocusPolicy;
+ mVolCb = vc;
}
/**
@@ -134,6 +141,7 @@ public class AudioPolicy {
private AudioPolicyFocusListener mFocusListener;
private AudioPolicyStatusListener mStatusListener;
private boolean mIsFocusPolicy = false;
+ private AudioPolicyVolumeCallback mVolCb;
/**
* Constructs a new Builder with no audio mixes.
@@ -208,6 +216,22 @@ public class AudioPolicy {
mStatusListener = l;
}
+ @SystemApi
+ /**
+ * Sets the callback to receive all volume key-related events.
+ * The callback will only be called if the device is configured to handle volume events
+ * in the PhoneWindowManager (see config_handleVolumeKeysInWindowManager)
+ * @param vc
+ * @return the same Builder instance.
+ */
+ public Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) {
+ if (vc == null) {
+ throw new IllegalArgumentException("Invalid null volume callback");
+ }
+ mVolCb = vc;
+ return this;
+ }
+
/**
* Combines all of the attributes that have been set on this {@code Builder} and returns a
* new {@link AudioPolicy} object.
@@ -229,7 +253,90 @@ public class AudioPolicy {
+ "an AudioPolicyFocusListener");
}
return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
- mFocusListener, mStatusListener, mIsFocusPolicy);
+ mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb);
+ }
+ }
+
+ /**
+ * @hide
+ * Update the current configuration of the set of audio mixes by adding new ones, while
+ * keeping the policy registered.
+ * This method can only be called on a registered policy.
+ * @param mixes the list of {@link AudioMix} to add
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int attachMixes(@NonNull List<AudioMix> mixes) {
+ if (mixes == null) {
+ throw new IllegalArgumentException("Illegal null list of AudioMix");
+ }
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
+ }
+ final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
+ for (AudioMix mix : mixes) {
+ if (mix == null) {
+ throw new IllegalArgumentException("Illegal null AudioMix in attachMixes");
+ } else {
+ zeMixes.add(mix);
+ }
+ }
+ final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
+ IAudioService service = getService();
+ try {
+ final int status = service.addMixForPolicy(cfg, this.cb());
+ if (status == AudioManager.SUCCESS) {
+ mConfig.add(zeMixes);
+ }
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in attachMixes", e);
+ return AudioManager.ERROR;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Update the current configuration of the set of audio mixes by removing some, while
+ * keeping the policy registered.
+ * This method can only be called on a registered policy.
+ * @param mixes the list of {@link AudioMix} to remove
+ * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+ * otherwise.
+ */
+ @SystemApi
+ public int detachMixes(@NonNull List<AudioMix> mixes) {
+ if (mixes == null) {
+ throw new IllegalArgumentException("Illegal null list of AudioMix");
+ }
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
+ }
+ final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
+ for (AudioMix mix : mixes) {
+ if (mix == null) {
+ throw new IllegalArgumentException("Illegal null AudioMix in detachMixes");
+ // TODO also check mix is currently contained in list of mixes
+ } else {
+ zeMixes.add(mix);
+ }
+ }
+ final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
+ IAudioService service = getService();
+ try {
+ final int status = service.removeMixForPolicy(cfg, this.cb());
+ if (status == AudioManager.SUCCESS) {
+ mConfig.remove(zeMixes);
+ }
+ return status;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in detachMixes", e);
+ return AudioManager.ERROR;
+ }
}
}
@@ -377,6 +484,7 @@ public class AudioPolicy {
new AudioAttributes.Builder()
.setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
.addTag(addressForTag(mix))
+ .addTag(AudioRecord.SUBMIX_FIXED_VOLUME)
.build(),
mixFormat,
AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
@@ -440,9 +548,9 @@ public class AudioPolicy {
* Only ever called if the {@link AudioPolicy} was built with
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
* @param afi information about the focus request and the requester
- * @param requestResult the result that was returned synchronously by the framework to the
- * application, {@link #AUDIOFOCUS_REQUEST_FAILED},or
- * {@link #AUDIOFOCUS_REQUEST_DELAYED}.
+ * @param requestResult deprecated after the addition of
+ * {@link AudioManager#setFocusRequestResult(AudioFocusInfo, int, AudioPolicy)}
+ * in Android P, always equal to {@link #AUDIOFOCUS_REQUEST_GRANTED}.
*/
public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
/**
@@ -455,6 +563,23 @@ public class AudioPolicy {
public void onAudioFocusAbandon(AudioFocusInfo afi) {}
}
+ @SystemApi
+ /**
+ * Callback class to receive volume change-related events.
+ * See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the
+ * {@link AudioPolicy} to receive those events.
+ *
+ */
+ public static abstract class AudioPolicyVolumeCallback {
+ /** @hide */
+ public AudioPolicyVolumeCallback() {}
+ /**
+ * Called when volume key-related changes are triggered, on the key down event.
+ * @param adjustment the type of volume adjustment for the key.
+ */
+ public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {}
+ }
+
private void onPolicyStatusChange() {
AudioPolicyStatusListener l;
synchronized (mLock) {
@@ -494,7 +619,7 @@ public class AudioPolicy {
sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
if (DEBUG) {
Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
- + afi.getClientId() + "reqRes=" + requestResult);
+ + afi.getClientId() + " gen=" + afi.getGen());
}
}
@@ -517,6 +642,13 @@ public class AudioPolicy {
}
}
}
+
+ public void notifyVolumeAdjust(int adjustment) {
+ sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);
+ if (DEBUG) {
+ Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
+ }
+ }
};
//==================================================
@@ -528,6 +660,7 @@ public class AudioPolicy {
private final static int MSG_MIX_STATE_UPDATE = 3;
private final static int MSG_FOCUS_REQUEST = 4;
private final static int MSG_FOCUS_ABANDON = 5;
+ private final static int MSG_VOL_ADJUST = 6;
private class EventHandler extends Handler {
public EventHandler(AudioPolicy ap, Looper looper) {
@@ -571,6 +704,13 @@ public class AudioPolicy {
Log.e(TAG, "Invalid null focus listener for focus abandon event");
}
break;
+ case MSG_VOL_ADJUST:
+ if (mVolCb != null) {
+ mVolCb.onVolumeAdjustment(msg.arg1);
+ } else { // should never be null, but don't crash
+ Log.e(TAG, "Invalid null volume event");
+ }
+ break;
default:
Log.e(TAG, "Unknown event " + msg.what);
}
diff --git a/android/media/audiopolicy/AudioPolicyConfig.java b/android/media/audiopolicy/AudioPolicyConfig.java
index cafa5a8c..f725cacf 100644
--- a/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/android/media/audiopolicy/AudioPolicyConfig.java
@@ -16,6 +16,7 @@
package android.media.audiopolicy;
+import android.annotation.NonNull;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioPatch;
@@ -24,6 +25,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.Objects;
@@ -35,11 +38,16 @@ public class AudioPolicyConfig implements Parcelable {
private static final String TAG = "AudioPolicyConfig";
- protected ArrayList<AudioMix> mMixes;
+ protected final ArrayList<AudioMix> mMixes;
protected int mDuckingPolicy = AudioPolicy.FOCUS_POLICY_DUCKING_IN_APP;
private String mRegistrationId = null;
+ /** counter for the mixes that are / have been in the list of AudioMix
+ * e.g. register 4 mixes (counter is 3), remove 1 (counter is 3), add 1 (counter is 4)
+ */
+ private int mMixCounter = 0;
+
protected AudioPolicyConfig(AudioPolicyConfig conf) {
mMixes = conf.mMixes;
}
@@ -201,20 +209,39 @@ public class AudioPolicyConfig implements Parcelable {
return;
}
mRegistrationId = regId == null ? "" : regId;
- int mixIndex = 0;
for (AudioMix mix : mMixes) {
- if (!mRegistrationId.isEmpty()) {
- if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
- AudioMix.ROUTE_FLAG_LOOP_BACK) {
- mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
- + mixIndex++);
- } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
- AudioMix.ROUTE_FLAG_RENDER) {
- mix.setRegistration(mix.mDeviceAddress);
- }
- } else {
- mix.setRegistration("");
+ setMixRegistration(mix);
+ }
+ }
+
+ private void setMixRegistration(@NonNull final AudioMix mix) {
+ if (!mRegistrationId.isEmpty()) {
+ if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
+ AudioMix.ROUTE_FLAG_LOOP_BACK) {
+ mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
+ + mMixCounter);
+ } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
+ AudioMix.ROUTE_FLAG_RENDER) {
+ mix.setRegistration(mix.mDeviceAddress);
}
+ } else {
+ mix.setRegistration("");
+ }
+ mMixCounter++;
+ }
+
+ @GuardedBy("mMixes")
+ protected void add(@NonNull ArrayList<AudioMix> mixes) {
+ for (AudioMix mix : mixes) {
+ setMixRegistration(mix);
+ mMixes.add(mix);
+ }
+ }
+
+ @GuardedBy("mMixes")
+ protected void remove(@NonNull ArrayList<AudioMix> mixes) {
+ for (AudioMix mix : mixes) {
+ mMixes.remove(mix);
}
}