1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
|
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.telecom;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import com.android.server.telecom.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* CallAttributes represents a set of properties that define a new Call. Apps should build an
* instance of this class and use {@link TelecomManager#addCall} to start a new call with Telecom.
*
* <p>
* Apps should first register a {@link PhoneAccount} via {@link TelecomManager#registerPhoneAccount}
* and use the same {@link PhoneAccountHandle} registered with Telecom when creating an
* instance of CallAttributes.
*/
public final class CallAttributes implements Parcelable {
/** PhoneAccountHandle associated with the App managing calls **/
private final PhoneAccountHandle mPhoneAccountHandle;
/** Display name of the person on the other end of the call **/
private final CharSequence mDisplayName;
/** Address of the call. Note, this can be extended to a meeting link **/
private final Uri mAddress;
/** The direction (Outgoing/Incoming) of the new Call **/
@Direction private final int mDirection;
/** Information related to data being transmitted (voice, video, etc. ) **/
@CallType private final int mCallType;
/** Allows a package to opt into capabilities on the telecom side, on a per-call basis **/
@CallCapability private final int mCallCapabilities;
/** @hide **/
public static final String CALL_CAPABILITIES_KEY = "TelecomCapabilities";
/** @hide **/
public static final String DISPLAY_NAME_KEY = "DisplayName";
/** @hide **/
public static final String CALLER_PID_KEY = "CallerPid";
/** @hide **/
public static final String CALLER_UID_KEY = "CallerUid";
private CallAttributes(@NonNull PhoneAccountHandle phoneAccountHandle,
@NonNull CharSequence displayName,
@NonNull Uri address,
int direction,
int callType,
int callCapabilities) {
mPhoneAccountHandle = phoneAccountHandle;
mDisplayName = displayName;
mAddress = address;
mDirection = direction;
mCallType = callType;
mCallCapabilities = callCapabilities;
}
/** @hide */
@IntDef(value = {DIRECTION_INCOMING, DIRECTION_OUTGOING})
@Retention(RetentionPolicy.SOURCE)
public @interface Direction {
}
/**
* Indicates that the call is an incoming call.
*/
public static final int DIRECTION_INCOMING = 1;
/**
* Indicates that the call is an outgoing call.
*/
public static final int DIRECTION_OUTGOING = 2;
/** @hide */
@IntDef(value = {AUDIO_CALL, VIDEO_CALL})
@Retention(RetentionPolicy.SOURCE)
public @interface CallType {
}
/**
* Used when answering or dialing a call to indicate that the call does not have a video
* component
*/
public static final int AUDIO_CALL = 1;
/**
* Indicates video transmission is supported
*/
public static final int VIDEO_CALL = 2;
/** @hide */
@IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER,
SUPPORTS_VIDEO_CALLING}, flag = true)
@Retention(RetentionPolicy.SOURCE)
public @interface CallCapability {
}
/**
* The call being created can be set to inactive (traditionally referred to as hold). This
* means that once a new call goes active, if the active call needs to be held in order to
* place or receive an incoming call, the active call will be placed on hold. otherwise, the
* active call may be disconnected.
*/
public static final int SUPPORTS_SET_INACTIVE = 1 << 1;
/**
* The call can be streamed from a root device to another device to continue the call without
* completely transferring it.
*/
public static final int SUPPORTS_STREAM = 1 << 2;
/**
* The call can be completely transferred from one endpoint to another.
*/
public static final int SUPPORTS_TRANSFER = 1 << 3;
/**
* The call supports video calling. This allows clients to gate video calling on a per call
* basis as opposed to re-registering the phone account.
*/
@FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE)
public static final int SUPPORTS_VIDEO_CALLING = 1 << 4;
/**
* Build an instance of {@link CallAttributes}. In order to build a valid instance, a
* {@link PhoneAccountHandle}, call direction, display name, and {@link Uri} address
* are required.
*
* <p>
* Note: Pass in the same {@link PhoneAccountHandle} that was used to register a
* {@link PhoneAccount} with Telecom. see {@link TelecomManager#registerPhoneAccount}
*/
public static final class Builder {
// required and final fields
private final PhoneAccountHandle mPhoneAccountHandle;
@Direction private final int mDirection;
private final CharSequence mDisplayName;
private final Uri mAddress;
// optional fields
@CallType private int mCallType = CallAttributes.AUDIO_CALL;
@CallCapability private int mCallCapabilities = SUPPORTS_SET_INACTIVE;
/**
* Constructor for the CallAttributes.Builder class
*
* @param phoneAccountHandle that belongs to package registered with Telecom
* @param callDirection of the new call that will be added to Telecom
* @param displayName of the caller for incoming calls or initiating user for outgoing calls
* @param address of the caller for incoming calls or destination for outgoing calls
*/
public Builder(@NonNull PhoneAccountHandle phoneAccountHandle,
@Direction int callDirection, @NonNull CharSequence displayName,
@NonNull Uri address) {
if (!isInRange(DIRECTION_INCOMING, DIRECTION_OUTGOING, callDirection)) {
throw new IllegalArgumentException(TextUtils.formatSimple("CallDirection=[%d] is"
+ " invalid. CallDirections value should be within [%d, %d]",
callDirection, DIRECTION_INCOMING, DIRECTION_OUTGOING));
}
Objects.requireNonNull(phoneAccountHandle);
Objects.requireNonNull(displayName);
Objects.requireNonNull(address);
mPhoneAccountHandle = phoneAccountHandle;
mDirection = callDirection;
mDisplayName = displayName;
mAddress = address;
}
/**
* Sets the type of call; uses to indicate if a call is a video call or audio call.
* @param callType The call type.
* @return Builder
*/
@NonNull
public Builder setCallType(@CallType int callType) {
if (!isInRange(AUDIO_CALL, VIDEO_CALL, callType)) {
throw new IllegalArgumentException(TextUtils.formatSimple("CallType=[%d] is"
+ " invalid. CallTypes value should be within [%d, %d]",
callType, AUDIO_CALL, VIDEO_CALL));
}
mCallType = callType;
return this;
}
/**
* Sets the capabilities of this call. Use this to indicate whether your app supports
* holding, streaming and call transfers.
* @param callCapabilities Bitmask of call capabilities.
* @return Builder
*/
@NonNull
public Builder setCallCapabilities(@CallCapability int callCapabilities) {
mCallCapabilities = callCapabilities;
return this;
}
/**
* Build an instance of {@link CallAttributes} based on the last values passed to the
* setters or default values.
*
* @return an instance of {@link CallAttributes}
*/
@NonNull
public CallAttributes build() {
return new CallAttributes(mPhoneAccountHandle, mDisplayName, mAddress, mDirection,
mCallType, mCallCapabilities);
}
/** @hide */
private boolean isInRange(int floor, int ceiling, int value) {
return value >= floor && value <= ceiling;
}
}
/**
* The {@link PhoneAccountHandle} that should be registered to Telecom to allow calls. The
* {@link PhoneAccountHandle} should be registered before creating a CallAttributes instance.
*
* @return the {@link PhoneAccountHandle} for this package that allows this call to be created
*/
@NonNull public PhoneAccountHandle getPhoneAccountHandle() {
return mPhoneAccountHandle;
}
/**
* @return display name of the incoming caller or the person being called for an outgoing call
*/
@NonNull public CharSequence getDisplayName() {
return mDisplayName;
}
/**
* @return address of the incoming caller
* or the address of the person being called for an outgoing call
*/
@NonNull public Uri getAddress() {
return mAddress;
}
/**
* @return the direction of the new call.
*/
public @Direction int getDirection() {
return mDirection;
}
/**
* @return Information related to data being transmitted (voice, video, etc. )
*/
public @CallType int getCallType() {
return mCallType;
}
/**
* @return The allowed capabilities of the new call
*/
public @CallCapability int getCallCapabilities() {
return mCallCapabilities;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@Nullable Parcel dest, int flags) {
dest.writeParcelable(mPhoneAccountHandle, flags);
dest.writeCharSequence(mDisplayName);
dest.writeParcelable(mAddress, flags);
dest.writeInt(mDirection);
dest.writeInt(mCallType);
dest.writeInt(mCallCapabilities);
}
/**
* Responsible for creating CallAttribute objects for deserialized Parcels.
*/
public static final @android.annotation.NonNull
Parcelable.Creator<CallAttributes> CREATOR =
new Parcelable.Creator<>() {
@Override
public CallAttributes createFromParcel(Parcel source) {
return new CallAttributes(source.readParcelable(getClass().getClassLoader(),
android.telecom.PhoneAccountHandle.class),
source.readCharSequence(),
source.readParcelable(getClass().getClassLoader(),
android.net.Uri.class),
source.readInt(),
source.readInt(),
source.readInt());
}
@Override
public CallAttributes[] newArray(int size) {
return new CallAttributes[size];
}
};
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{ CallAttributes: [phoneAccountHandle: ")
.append(mPhoneAccountHandle) /* PhoneAccountHandle#toString handles PII */
.append("], [contactName: ")
.append(Log.pii(mDisplayName))
.append("], [address=")
.append(Log.pii(mAddress))
.append("], [direction=")
.append(mDirection)
.append("], [callType=")
.append(mCallType)
.append("], [mCallCapabilities=")
.append(mCallCapabilities)
.append("] }");
return sb.toString();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
CallAttributes that = (CallAttributes) obj;
return this.mDirection == that.mDirection
&& this.mCallType == that.mCallType
&& this.mCallCapabilities == that.mCallCapabilities
&& Objects.equals(this.mPhoneAccountHandle, that.mPhoneAccountHandle)
&& Objects.equals(this.mAddress, that.mAddress)
&& Objects.equals(this.mDisplayName, that.mDisplayName);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hash(mPhoneAccountHandle, mAddress, mDisplayName,
mDirection, mCallType, mCallCapabilities);
}
}
|