/* * Copyright (C) 2019 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 android.media; import android.annotation.NonNull; import android.media.AudioAttributes.AttributeUsage; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMixingRule; import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion; import android.media.projection.MediaProjection; import android.os.RemoteException; import com.android.internal.util.Preconditions; import java.util.function.ToIntFunction; /** * Configuration for capturing audio played by other apps. * * When capturing audio signals played by other apps (and yours), * you will only capture a mix of the audio signals played by players * (such as AudioTrack or MediaPlayer) which present the following characteristics: *
An example for creating a capture configuration for capturing all media playback: * *
* MediaProjection mediaProjection; * // Retrieve a audio capable projection from the MediaProjectionManager * AudioPlaybackCaptureConfiguration config = * new AudioPlaybackCaptureConfiguration.Builder(mediaProjection) * .addMatchingUsage(AudioAttributes.USAGE_MEDIA) * .build(); * AudioRecord record = new AudioRecord.Builder() * .setAudioPlaybackCaptureConfig(config) * .build(); ** * @see Builder * @see android.media.projection.MediaProjectionManager#getMediaProjection(int, Intent) * @see AudioRecord.Builder#setAudioPlaybackCaptureConfig(AudioPlaybackCaptureConfiguration) */ public final class AudioPlaybackCaptureConfiguration { private final AudioMixingRule mAudioMixingRule; private final MediaProjection mProjection; private AudioPlaybackCaptureConfiguration(AudioMixingRule audioMixingRule, MediaProjection projection) { mAudioMixingRule = audioMixingRule; mProjection = projection; } /** * @return the {@code MediaProjection} used to build this object. * @see Builder#Builder(MediaProjection) */ public @NonNull MediaProjection getMediaProjection() { return mProjection; } /** @return the usages passed to {@link Builder#addMatchingUsage(int)}. */ @AttributeUsage public @NonNull int[] getMatchingUsages() { return getIntPredicates(AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE, criterion -> criterion.getAudioAttributes().getUsage()); } /** @return the UIDs passed to {@link Builder#addMatchingUid(int)}. */ public @NonNull int[] getMatchingUids() { return getIntPredicates(AudioMixingRule.RULE_MATCH_UID, criterion -> criterion.getIntProp()); } /** @return the usages passed to {@link Builder#excludeUsage(int)}. */ @AttributeUsage public @NonNull int[] getExcludeUsages() { return getIntPredicates(AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE, criterion -> criterion.getAudioAttributes().getUsage()); } /** @return the UIDs passed to {@link Builder#excludeUid(int)}. */ public @NonNull int[] getExcludeUids() { return getIntPredicates(AudioMixingRule.RULE_EXCLUDE_UID, criterion -> criterion.getIntProp()); } private int[] getIntPredicates(int rule, ToIntFunction
If called multiple times, will capture audio output that matches any of the given * attributes. * * @throws IllegalStateException if called in conjunction with * {@link #excludeUsage(int)}. */ public @NonNull Builder addMatchingUsage(@AttributeUsage int usage) { Preconditions.checkState( mUsageMatchType != MATCH_TYPE_EXCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); mAudioMixingRuleBuilder.addRule(new AudioAttributes.Builder().setUsage(usage).build(), AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE); mUsageMatchType = MATCH_TYPE_INCLUSIVE; return this; } /** * Only capture audio output by app with the matching {@code uid}. * *
If called multiple times, will capture audio output by apps whose uid is any of the * given uids. * * @throws IllegalStateException if called in conjunction with {@link #excludeUid(int)}. */ public @NonNull Builder addMatchingUid(int uid) { Preconditions.checkState( mUidMatchType != MATCH_TYPE_EXCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); mAudioMixingRuleBuilder.addMixRule(AudioMixingRule.RULE_MATCH_UID, uid); mUidMatchType = MATCH_TYPE_INCLUSIVE; return this; } /** * Only capture audio output that does not match the given {@link AudioAttributes}. * *
If called multiple times, will capture audio output that does not match any of the * given attributes. * * @throws IllegalStateException if called in conjunction with * {@link #addMatchingUsage(int)}. */ public @NonNull Builder excludeUsage(@AttributeUsage int usage) { Preconditions.checkState( mUsageMatchType != MATCH_TYPE_INCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); mAudioMixingRuleBuilder.excludeRule(new AudioAttributes.Builder() .setUsage(usage) .build(), AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE); mUsageMatchType = MATCH_TYPE_EXCLUSIVE; return this; } /** * Only capture audio output by apps that do not have the matching {@code uid}. * *
If called multiple times, will capture audio output by apps whose uid is not any of * the given uids. * * @throws IllegalStateException if called in conjunction with {@link #addMatchingUid(int)}. */ public @NonNull Builder excludeUid(int uid) { Preconditions.checkState( mUidMatchType != MATCH_TYPE_INCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); mAudioMixingRuleBuilder.excludeMixRule(AudioMixingRule.RULE_MATCH_UID, uid); mUidMatchType = MATCH_TYPE_EXCLUSIVE; return this; } /** * Builds the configuration instance. * * @throws UnsupportedOperationException if the parameters set are incompatible. */ public @NonNull AudioPlaybackCaptureConfiguration build() { return new AudioPlaybackCaptureConfiguration(mAudioMixingRuleBuilder.build(), mProjection); } } }