/* * Copyright (C) 2018 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.car.audio; import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.car.media.CarAudioManager; import android.content.pm.PackageManager; import android.media.AudioAttributes; import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.audiopolicy.AudioPolicy; import android.os.Bundle; import android.util.IndentingPrintWriter; import android.util.SparseArray; import com.android.car.CarLog; import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.utils.Slogf; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * Implements {@link AudioPolicy.AudioPolicyFocusListener} * *
Note: Manages audio focus on a per zone basis.
*/
final class CarZonesAudioFocus extends AudioPolicy.AudioPolicyFocusListener {
private static final String TAG = CarLog.tagFor(CarZonesAudioFocus.class);
private final CarFocusCallback mCarFocusCallback;
private final boolean mDelayedFocusEnabled;
private CarAudioService mCarAudioService; // Dynamically assigned just after construction
private AudioPolicy mAudioPolicy; // Dynamically assigned just after construction
private final SparseArray Note: This has to happen after the construction to avoid a chicken and egg
* problem when setting up the AudioPolicy which must depend on this object.
* @param carAudioService owning car audio service
* @param parentPolicy owning parent car audio policy
*/
void setOwningPolicy(CarAudioService carAudioService, AudioPolicy parentPolicy) {
mAudioPolicy = parentPolicy;
mCarAudioService = carAudioService;
for (int i = 0; i < mFocusZones.size(); i++) {
mFocusZones.valueAt(i).setOwningPolicy(mAudioPolicy);
}
}
void setRestrictFocus(boolean isFocusRestricted) {
int[] zoneIds = new int[mFocusZones.size()];
for (int i = 0; i < mFocusZones.size(); i++) {
zoneIds[i] = mFocusZones.keyAt(i);
mFocusZones.valueAt(i).setRestrictFocus(isFocusRestricted);
}
notifyFocusCallback(zoneIds);
}
@Override
public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
int zoneId = getAudioZoneIdForAudioFocusInfo(afi);
getCarAudioFocusForZoneId(zoneId).onAudioFocusRequest(afi, requestResult);
notifyFocusCallback(new int[]{zoneId});
}
/**
* @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
* Note that we'll get this call for a focus holder that dies while in the focus stack, so
* we don't need to watch for death notifications directly.
*/
@Override
public void onAudioFocusAbandon(AudioFocusInfo afi) {
int zoneId = getAudioZoneIdForAudioFocusInfo(afi);
getCarAudioFocusForZoneId(zoneId).onAudioFocusAbandon(afi);
notifyFocusCallback(new int[]{zoneId});
}
@NonNull
private CarAudioFocus getCarAudioFocusForZoneId(int zoneId) {
return mFocusZones.get(zoneId);
}
private int getAudioZoneIdForAudioFocusInfo(AudioFocusInfo afi) {
int zoneId = mCarAudioService.getZoneIdForUid(afi.getClientUid());
// If the bundle attribute for AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID has been assigned
// Use zone id from that instead.
Bundle bundle = afi.getAttributes().getBundle();
if (bundle != null) {
int bundleZoneId =
bundle.getInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID,
-1);
// check if the zone id is within current zones bounds
if (mCarAudioService.isAudioZoneIdValid(bundleZoneId)) {
Slogf.d(TAG, "getFocusForAudioFocusInfo valid zoneId %d with bundle request for"
+ " client %s", bundleZoneId, afi.getClientId());
zoneId = bundleZoneId;
} else {
Slogf.w(TAG, "getFocusForAudioFocusInfo invalid zoneId %d with bundle request for "
+ "client %s, dispatching focus request to zoneId %d", bundleZoneId,
afi.getClientId(), zoneId);
}
}
return zoneId;
}
private void notifyFocusCallback(int[] zoneIds) {
if (mCarFocusCallback == null) {
return;
}
SparseArray> focusHoldersByZoneId = new SparseArray<>();
for (int i = 0; i < zoneIds.length; i++) {
int zoneId = zoneIds[i];
List
> focusHoldersByZoneId);
}
}