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.telecom; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.os.Parcel; 22 import android.os.ParcelUuid; 23 import android.os.Parcelable; 24 import android.text.TextUtils; 25 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 import java.util.Objects; 29 import java.util.UUID; 30 31 /** 32 * Encapsulates the endpoint where call media can flow 33 */ 34 public final class CallEndpoint implements Parcelable { 35 /** @hide */ 36 public static final int ENDPOINT_OPERATION_SUCCESS = 0; 37 /** @hide */ 38 public static final int ENDPOINT_OPERATION_FAILED = 1; 39 40 /** @hide */ 41 @Retention(RetentionPolicy.SOURCE) 42 @IntDef({TYPE_UNKNOWN, TYPE_EARPIECE, TYPE_BLUETOOTH, TYPE_WIRED_HEADSET, TYPE_SPEAKER, 43 TYPE_STREAMING}) 44 public @interface EndpointType {} 45 46 /** Indicates that the type of endpoint through which call media flows is unknown type. */ 47 public static final int TYPE_UNKNOWN = -1; 48 49 /** Indicates that the type of endpoint through which call media flows is an earpiece. */ 50 public static final int TYPE_EARPIECE = 1; 51 52 /** Indicates that the type of endpoint through which call media flows is a Bluetooth. */ 53 public static final int TYPE_BLUETOOTH = 2; 54 55 /** Indicates that the type of endpoint through which call media flows is a wired headset. */ 56 public static final int TYPE_WIRED_HEADSET = 3; 57 58 /** Indicates that the type of endpoint through which call media flows is a speakerphone. */ 59 public static final int TYPE_SPEAKER = 4; 60 61 /** Indicates that the type of endpoint through which call media flows is an external. */ 62 public static final int TYPE_STREAMING = 5; 63 64 /** 65 * Error message attached to IllegalArgumentException when the endpoint name or id is null. 66 * @hide 67 */ 68 private static final String CALLENDPOINT_NAME_ID_NULL_ERROR = 69 "CallEndpoint name cannot be null."; 70 71 private final CharSequence mName; 72 private final int mType; 73 private final ParcelUuid mIdentifier; 74 75 /** 76 * Constructor for a {@link CallEndpoint} object. 77 * 78 * @param name Human-readable name associated with the endpoint 79 * @param type The type of endpoint through which call media being routed 80 * Allowed values: 81 * {@link #TYPE_EARPIECE} 82 * {@link #TYPE_BLUETOOTH} 83 * {@link #TYPE_WIRED_HEADSET} 84 * {@link #TYPE_SPEAKER} 85 * {@link #TYPE_STREAMING} 86 * {@link #TYPE_UNKNOWN} 87 * @param id A unique identifier for this endpoint on the device 88 */ CallEndpoint(@onNull CharSequence name, @EndpointType int type, @NonNull ParcelUuid id)89 public CallEndpoint(@NonNull CharSequence name, @EndpointType int type, 90 @NonNull ParcelUuid id) { 91 // Ensure that endpoint name and id are non-null. 92 if (name == null || id == null) { 93 throw new IllegalArgumentException(CALLENDPOINT_NAME_ID_NULL_ERROR); 94 } 95 96 this.mName = name; 97 this.mType = type; 98 this.mIdentifier = id; 99 } 100 101 /** @hide */ CallEndpoint(@onNull CharSequence name, @EndpointType int type)102 public CallEndpoint(@NonNull CharSequence name, @EndpointType int type) { 103 this(name, type, new ParcelUuid(UUID.randomUUID())); 104 } 105 106 /** @hide */ CallEndpoint(CallEndpoint endpoint)107 public CallEndpoint(CallEndpoint endpoint) { 108 // Ensure that endpoint name and id are non-null. 109 if (endpoint.getEndpointName() == null || endpoint.getIdentifier() == null) { 110 throw new IllegalArgumentException(CALLENDPOINT_NAME_ID_NULL_ERROR); 111 } 112 113 mName = endpoint.getEndpointName(); 114 mType = endpoint.getEndpointType(); 115 mIdentifier = endpoint.getIdentifier(); 116 } 117 118 /** 119 * {@inheritDoc} 120 */ 121 @Override equals(Object obj)122 public boolean equals(Object obj) { 123 if (obj == null) { 124 return false; 125 } 126 if (!(obj instanceof CallEndpoint)) { 127 return false; 128 } 129 CallEndpoint endpoint = (CallEndpoint) obj; 130 return Objects.equals(getEndpointName(), endpoint.getEndpointName()) 131 && getEndpointType() == endpoint.getEndpointType() 132 && getIdentifier().equals(endpoint.getIdentifier()); 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override hashCode()139 public int hashCode() { 140 return Objects.hash(mName, mType, mIdentifier); 141 } 142 143 /** 144 * {@inheritDoc} 145 */ 146 @Override toString()147 public String toString() { 148 return TextUtils.formatSimple("[CallEndpoint Name: %s, Type: %s, Identifier: %s]", 149 mName.toString(), endpointTypeToString(mType), mIdentifier.toString()); 150 } 151 152 /** 153 * @return Human-readable name associated with the endpoint 154 */ 155 @NonNull getEndpointName()156 public CharSequence getEndpointName() { 157 return mName; 158 } 159 160 /** 161 * @return The type of endpoint through which call media being routed 162 */ 163 @EndpointType getEndpointType()164 public int getEndpointType() { 165 return mType; 166 } 167 168 /** 169 * @return A unique identifier for this endpoint on the device 170 */ 171 @NonNull getIdentifier()172 public ParcelUuid getIdentifier() { 173 return mIdentifier; 174 } 175 176 /** 177 * Converts the provided endpoint type into a human-readable string representation. 178 * 179 * @param endpointType to convert into a string. 180 * @return String representation of the provided endpoint type. 181 * @hide 182 */ 183 @NonNull endpointTypeToString(int endpointType)184 public static String endpointTypeToString(int endpointType) { 185 switch (endpointType) { 186 case TYPE_EARPIECE: 187 return "EARPIECE"; 188 case TYPE_BLUETOOTH: 189 return "BLUETOOTH"; 190 case TYPE_WIRED_HEADSET: 191 return "WIRED_HEADSET"; 192 case TYPE_SPEAKER: 193 return "SPEAKER"; 194 case TYPE_STREAMING: 195 return "EXTERNAL"; 196 default: 197 return "UNKNOWN (" + endpointType + ")"; 198 } 199 } 200 201 /** 202 * Responsible for creating CallEndpoint objects for deserialized Parcels. 203 */ 204 public static final @android.annotation.NonNull Parcelable.Creator<CallEndpoint> CREATOR = 205 new Parcelable.Creator<CallEndpoint>() { 206 207 @Override 208 public CallEndpoint createFromParcel(Parcel source) { 209 CharSequence name = source.readCharSequence(); 210 int type = source.readInt(); 211 ParcelUuid id = ParcelUuid.CREATOR.createFromParcel(source); 212 213 return new CallEndpoint(name, type, id); 214 } 215 216 @Override 217 public CallEndpoint[] newArray(int size) { 218 return new CallEndpoint[size]; 219 } 220 }; 221 222 /** 223 * {@inheritDoc} 224 */ 225 @Override describeContents()226 public int describeContents() { 227 return 0; 228 } 229 230 /** 231 * {@inheritDoc} 232 */ 233 @Override writeToParcel(@onNull Parcel destination, int flags)234 public void writeToParcel(@NonNull Parcel destination, int flags) { 235 destination.writeCharSequence(mName); 236 destination.writeInt(mType); 237 mIdentifier.writeToParcel(destination, flags); 238 } 239 } 240