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