• 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
117     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
118             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
toString()119     public String toString() {
120         return new StringBuilder().append("OemCarAudioFocusResult { audioFocusEntry = ")
121                 .append(mAudioFocusEntry)
122                 .append(", mNewlyLostAudioFocusEntries = ").append(mNewlyLostAudioFocusEntries)
123                 .append(", mNewlyBlockedAudioFocusEntries = ")
124                 .append(mNewlyBlockedAudioFocusEntries)
125                 .append(", mAudioFocusResult = ").append(mAudioFocusResult)
126                 .append(" }").toString();
127     }
128 
129     @Override
130     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
131             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
writeToParcel(@onNull Parcel dest, int flags)132     public void writeToParcel(@NonNull Parcel dest, int flags) {
133         byte flg = 0;
134         if (mAudioFocusEntry != null) {
135             flg = (byte) (flg | Builder.FOCUS_ENTRY_FIELDS_SET);
136         }
137         dest.writeByte(flg);
138         if (mAudioFocusEntry != null) {
139             mAudioFocusEntry.writeToParcel(dest, flags);
140         }
141         dest.writeParcelableList(mNewlyLostAudioFocusEntries, flags);
142         dest.writeParcelableList(mNewlyBlockedAudioFocusEntries, flags);
143         dest.writeInt(mAudioFocusResult);
144     }
145 
146     // TODO(b/260757994): Remove ApiRequirements for overridden methods
147     @Override
148     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
149             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
describeContents()150     public int describeContents() {
151         return 0;
152     }
153 
154     /** @hide */
155     @SuppressWarnings({"unchecked", "RedundantCast"})
156     @VisibleForTesting
OemCarAudioFocusResult(@onNull Parcel in)157     public OemCarAudioFocusResult(@NonNull Parcel in) {
158         byte flg = in.readByte();
159         AudioFocusEntry audioFocusEntry = (flg & Builder.FOCUS_ENTRY_FIELDS_SET) == 0
160                 ? null : AudioFocusEntry.CREATOR.createFromParcel(in);
161         List<AudioFocusEntry> audioFocusLosers = new ArrayList<>();
162         in.readParcelableList(audioFocusLosers, AudioFocusEntry.class.getClassLoader(),
163                 AudioFocusEntry.class);
164         List<AudioFocusEntry> audioFocusBlocked = new ArrayList<>();
165         in.readParcelableList(audioFocusBlocked, AudioFocusEntry.class.getClassLoader(),
166                 AudioFocusEntry.class);
167         int audioFocusResult = in.readInt();
168 
169         this.mAudioFocusEntry = audioFocusEntry;
170         this.mNewlyLostAudioFocusEntries = audioFocusLosers;
171         this.mNewlyBlockedAudioFocusEntries = audioFocusBlocked;
172         this.mAudioFocusResult = audioFocusResult;
173     }
174 
175     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
176             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
177     @NonNull
178     public static final OemCarAudioFocusResult EMPTY_OEM_CAR_AUDIO_FOCUS_RESULTS =
179             new OemCarAudioFocusResult(null,
180                     /* newlyLostAudioFocusEntries= */ new ArrayList<>(/* initialCapacity= */ 0),
181                     /* newlyBlockedAudioFocusEntries= */ new ArrayList<>(/* initialCapacity= */ 0),
182                     AUDIOFOCUS_REQUEST_FAILED);
183 
184     // TODO(b/260757994): Remove ApiRequirements for overridden methods
185     @Override
186     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
187             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
equals(Object o)188     public boolean equals(Object o) {
189         if (this == o) {
190             return true;
191         }
192 
193         if (!(o instanceof OemCarAudioFocusResult)) {
194             return false;
195         }
196 
197         OemCarAudioFocusResult that = (OemCarAudioFocusResult) o;
198 
199         return Objects.equals(mAudioFocusEntry, that.mAudioFocusEntry)
200                 && mAudioFocusResult == that.mAudioFocusResult
201                 && mNewlyBlockedAudioFocusEntries.equals(
202                 that.mNewlyBlockedAudioFocusEntries)
203                 && mNewlyLostAudioFocusEntries.equals(that.mNewlyLostAudioFocusEntries);
204     }
205 
206     // TODO(b/260757994): Remove ApiRequirements for overridden methods
207     @Override
208     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
209             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
hashCode()210     public int hashCode() {
211         return Objects.hash(mAudioFocusEntry, mAudioFocusResult,
212                 mNewlyBlockedAudioFocusEntries, mNewlyLostAudioFocusEntries);
213     }
214 
215     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
216             minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
217     @NonNull
218     public static final Parcelable.Creator<OemCarAudioFocusResult> CREATOR =
219             new Parcelable.Creator<OemCarAudioFocusResult>() {
220         @Override
221         public OemCarAudioFocusResult[] newArray(int size) {
222             return new OemCarAudioFocusResult[size];
223         }
224 
225         @Override
226         public OemCarAudioFocusResult createFromParcel(@NonNull Parcel in) {
227             return new OemCarAudioFocusResult(in);
228         }
229     };
230 
231     /**
232      * A builder for {@link OemCarAudioFocusResult}
233      */
234     @SuppressWarnings("WeakerAccess")
235     public static final class Builder {
236 
237         private static final int FOCUS_ENTRY_FIELDS_SET = 0x1;
238         private static final int NEWLY_LOSS_FIELDS_SET = 0x2;
239         private static final int NEWLY_BLOCKED_FIELDS_SET = 0x4;
240         private static final int FOCUS_RESULT_FIELDS_SET = 0x8;
241         private static final int BUILDER_USED_FIELDS_SET = 0x10;
242         private @Nullable AudioFocusEntry mAudioFocusEntry;
243         private @NonNull List<AudioFocusEntry> mNewlyLostAudioFocusEntries;
244         private @NonNull List<AudioFocusEntry> mNewlyBlockedAudioFocusEntries;
245         private int mAudioFocusResult;
246 
247         private long mBuilderFieldsSet = 0L;
248 
Builder( @onNull List<AudioFocusEntry> newlyLostAudioFocusEntries, @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries, int audioFocusResult)249         public Builder(
250                 @NonNull List<AudioFocusEntry> newlyLostAudioFocusEntries,
251                 @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries,
252                 int audioFocusResult) {
253             Preconditions.checkArgument(newlyLostAudioFocusEntries != null,
254                     "Newly lost focus entries can not be null");
255             Preconditions.checkArgument(newlyBlockedAudioFocusEntries != null,
256                     "Newly blocked focus entries can not be null");
257             mNewlyLostAudioFocusEntries = newlyLostAudioFocusEntries;
258             mNewlyBlockedAudioFocusEntries = newlyBlockedAudioFocusEntries;
259             mAudioFocusResult = audioFocusResult;
260         }
261 
262         /** @see OemCarAudioFocusResult#getAudioFocusEntry */
263         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
264                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
265         @NonNull
setAudioFocusEntry(@onNull AudioFocusEntry focusEntry)266         public Builder setAudioFocusEntry(@NonNull AudioFocusEntry focusEntry) {
267             Preconditions.checkArgument(focusEntry != null,
268                     "Focus entry can not be null");
269             checkNotUsed();
270             mBuilderFieldsSet |= FOCUS_ENTRY_FIELDS_SET;
271             mAudioFocusEntry = focusEntry;
272             return this;
273         }
274 
275         /** @see OemCarAudioFocusResult#getNewlyLostAudioFocusEntries */
276         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
277                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
278         @NonNull
setNewlyLostAudioFocusEntries( @onNull List<AudioFocusEntry> newlyLostAudioFocusEntries)279         public Builder setNewlyLostAudioFocusEntries(
280                 @NonNull List<AudioFocusEntry> newlyLostAudioFocusEntries) {
281             Preconditions.checkArgument(newlyLostAudioFocusEntries != null,
282                     "Newly lost focus entries can not be null");
283             checkNotUsed();
284             mBuilderFieldsSet |= NEWLY_LOSS_FIELDS_SET;
285             mNewlyLostAudioFocusEntries = newlyLostAudioFocusEntries;
286             return this;
287         }
288 
289         /** @see #setNewlyLostAudioFocusEntries */
290         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
291                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
292         @NonNull
addNewlyLostAudioFocusEntry(@onNull AudioFocusEntry lossEntry)293         public Builder addNewlyLostAudioFocusEntry(@NonNull AudioFocusEntry lossEntry) {
294             Preconditions.checkArgument(lossEntry != null,
295                     "Newly lost focus entry can not be null");
296             if (mNewlyLostAudioFocusEntries == null) {
297                 setNewlyLostAudioFocusEntries(new ArrayList<>());
298             }
299             mNewlyLostAudioFocusEntries.add(lossEntry);
300             return this;
301         }
302 
303         /** @see OemCarAudioFocusResult#getNewlyBlockedAudioFocusEntries */
304         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
305                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
306         @NonNull
setNewlyBlockedAudioFocusEntries( @onNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries)307         public Builder setNewlyBlockedAudioFocusEntries(
308                 @NonNull List<AudioFocusEntry> newlyBlockedAudioFocusEntries) {
309             Preconditions.checkArgument(newlyBlockedAudioFocusEntries != null,
310                     "Newly blocked focus entries can not be null");
311             checkNotUsed();
312             mBuilderFieldsSet |= NEWLY_BLOCKED_FIELDS_SET;
313             mNewlyBlockedAudioFocusEntries = newlyBlockedAudioFocusEntries;
314             return this;
315         }
316 
317         /** @see #setNewlyBlockedAudioFocusEntries */
318         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
319                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
320         @NonNull
addNewlyBlockedAudioFocusEntry( @onNull AudioFocusEntry blockedEntry)321         public Builder addNewlyBlockedAudioFocusEntry(
322                 @NonNull AudioFocusEntry blockedEntry) {
323             Preconditions.checkArgument(blockedEntry != null,
324                     "Newly blocked focus entry can not be null");
325             if (mNewlyBlockedAudioFocusEntries == null) {
326                 setNewlyBlockedAudioFocusEntries(new ArrayList<>());
327             }
328             mNewlyBlockedAudioFocusEntries.add(blockedEntry);
329             return this;
330         }
331 
332         /** @see OemCarAudioFocusResult#getAudioFocusResult */
333         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
334                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
335         @NonNull
setAudioFocusResult(int audioFocusResult)336         public Builder setAudioFocusResult(int audioFocusResult) {
337             mBuilderFieldsSet |= FOCUS_RESULT_FIELDS_SET;
338             mAudioFocusResult = audioFocusResult;
339             return this;
340         }
341 
342         /** Builds the instance. This builder should not be touched after calling this! */
343         @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3,
344                 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0)
345         @NonNull
build()346         public OemCarAudioFocusResult build() {
347             checkNotUsed();
348             mBuilderFieldsSet |= BUILDER_USED_FIELDS_SET; // Mark builder used
349 
350             OemCarAudioFocusResult o = new OemCarAudioFocusResult(
351                     mAudioFocusEntry,
352                     mNewlyLostAudioFocusEntries,
353                     mNewlyBlockedAudioFocusEntries,
354                     mAudioFocusResult);
355             return o;
356         }
357 
checkNotUsed()358         private void checkNotUsed() {
359             if ((mBuilderFieldsSet & BUILDER_USED_FIELDS_SET) != 0) {
360                 throw new IllegalStateException(
361                         "This Builder should not be reused. Use a new Builder instance instead");
362             }
363         }
364     }
365 }
366