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 com.android.car.audio;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.media.AudioAttributes;
22 import android.util.ArraySet;
23 import android.util.proto.ProtoOutputStream;
24 
25 import com.android.car.audio.CarAudioDumpProto.CarAudioAttributesProto;
26 import com.android.car.audio.CarAudioDumpProto.CarAudioContextInfoProto;
27 import com.android.car.audio.CarAudioDumpProto.CarAudioContextProto;
28 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
29 import com.android.car.internal.util.IndentingPrintWriter;
30 import com.android.internal.util.Preconditions;
31 
32 import java.util.Arrays;
33 import java.util.Objects;
34 
35 /**
36  * Class used to encapsulate the car audio context, which is represented by a
37  * list of {@link AudioAttributes}
38  */
39 final class CarAudioContextInfo {
40 
41     private final String mName;
42     private final @CarAudioContext.AudioContext int mId;
43 
44     private final AudioAttributes[] mAudioAttributes;
45 
CarAudioContextInfo(AudioAttributes[] audioAttributes, String name, @CarAudioContext.AudioContext int id)46     CarAudioContextInfo(AudioAttributes[] audioAttributes, String name,
47             @CarAudioContext.AudioContext int id) {
48         Objects.requireNonNull(audioAttributes,
49                 "Car audio context's audio attributes can not be null");
50         Preconditions.checkArgument(audioAttributes.length != 0,
51                 "Car audio context's audio attributes can not be empty");
52         mAudioAttributes = audioAttributes;
53         Objects.requireNonNull(name,
54                 "Car audio context's name can not be null");
55         mName = Preconditions.checkStringNotEmpty(name,
56                 "Car audio context's name can not be empty");
57         mId = Preconditions.checkArgumentNonnegative(id,
58                 "Car audio context's id can not be negative");
59     }
60 
getName()61     String getName() {
62         return mName;
63     }
64 
getId()65     @CarAudioContext.AudioContext int getId() {
66         return mId;
67     }
68 
getAudioAttributes()69     AudioAttributes[] getAudioAttributes() {
70         return mAudioAttributes;
71     }
72 
73     @Override
toString()74     public String toString() {
75         return new StringBuilder().append(mName)
76                 .append("[").append(mId).append("] attributes: ")
77                 .append(Arrays.toString(mAudioAttributes)).toString();
78     }
79 
80     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)81     void dump(IndentingPrintWriter writer) {
82         writer.printf("Context %s id %s\n", mName, mId);
83         writer.increaseIndent();
84         for (int index = 0; index < mAudioAttributes.length; index++) {
85             writer.println(mAudioAttributes[index]);
86         }
87         writer.decreaseIndent();
88     }
89 
90     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)91     void dumpProto(ProtoOutputStream proto) {
92         long contextInfosToken = proto.start(CarAudioContextProto.CAR_AUDIO_CONTEXT_INFOS);
93         proto.write(CarAudioContextInfoProto.NAME, mName);
94         proto.write(CarAudioContextInfoProto.ID, mId);
95         for (int index = 0; index < mAudioAttributes.length; index++) {
96             dumpCarAudioAttributesProto(mAudioAttributes[index],
97                     CarAudioContextInfoProto.ATTRIBUTES, proto);
98         }
99         proto.end(contextInfosToken);
100     }
101 
102     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpCarAudioAttributesProto(AudioAttributes attributes, long fieldId, ProtoOutputStream proto)103     static void dumpCarAudioAttributesProto(AudioAttributes attributes, long fieldId,
104             ProtoOutputStream proto) {
105         long token = proto.start(fieldId);
106         proto.write(CarAudioAttributesProto.USAGE, attributes.getUsage());
107         proto.write(CarAudioAttributesProto.CONTENT_TYPE, attributes.getContentType());
108         proto.end(token);
109     }
110 
111     @Override
equals(Object other)112     public boolean equals(Object other) {
113         if (this == other) {
114             return true;
115         }
116 
117         if (!(other instanceof CarAudioContextInfo)) {
118             return false;
119         }
120 
121         CarAudioContextInfo info = (CarAudioContextInfo) other;
122 
123         return mId == info.mId && mName.equals(info.mName)
124                 && audioAttributesMatch(info.mAudioAttributes);
125     }
126 
audioAttributesMatch(AudioAttributes[] audioAttributes)127     private boolean audioAttributesMatch(AudioAttributes[] audioAttributes) {
128         if (mAudioAttributes.length != audioAttributes.length) {
129             return false;
130         }
131 
132         ArraySet<AudioAttributes> attributes =
133                 new ArraySet<>(mAudioAttributes.length);
134         for (int index = 0; index < mAudioAttributes.length; index++) {
135             attributes.add(mAudioAttributes[index]);
136         }
137 
138         for (int index = 0; index < audioAttributes.length; index++) {
139             if (!attributes.remove(audioAttributes[index])) {
140                 return false;
141             }
142         }
143 
144         return attributes.isEmpty();
145     }
146 
147     @Override
hashCode()148     public int hashCode() {
149         return Objects.hash(mId, mName, Arrays.hashCode(mAudioAttributes));
150     }
151 }
152