• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.car.oem;
18 
19 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_FAILED;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.car.annotation.ApiRequirements;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.util.Preconditions;
30 
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Objects;
34 
35 /**
36  * Class to encapsulate the audio focus result from the OEM audio service
37  *
38  * @hide
39  */
40 @SystemApi
41 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
42         minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
43 public final class OemCarAudioFocusResult implements Parcelable {
44     private final @Nullable AudioFocusEntry mAudioFocusEntry;
45     private final @NonNull List<AudioFocusEntry> mNewlyLostAudioFocusEntries;
46     private final @NonNull List<AudioFocusEntry> mNewlyBlockedAudioFocusEntries;
47     private final int mAudioFocusResult;
48 
OemCarAudioFocusResult( @ullable AudioFocusEntry audioFocusEntry, @NonNull List<AudioFocusEntry> newlyLostAudioFocusEntries, @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries, int audioFocusResult)49     OemCarAudioFocusResult(
50             @Nullable AudioFocusEntry audioFocusEntry,
51             @NonNull List<AudioFocusEntry> newlyLostAudioFocusEntries,
52             @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries, int audioFocusResult) {
53         Preconditions.checkArgument(newlyLostAudioFocusEntries != null,
54                 "Newly lost focus entries can not be null");
55         Preconditions.checkArgument(newlyBlockedAudioFocusEntries != null,
56                 "Newly blocked focus entries can not be null");
57         this.mAudioFocusEntry = audioFocusEntry;
58         this.mNewlyLostAudioFocusEntries = newlyLostAudioFocusEntries;
59         this.mNewlyBlockedAudioFocusEntries = newlyBlockedAudioFocusEntries;
60         this.mAudioFocusResult = audioFocusResult;
61     }
62 
63     /**
64      * Returns the result of the focus request
65      * The result can be granted, delayed, or failed. In the case of granted the car audio stack
66      * will be changed according to the entries returned in newly lost and newly blocked.
67      * For delayed results the entry will be added as the current delayed request and it will be
68      * re-evaluated once any of the current focus holders abandons focus. For failed request,
69      * the car audio focus stack will not change and the current request will not gain focus.
70      */
71     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
72             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
getAudioFocusEntry()73     public @Nullable AudioFocusEntry getAudioFocusEntry() {
74         return new AudioFocusEntry.Builder(mAudioFocusEntry).build();
75     }
76 
77     /**
78      * Returns the entries that were previously holding focus but now have lost focus.
79      *
80      * <p>Note: the lost can be permanent or transient, in the case of permanent loss the entry
81      * will receive permanent focus loss and it will be removed from the car audio focus stack.
82      * For transient losses, the new entry will be added as a blocker but will only receive
83      * transient focus loss.
84      */
85     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
86             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
getNewlyLostAudioFocusEntries()87     public @NonNull List<AudioFocusEntry> getNewlyLostAudioFocusEntries() {
88         return new ArrayList<>(mNewlyLostAudioFocusEntries);
89     }
90 
91     /**
92      * Returns the entries that had previously lost focus and continue to be blocked by new entry
93      *
94      * <p>Note: the block can be permanent or transient, in the case of permanent block the entry
95      * will receive permanent focus loss and it will be removed from the car audio focus stack.
96      * For transient losses, the new entry will be added as a blocker but will only receive
97      * transient focus loss.
98      */
99     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
100             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
getNewlyBlockedAudioFocusEntries()101     public @NonNull List<AudioFocusEntry> getNewlyBlockedAudioFocusEntries() {
102         return new ArrayList<>(mNewlyBlockedAudioFocusEntries);
103     }
104 
105     /**
106      * Returns the focus results, must be on of {@link AudioManager.AUDIOFOCUS_GAIN},
107      * {@link AudioManager.AUDIOFOCUS_LOSS}, {@link AudioManager.AUDIOFOCUS_LOSS_TRANSIENT},
108      * {@link AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}
109      */
110     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
111             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
getAudioFocusResult()112     public int getAudioFocusResult() {
113         return mAudioFocusResult;
114     }
115 
116     @Override
toString()117     public String toString() {
118         return new StringBuilder().append("OemCarAudioFocusResult { audioFocusEntry = ")
119                 .append(mAudioFocusEntry)
120                 .append(", mNewlyLostAudioFocusEntries = ").append(mNewlyLostAudioFocusEntries)
121                 .append(", mNewlyBlockedAudioFocusEntries = ")
122                 .append(mNewlyBlockedAudioFocusEntries)
123                 .append(", mAudioFocusResult = ").append(mAudioFocusResult)
124                 .append(" }").toString();
125     }
126 
127     @Override
128     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
129             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
writeToParcel(@onNull Parcel dest, int flags)130     public void writeToParcel(@NonNull Parcel dest, int flags) {
131         byte flg = 0;
132         if (mAudioFocusEntry != null) {
133             flg = (byte) (flg | Builder.FOCUS_ENTRY_FIELDS_SET);
134         }
135         dest.writeByte(flg);
136         if (mAudioFocusEntry != null) {
137             mAudioFocusEntry.writeToParcel(dest, flags);
138         }
139         dest.writeParcelableList(mNewlyLostAudioFocusEntries, flags);
140         dest.writeParcelableList(mNewlyBlockedAudioFocusEntries, flags);
141         dest.writeInt(mAudioFocusResult);
142     }
143 
144     // TODO(b/260757994): Remove ApiRequirements for overridden methods
145     @Override
146     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
147             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
describeContents()148     public int describeContents() {
149         return 0;
150     }
151 
152     /** @hide */
153     @SuppressWarnings({"unchecked", "RedundantCast"})
154     @VisibleForTesting
OemCarAudioFocusResult(@onNull Parcel in)155     public OemCarAudioFocusResult(@NonNull Parcel in) {
156         byte flg = in.readByte();
157         AudioFocusEntry audioFocusEntry = (flg & Builder.FOCUS_ENTRY_FIELDS_SET) == 0
158                 ? null : AudioFocusEntry.CREATOR.createFromParcel(in);
159         List<AudioFocusEntry> audioFocusLosers = new ArrayList<>();
160         in.readParcelableList(audioFocusLosers, AudioFocusEntry.class.getClassLoader(),
161                 AudioFocusEntry.class);
162         List<AudioFocusEntry> audioFocusBlocked = new ArrayList<>();
163         in.readParcelableList(audioFocusBlocked, AudioFocusEntry.class.getClassLoader(),
164                 AudioFocusEntry.class);
165         int audioFocusResult = in.readInt();
166 
167         this.mAudioFocusEntry = audioFocusEntry;
168         this.mNewlyLostAudioFocusEntries = audioFocusLosers;
169         this.mNewlyBlockedAudioFocusEntries = audioFocusBlocked;
170         this.mAudioFocusResult = audioFocusResult;
171     }
172 
173     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
174             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
175     @NonNull
176     public static final OemCarAudioFocusResult EMPTY_OEM_CAR_AUDIO_FOCUS_RESULTS =
177             new OemCarAudioFocusResult(null,
178                     /* newlyLostAudioFocusEntries= */ new ArrayList<>(/* initialCapacity= */ 0),
179                     /* newlyBlockedAudioFocusEntries= */ new ArrayList<>(/* initialCapacity= */ 0),
180                     AUDIOFOCUS_REQUEST_FAILED);
181 
182     @Override
equals(Object o)183     public boolean equals(Object o) {
184         if (this == o) {
185             return true;
186         }
187 
188         if (!(o instanceof OemCarAudioFocusResult)) {
189             return false;
190         }
191 
192         OemCarAudioFocusResult that = (OemCarAudioFocusResult) o;
193 
194         return Objects.equals(mAudioFocusEntry, that.mAudioFocusEntry)
195                 && mAudioFocusResult == that.mAudioFocusResult
196                 && mNewlyBlockedAudioFocusEntries.equals(
197                 that.mNewlyBlockedAudioFocusEntries)
198                 && mNewlyLostAudioFocusEntries.equals(that.mNewlyLostAudioFocusEntries);
199     }
200 
201     @Override
hashCode()202     public int hashCode() {
203         return Objects.hash(mAudioFocusEntry, mAudioFocusResult,
204                 mNewlyBlockedAudioFocusEntries, mNewlyLostAudioFocusEntries);
205     }
206 
207     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
208             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
209     @NonNull
210     public static final Parcelable.Creator<OemCarAudioFocusResult> CREATOR =
211             new Parcelable.Creator<OemCarAudioFocusResult>() {
212         @Override
213         public OemCarAudioFocusResult[] newArray(int size) {
214             return new OemCarAudioFocusResult[size];
215         }
216 
217         @Override
218         public OemCarAudioFocusResult createFromParcel(@NonNull Parcel in) {
219             return new OemCarAudioFocusResult(in);
220         }
221     };
222 
223     /**
224      * A builder for {@link OemCarAudioFocusResult}
225      */
226     @SuppressWarnings("WeakerAccess")
227     public static final class Builder {
228 
229         private static final int FOCUS_ENTRY_FIELDS_SET = 0x1;
230         private static final int NEWLY_LOSS_FIELDS_SET = 0x2;
231         private static final int NEWLY_BLOCKED_FIELDS_SET = 0x4;
232         private static final int FOCUS_RESULT_FIELDS_SET = 0x8;
233         private static final int BUILDER_USED_FIELDS_SET = 0x10;
234         private @Nullable AudioFocusEntry mAudioFocusEntry;
235         private @NonNull List<AudioFocusEntry> mNewlyLostAudioFocusEntries;
236         private @NonNull List<AudioFocusEntry> mNewlyBlockedAudioFocusEntries;
237         private int mAudioFocusResult;
238 
239         private long mBuilderFieldsSet = 0L;
240 
Builder( @onNull List<AudioFocusEntry> newlyLostAudioFocusEntries, @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries, int audioFocusResult)241         public Builder(
242                 @NonNull List<AudioFocusEntry> newlyLostAudioFocusEntries,
243                 @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries,
244                 int audioFocusResult) {
245             Preconditions.checkArgument(newlyLostAudioFocusEntries != null,
246                     "Newly lost focus entries can not be null");
247             Preconditions.checkArgument(newlyBlockedAudioFocusEntries != null,
248                     "Newly blocked focus entries can not be null");
249             mNewlyLostAudioFocusEntries = newlyLostAudioFocusEntries;
250             mNewlyBlockedAudioFocusEntries = newlyBlockedAudioFocusEntries;
251             mAudioFocusResult = audioFocusResult;
252         }
253 
254         /** @see OemCarAudioFocusResult#getAudioFocusEntry */
255         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
256                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
257         @NonNull
setAudioFocusEntry(@onNull AudioFocusEntry focusEntry)258         public Builder setAudioFocusEntry(@NonNull AudioFocusEntry focusEntry) {
259             Preconditions.checkArgument(focusEntry != null,
260                     "Focus entry can not be null");
261             checkNotUsed();
262             mBuilderFieldsSet |= FOCUS_ENTRY_FIELDS_SET;
263             mAudioFocusEntry = focusEntry;
264             return this;
265         }
266 
267         /** @see OemCarAudioFocusResult#getNewlyLostAudioFocusEntries */
268         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
269                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
270         @NonNull
setNewlyLostAudioFocusEntries( @onNull List<AudioFocusEntry> newlyLostAudioFocusEntries)271         public Builder setNewlyLostAudioFocusEntries(
272                 @NonNull List<AudioFocusEntry> newlyLostAudioFocusEntries) {
273             Preconditions.checkArgument(newlyLostAudioFocusEntries != null,
274                     "Newly lost focus entries can not be null");
275             checkNotUsed();
276             mBuilderFieldsSet |= NEWLY_LOSS_FIELDS_SET;
277             mNewlyLostAudioFocusEntries = newlyLostAudioFocusEntries;
278             return this;
279         }
280 
281         /** @see #setNewlyLostAudioFocusEntries */
282         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
283                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
284         @NonNull
addNewlyLostAudioFocusEntry(@onNull AudioFocusEntry lossEntry)285         public Builder addNewlyLostAudioFocusEntry(@NonNull AudioFocusEntry lossEntry) {
286             Preconditions.checkArgument(lossEntry != null,
287                     "Newly lost focus entry can not be null");
288             if (mNewlyLostAudioFocusEntries == null) {
289                 setNewlyLostAudioFocusEntries(new ArrayList<>());
290             }
291             mNewlyLostAudioFocusEntries.add(lossEntry);
292             return this;
293         }
294 
295         /** @see OemCarAudioFocusResult#getNewlyBlockedAudioFocusEntries */
296         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
297                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
298         @NonNull
setNewlyBlockedAudioFocusEntries( @onNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries)299         public Builder setNewlyBlockedAudioFocusEntries(
300                 @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries) {
301             Preconditions.checkArgument(newlyBlockedAudioFocusEntries != null,
302                     "Newly blocked focus entries can not be null");
303             checkNotUsed();
304             mBuilderFieldsSet |= NEWLY_BLOCKED_FIELDS_SET;
305             mNewlyBlockedAudioFocusEntries = newlyBlockedAudioFocusEntries;
306             return this;
307         }
308 
309         /** @see #setNewlyBlockedAudioFocusEntries */
310         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
311                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
312         @NonNull
addNewlyBlockedAudioFocusEntry( @onNull AudioFocusEntry blockedEntry)313         public Builder addNewlyBlockedAudioFocusEntry(
314                 @NonNull AudioFocusEntry blockedEntry) {
315             Preconditions.checkArgument(blockedEntry != null,
316                     "Newly blocked focus entry can not be null");
317             if (mNewlyBlockedAudioFocusEntries == null) {
318                 setNewlyBlockedAudioFocusEntries(new ArrayList<>());
319             }
320             mNewlyBlockedAudioFocusEntries.add(blockedEntry);
321             return this;
322         }
323 
324         /** @see OemCarAudioFocusResult#getAudioFocusResult */
325         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
326                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
327         @NonNull
setAudioFocusResult(int audioFocusResult)328         public Builder setAudioFocusResult(int audioFocusResult) {
329             mBuilderFieldsSet |= FOCUS_RESULT_FIELDS_SET;
330             mAudioFocusResult = audioFocusResult;
331             return this;
332         }
333 
334         /** Builds the instance. This builder should not be touched after calling this! */
335         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
336                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
337         @NonNull
build()338         public OemCarAudioFocusResult build() {
339             checkNotUsed();
340             mBuilderFieldsSet |= BUILDER_USED_FIELDS_SET; // Mark builder used
341 
342             OemCarAudioFocusResult o = new OemCarAudioFocusResult(
343                     mAudioFocusEntry,
344                     mNewlyLostAudioFocusEntries,
345                     mNewlyBlockedAudioFocusEntries,
346                     mAudioFocusResult);
347             return o;
348         }
349 
checkNotUsed()350         private void checkNotUsed() {
351             if ((mBuilderFieldsSet & BUILDER_USED_FIELDS_SET) != 0) {
352                 throw new IllegalStateException(
353                         "This Builder should not be reused. Use a new Builder instance instead");
354             }
355         }
356     }
357 }
358