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