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 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 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 @VisibleForTesting 45 public class BluetoothCall { 46 47 private Call mCall; 48 private UUID mCallId; 49 50 // An index used to identify calls for CLCC (C* List Current Calls). 51 int mClccIndex = -1; 52 getCall()53 public Call getCall() { 54 return mCall; 55 } 56 isCallNull()57 public boolean isCallNull() { 58 return mCall == null; 59 } 60 setCall(Call call)61 public void setCall(Call call) { 62 mCall = call; 63 } 64 BluetoothCall(Call call)65 public BluetoothCall(Call call) { 66 mCall = call; 67 mCallId = UUID.randomUUID(); 68 } 69 BluetoothCall(Call call, UUID callId)70 public BluetoothCall(Call call, UUID callId) { 71 mCall = call; 72 mCallId = callId; 73 } 74 getTbsCallId()75 public UUID getTbsCallId() { 76 return mCallId; 77 } 78 setTbsCallId(UUID callId)79 public void setTbsCallId(UUID callId) { 80 mCallId = callId; 81 } 82 getRemainingPostDialSequence()83 public String getRemainingPostDialSequence() { 84 return mCall.getRemainingPostDialSequence(); 85 } 86 answer(int videoState)87 public void answer(int videoState) { 88 mCall.answer(videoState); 89 } 90 deflect(Uri address)91 public void deflect(Uri address) { 92 mCall.deflect(address); 93 } 94 reject(boolean rejectWithMessage, String textMessage)95 public void reject(boolean rejectWithMessage, String textMessage) { 96 mCall.reject(rejectWithMessage, textMessage); 97 } 98 disconnect()99 public void disconnect() { 100 mCall.disconnect(); 101 } 102 hold()103 public void hold() { 104 mCall.hold(); 105 } 106 unhold()107 public void unhold() { 108 mCall.unhold(); 109 } 110 enterBackgroundAudioProcessing()111 public void enterBackgroundAudioProcessing() { 112 mCall.enterBackgroundAudioProcessing(); 113 } 114 exitBackgroundAudioProcessing(boolean shouldRing)115 public void exitBackgroundAudioProcessing(boolean shouldRing) { 116 mCall.exitBackgroundAudioProcessing(shouldRing); 117 } 118 playDtmfTone(char digit)119 public void playDtmfTone(char digit) { 120 mCall.playDtmfTone(digit); 121 } 122 stopDtmfTone()123 public void stopDtmfTone() { 124 mCall.stopDtmfTone(); 125 } 126 postDialContinue(boolean proceed)127 public void postDialContinue(boolean proceed) { 128 mCall.postDialContinue(proceed); 129 } 130 phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault)131 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) { 132 mCall.phoneAccountSelected(accountHandle, setDefault); 133 } 134 conference(BluetoothCall callToConferenceWith)135 public void conference(BluetoothCall callToConferenceWith) { 136 if (callToConferenceWith != null) { 137 mCall.conference(callToConferenceWith.getCall()); 138 } 139 } 140 splitFromConference()141 public void splitFromConference() { 142 mCall.splitFromConference(); 143 } 144 mergeConference()145 public void mergeConference() { 146 mCall.mergeConference(); 147 } 148 swapConference()149 public void swapConference() { 150 mCall.swapConference(); 151 } 152 pullExternalCall()153 public void pullExternalCall() { 154 mCall.pullExternalCall(); 155 } 156 sendCallEvent(String event, Bundle extras)157 public void sendCallEvent(String event, Bundle extras) { 158 mCall.sendCallEvent(event, extras); 159 } 160 sendRttRequest()161 public void sendRttRequest() { 162 mCall.sendRttRequest(); 163 } 164 respondToRttRequest(int id, boolean accept)165 public void respondToRttRequest(int id, boolean accept) { 166 mCall.respondToRttRequest(id, accept); 167 } 168 handoverTo(PhoneAccountHandle toHandle, int videoState, Bundle extras)169 public void handoverTo(PhoneAccountHandle toHandle, int videoState, Bundle extras) { 170 mCall.handoverTo(toHandle, videoState, extras); 171 } 172 stopRtt()173 public void stopRtt() { 174 mCall.stopRtt(); 175 } 176 removeExtras(List<String> keys)177 public void removeExtras(List<String> keys) { 178 mCall.removeExtras(keys); 179 } 180 removeExtras(String... keys)181 public void removeExtras(String... keys) { 182 mCall.removeExtras(keys); 183 } 184 185 /** Returns the parent Call id. */ 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 BluetoothCallShimImpl.newInstance() 282 .isSilentRingingRequested(getDetails().getExtras()); 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 isHighDefAudio()309 public boolean isHighDefAudio() { 310 return getDetails().hasProperty(Call.Details.PROPERTY_HIGH_DEF_AUDIO); 311 } 312 getId()313 public Integer getId() { 314 return System.identityHashCode(mCall); 315 } 316 wasConferencePreviouslyMerged()317 public boolean wasConferencePreviouslyMerged() { 318 return can(Call.Details.CAPABILITY_SWAP_CONFERENCE) 319 && !can(Call.Details.CAPABILITY_MERGE_CONFERENCE); 320 } 321 getDisconnectCause()322 public DisconnectCause getDisconnectCause() { 323 return getDetails().getDisconnectCause(); 324 } 325 326 /** Returns the list of ids of corresponding Call List. */ getIds(List<Call> calls)327 public static List<Integer> getIds(List<Call> calls) { 328 List<Integer> result = new ArrayList<>(); 329 for (Call call : calls) { 330 if (call != null) { 331 result.add(System.identityHashCode(call)); 332 } 333 } 334 return result; 335 } 336 hasProperty(int property)337 public boolean hasProperty(int property) { 338 return getDetails().hasProperty(property); 339 } 340 } 341