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