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