• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.audio;
18 
19 import static com.android.car.audio.CarAudioService.SystemClockWrapper;
20 import static com.android.car.audio.CarAudioUtils.hasExpired;
21 
22 import android.annotation.NonNull;
23 import android.media.AudioAttributes;
24 import android.media.AudioManager;
25 import android.media.AudioPlaybackConfiguration;
26 import android.util.ArrayMap;
27 
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.util.Preconditions;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 
36 final class CarAudioPlaybackCallback extends AudioManager.AudioPlaybackCallback {
37     private final Object mLock = new Object();
38     @GuardedBy("mLock")
39     private final ArrayMap<AudioAttributes, Long> mAudioAttributesStartTime = new ArrayMap<>();
40     @GuardedBy("mLock")
41     private final ArrayMap<String, AudioPlaybackConfiguration> mLastActiveConfigs =
42             new ArrayMap<>();
43     private final CarAudioZone mCarPrimaryAudioZone;
44     private final SystemClockWrapper mClock;
45     private final int mVolumeKeyEventTimeoutMs;
46 
CarAudioPlaybackCallback(@onNull CarAudioZone carPrimaryAudioZone, @NonNull SystemClockWrapper clock, int volumeKeyEventTimeoutMs)47     CarAudioPlaybackCallback(@NonNull CarAudioZone carPrimaryAudioZone,
48             @NonNull SystemClockWrapper clock,
49             int volumeKeyEventTimeoutMs) {
50         mCarPrimaryAudioZone = Objects.requireNonNull(carPrimaryAudioZone);
51         mClock = Objects.requireNonNull(clock);
52         mVolumeKeyEventTimeoutMs = Preconditions.checkArgumentNonnegative(volumeKeyEventTimeoutMs);
53     }
54 
55     @Override
onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configurations)56     public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configurations) {
57         ArrayMap<String, AudioPlaybackConfiguration> newActiveConfigs =
58                 filterNewActiveConfiguration(configurations);
59 
60         synchronized (mLock) {
61             List<AudioPlaybackConfiguration> newlyInactiveConfigurations =
62                     getNewlyInactiveConfigurationsLocked(newActiveConfigs);
63 
64             mLastActiveConfigs.clear();
65             mLastActiveConfigs.putAll(newActiveConfigs);
66 
67             startTimersForContextThatBecameInactiveLocked(newlyInactiveConfigurations);
68         }
69     }
70 
71     /**
72      * Returns all active contexts for the primary zone
73      * @return all active audio contexts, including those that recently became inactive but are
74      * considered active due to the audio playback timeout.
75      */
getAllActiveAudioAttributesForPrimaryZone()76     public List<AudioAttributes> getAllActiveAudioAttributesForPrimaryZone() {
77         synchronized (mLock) {
78             List<AudioAttributes> activeContexts = getCurrentlyActiveAttributesLocked();
79             activeContexts
80                     .addAll(getStillActiveContextAndRemoveExpiredContextsLocked());
81             return activeContexts;
82         }
83     }
84 
85     @GuardedBy("mLock")
startTimersForContextThatBecameInactiveLocked( List<AudioPlaybackConfiguration> inactiveConfigs)86     private void startTimersForContextThatBecameInactiveLocked(
87             List<AudioPlaybackConfiguration> inactiveConfigs) {
88         List<AudioAttributes> activeAttributes = mCarPrimaryAudioZone
89                 .findActiveAudioAttributesFromPlaybackConfigurations(inactiveConfigs);
90 
91         for (int index = 0; index < inactiveConfigs.size(); index++) {
92             mAudioAttributesStartTime.put(activeAttributes.get(index), mClock.uptimeMillis());
93         }
94     }
95 
96     @GuardedBy("mLock")
getNewlyInactiveConfigurationsLocked( Map<String, AudioPlaybackConfiguration> newActiveConfigurations)97     private List<AudioPlaybackConfiguration> getNewlyInactiveConfigurationsLocked(
98             Map<String, AudioPlaybackConfiguration> newActiveConfigurations) {
99         List<AudioPlaybackConfiguration> newlyInactiveConfigurations = new ArrayList<>();
100         for (int index = 0; index < mLastActiveConfigs.size(); index++) {
101             if (newActiveConfigurations
102                     .containsKey(mLastActiveConfigs.keyAt(index))) {
103                 continue;
104             }
105             newlyInactiveConfigurations.add(mLastActiveConfigs.valueAt(index));
106         }
107         return newlyInactiveConfigurations;
108     }
109 
filterNewActiveConfiguration( List<AudioPlaybackConfiguration> configurations)110     private ArrayMap<String, AudioPlaybackConfiguration> filterNewActiveConfiguration(
111             List<AudioPlaybackConfiguration> configurations) {
112         ArrayMap<String, AudioPlaybackConfiguration> newActiveConfigs = new ArrayMap<>();
113         for (int index = 0; index < configurations.size(); index++) {
114             AudioPlaybackConfiguration configuration = configurations.get(index);
115             if (!configuration.isActive()) {
116                 continue;
117             }
118             if (mCarPrimaryAudioZone
119                     .isAudioDeviceInfoValidForZone(configuration.getAudioDeviceInfo())) {
120                 newActiveConfigs.put(
121                         configuration.getAudioDeviceInfo().getAddress(), configuration);
122             }
123         }
124         return newActiveConfigs;
125     }
126 
127     @GuardedBy("mLock")
getCurrentlyActiveAttributesLocked()128     private List<AudioAttributes> getCurrentlyActiveAttributesLocked() {
129         return mCarPrimaryAudioZone.findActiveAudioAttributesFromPlaybackConfigurations(
130                 new ArrayList<>(mLastActiveConfigs.values()));
131     }
132 
133     @GuardedBy("mLock")
getStillActiveContextAndRemoveExpiredContextsLocked()134     private List<AudioAttributes> getStillActiveContextAndRemoveExpiredContextsLocked() {
135         List<AudioAttributes> attributesToRemove = new ArrayList<>();
136         List<AudioAttributes> activeAttributes = new ArrayList<>();
137         for (int index = 0; index < mAudioAttributesStartTime.size(); index++) {
138             long startTime = mAudioAttributesStartTime.valueAt(index);
139             if (hasExpired(startTime, mClock.uptimeMillis(), mVolumeKeyEventTimeoutMs)) {
140                 attributesToRemove.add(mAudioAttributesStartTime.keyAt(index));
141                 continue;
142             }
143             activeAttributes.add(mAudioAttributesStartTime.keyAt(index));
144         }
145 
146         for (int indexToRemove = 0; indexToRemove < attributesToRemove.size(); indexToRemove++) {
147             mAudioAttributesStartTime.remove(attributesToRemove.get(indexToRemove));
148         }
149         return activeAttributes;
150     }
151 
resetStillActiveContexts()152     void resetStillActiveContexts() {
153         synchronized (mLock) {
154             mAudioAttributesStartTime.clear();
155         }
156     }
157 }
158