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