1 /*
2  * Copyright (C) 2020 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.bluetooth.telephony;
18 
19 import android.net.Uri;
20 import android.os.Bundle;
21 import android.os.Handler;
22 import android.telecom.Call;
23 import android.telecom.GatewayInfo;
24 import android.telecom.InCallService;
25 import android.telecom.PhoneAccountHandle;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  * A proxy class of android.telecom.Call that
34  * 1) facilitates testing of the BluetoothInCallService class; We can't mock the final class
35  * Call directly;
36  * 2) Some helper functions, to let Call have same methods as com.android.server.telecom.Call
37  *
38  * This is necessary due to the "final" attribute of the Call class. In order to
39  * test the correct functioning of the BluetoothInCallService class, the final class must be put
40  * into a container that can be mocked correctly.
41  */
42 @VisibleForTesting
43 public class BluetoothCall {
44 
45     private Call mCall;
46 
getCall()47     public Call getCall() {
48         return mCall;
49     }
50 
isCallNull()51     public boolean isCallNull() {
52         return mCall == null;
53     }
54 
setCall(Call call)55     public void setCall(Call call) {
56         mCall = call;
57     }
58 
BluetoothCall(Call call)59     public BluetoothCall(Call call) {
60         mCall = call;
61     }
62 
getRemainingPostDialSequence()63     public String getRemainingPostDialSequence() {
64         return mCall.getRemainingPostDialSequence();
65     }
66 
answer(int videoState)67     public void answer(int videoState) {
68         mCall.answer(videoState);
69     }
70 
deflect(Uri address)71     public void deflect(Uri address) {
72         mCall.deflect(address);
73     }
74 
reject(boolean rejectWithMessage, String textMessage)75     public void reject(boolean rejectWithMessage, String textMessage) {
76         mCall.reject(rejectWithMessage, textMessage);
77     }
78 
disconnect()79     public void disconnect() {
80         mCall.disconnect();
81     }
82 
hold()83     public void hold() {
84         mCall.hold();
85     }
86 
unhold()87     public void unhold() {
88         mCall.unhold();
89     }
90 
enterBackgroundAudioProcessing()91     public void enterBackgroundAudioProcessing() {
92         mCall.enterBackgroundAudioProcessing();
93     }
94 
exitBackgroundAudioProcessing(boolean shouldRing)95     public void exitBackgroundAudioProcessing(boolean shouldRing) {
96         mCall.exitBackgroundAudioProcessing(shouldRing);
97     }
98 
playDtmfTone(char digit)99     public void playDtmfTone(char digit) {
100         mCall.playDtmfTone(digit);
101     }
102 
stopDtmfTone()103     public void stopDtmfTone() {
104         mCall.stopDtmfTone();
105     }
106 
postDialContinue(boolean proceed)107     public void postDialContinue(boolean proceed) {
108         mCall.postDialContinue(proceed);
109     }
110 
phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault)111     public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
112         mCall.phoneAccountSelected(accountHandle, setDefault);
113     }
114 
conference(BluetoothCall callToConferenceWith)115     public void conference(BluetoothCall callToConferenceWith) {
116         if (callToConferenceWith != null) {
117             mCall.conference(callToConferenceWith.getCall());
118         }
119     }
120 
splitFromConference()121     public void splitFromConference() {
122         mCall.splitFromConference();
123     }
124 
mergeConference()125     public void mergeConference() {
126         mCall.mergeConference();
127     }
128 
swapConference()129     public void swapConference() {
130         mCall.swapConference();
131     }
132 
pullExternalCall()133     public void pullExternalCall() {
134         mCall.pullExternalCall();
135     }
136 
sendCallEvent(String event, Bundle extras)137     public void sendCallEvent(String event, Bundle extras) {
138         mCall.sendCallEvent(event, extras);
139     }
140 
sendRttRequest()141     public void sendRttRequest() {
142         mCall.sendRttRequest();
143     }
144 
respondToRttRequest(int id, boolean accept)145     public void respondToRttRequest(int id, boolean accept) {
146         mCall.respondToRttRequest(id, accept);
147     }
148 
handoverTo(PhoneAccountHandle toHandle, int videoState, Bundle extras)149     public void handoverTo(PhoneAccountHandle toHandle, int videoState, Bundle extras) {
150         mCall.handoverTo(toHandle, videoState, extras);
151     }
152 
stopRtt()153     public void stopRtt() {
154         mCall.stopRtt();
155     }
156 
putExtras(Bundle extras)157     public void putExtras(Bundle extras) {
158         mCall.putExtras(extras);
159     }
160 
putExtra(String key, boolean value)161     public void putExtra(String key, boolean value) {
162         mCall.putExtra(key, value);
163     }
164 
putExtra(String key, int value)165     public void putExtra(String key, int value) {
166         mCall.putExtra(key, value);
167     }
168 
putExtra(String key, String value)169     public void putExtra(String key, String value) {
170         mCall.putExtra(key, value);
171     }
172 
removeExtras(List<String> keys)173     public void removeExtras(List<String> keys) {
174         mCall.removeExtras(keys);
175     }
176 
removeExtras(String... keys)177     public void removeExtras(String... keys) {
178         mCall.removeExtras(keys);
179     }
180 
getParentId()181     public String getParentId() {
182         Call parent = mCall.getParent();
183         if (parent != null) {
184             return parent.getDetails().getTelecomCallId();
185         }
186         return null;
187     }
188 
getChildrenIds()189     public List<String> getChildrenIds() {
190         return getIds(mCall.getChildren());
191     }
192 
getConferenceableCalls()193     public List<String> getConferenceableCalls() {
194         return getIds(mCall.getConferenceableCalls());
195     }
196 
getState()197     public int getState() {
198         return mCall.getState();
199     }
200 
getCannedTextResponses()201     public List<String> getCannedTextResponses() {
202         return mCall.getCannedTextResponses();
203     }
204 
getVideoCall()205     public InCallService.VideoCall getVideoCall() {
206         return mCall.getVideoCall();
207     }
208 
getDetails()209     public Call.Details getDetails() {
210         return mCall.getDetails();
211     }
212 
getRttCall()213     public Call.RttCall getRttCall() {
214         return mCall.getRttCall();
215     }
216 
isRttActive()217     public boolean isRttActive() {
218         return mCall.isRttActive();
219     }
220 
registerCallback(Call.Callback callback)221     public void registerCallback(Call.Callback callback) {
222         mCall.registerCallback(callback);
223     }
224 
registerCallback(Call.Callback callback, Handler handler)225     public void registerCallback(Call.Callback callback, Handler handler) {
226         mCall.registerCallback(callback, handler);
227     }
228 
unregisterCallback(Call.Callback callback)229     public void unregisterCallback(Call.Callback callback) {
230         mCall.unregisterCallback(callback);
231     }
232 
toString()233     public String toString() {
234         String string = mCall.toString();
235         return string == null ? "" : string;
236     }
237 
addListener(Call.Listener listener)238     public void addListener(Call.Listener listener) {
239         mCall.addListener(listener);
240     }
241 
removeListener(Call.Listener listener)242     public void removeListener(Call.Listener listener) {
243         mCall.removeListener(listener);
244     }
245 
getGenericConferenceActiveChildCallId()246     public String getGenericConferenceActiveChildCallId() {
247         return mCall.getGenericConferenceActiveChildCall().getDetails().getTelecomCallId();
248     }
249 
getContactDisplayName()250     public String getContactDisplayName() {
251         return mCall.getDetails().getContactDisplayName();
252     }
253 
getAccountHandle()254     public PhoneAccountHandle getAccountHandle() {
255         return mCall.getDetails().getAccountHandle();
256     }
257 
getVideoState()258     public int getVideoState() {
259         return mCall.getDetails().getVideoState();
260     }
261 
getCallerDisplayName()262     public String getCallerDisplayName() {
263         return mCall.getDetails().getCallerDisplayName();
264     }
265 
266     @Override
equals(Object o)267     public boolean equals(Object o) {
268         if (o == null) {
269             return getCall() == null;
270         }
271         return o instanceof BluetoothCall && getCall() == ((BluetoothCall) o).getCall();
272     }
273 
274     // helper functions
isSilentRingingRequested()275     public boolean isSilentRingingRequested() {
276         return getDetails().getExtras() != null
277                 && getDetails().getExtras().getBoolean(Call.EXTRA_SILENT_RINGING_REQUESTED);
278     }
279 
isConference()280     public boolean isConference() {
281         return getDetails().hasProperty(Call.Details.PROPERTY_CONFERENCE);
282     }
283 
can(int capability)284     public boolean can(int capability) {
285         return getDetails().can(capability);
286     }
287 
getHandle()288     public Uri getHandle() {
289         return getDetails().getHandle();
290     }
291 
getGatewayInfo()292     public GatewayInfo getGatewayInfo() {
293         return getDetails().getGatewayInfo();
294     }
295 
isIncoming()296     public boolean isIncoming() {
297         return getDetails().getCallDirection() == Call.Details.DIRECTION_INCOMING;
298     }
299 
isExternalCall()300     public boolean isExternalCall() {
301         return getDetails().hasProperty(Call.Details.PROPERTY_IS_EXTERNAL_CALL);
302     }
303 
getTelecomCallId()304     public String getTelecomCallId() {
305         return getDetails().getTelecomCallId();
306     }
307 
wasConferencePreviouslyMerged()308     public boolean wasConferencePreviouslyMerged() {
309         return can(Call.Details.CAPABILITY_SWAP_CONFERENCE) &&
310                 !can(Call.Details.CAPABILITY_MERGE_CONFERENCE);
311     }
312 
getIds(List<Call> calls)313     public static List<String> getIds(List<Call> calls) {
314         List<String> result = new ArrayList<>();
315         for (Call call : calls) {
316             if (call != null) {
317                 result.add(call.getDetails().getTelecomCallId());
318             }
319         }
320         return result;
321     }
322 
hasProperty(int property)323     public boolean hasProperty(int property) {
324         return getDetails().hasProperty(property);
325     }
326 }
327