1 /* 2 * Copyright (C) 2015 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 android.telecom.cts; 18 19 import static org.junit.Assert.assertTrue; 20 21 import android.content.Intent; 22 import android.os.Bundle; 23 import android.telecom.Call; 24 import android.telecom.CallAudioState; 25 import android.telecom.InCallService; 26 import android.util.ArrayMap; 27 import android.util.Log; 28 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.concurrent.Semaphore; 34 35 public class MockInCallService extends InCallService { 36 private static String LOG_TAG = "MockInCallService"; 37 private final List<Call> mCalls = Collections.synchronizedList(new ArrayList<>()); 38 private final List<Call> mConferenceCalls = Collections.synchronizedList(new ArrayList<>()); 39 private static InCallServiceCallbacks sCallbacks; 40 private Map<Call, MockVideoCallCallback> mVideoCallCallbacks = 41 new ArrayMap<Call, MockVideoCallCallback>(); 42 43 protected static final Object sLock = new Object(); 44 private static boolean mIsServiceBound = false; 45 46 public static abstract class InCallServiceCallbacks { 47 private MockInCallService mService; 48 public Semaphore lock = new Semaphore(0); 49 onCallAdded(Call call, int numCalls)50 public void onCallAdded(Call call, int numCalls) {}; onCallRemoved(Call call, int numCalls)51 public void onCallRemoved(Call call, int numCalls) {}; onCallStateChanged(Call call, int state)52 public void onCallStateChanged(Call call, int state) {}; onParentChanged(Call call, Call parent)53 public void onParentChanged(Call call, Call parent) {}; onChildrenChanged(Call call, List<Call> children)54 public void onChildrenChanged(Call call, List<Call> children) {}; onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls)55 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}; onCallDestroyed(Call call)56 public void onCallDestroyed(Call call) {}; onDetailsChanged(Call call, Call.Details details)57 public void onDetailsChanged(Call call, Call.Details details) {}; onCanAddCallsChanged(boolean canAddCalls)58 public void onCanAddCallsChanged(boolean canAddCalls) {} onBringToForeground(boolean showDialpad)59 public void onBringToForeground(boolean showDialpad) {} onCallAudioStateChanged(CallAudioState audioState)60 public void onCallAudioStateChanged(CallAudioState audioState) {} onPostDialWait(Call call, String remainingPostDialSequence)61 public void onPostDialWait(Call call, String remainingPostDialSequence) {} onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses)62 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {} onSilenceRinger()63 public void onSilenceRinger() {} onConnectionEvent(Call call, String event, Bundle extras)64 public void onConnectionEvent(Call call, String event, Bundle extras) {} onRttModeChanged(Call call, int mode)65 public void onRttModeChanged(Call call, int mode) {} onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall)66 public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) {} onRttRequest(Call call, int id)67 public void onRttRequest(Call call, int id) {} onRttInitiationFailure(Call call, int reason)68 public void onRttInitiationFailure(Call call, int reason) {} onHandoverComplete(Call call)69 public void onHandoverComplete(Call call) {} onHandoverFailed(Call call, int failureReason)70 public void onHandoverFailed(Call call, int failureReason) {} 71 getService()72 final public MockInCallService getService() { 73 return mService; 74 } 75 setService(MockInCallService service)76 final public void setService(MockInCallService service) { 77 mService = service; 78 } 79 resetLock()80 public void resetLock() { 81 lock = new Semaphore(0); 82 } 83 } 84 85 /** 86 * Note that the super implementations of the callback methods are all no-ops, but we call 87 * them anyway to make sure that the CTS coverage tool detects that we are testing them. 88 */ 89 private Call.Callback mCallCallback = new Call.Callback() { 90 @Override 91 public void onStateChanged(Call call, int state) { 92 super.onStateChanged(call, state); 93 if (getCallbacks() != null) { 94 getCallbacks().onCallStateChanged(call, state); 95 } 96 } 97 98 @Override 99 public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) { 100 super.onVideoCallChanged(call, videoCall); 101 saveVideoCall(call, videoCall); 102 } 103 104 @Override 105 public void onParentChanged(Call call, Call parent) { 106 super.onParentChanged(call, parent); 107 if (getCallbacks() != null) { 108 getCallbacks().onParentChanged(call, parent); 109 } 110 } 111 112 @Override 113 public void onChildrenChanged(Call call, List<Call> children) { 114 super.onChildrenChanged(call, children); 115 if (getCallbacks() != null) { 116 getCallbacks().onChildrenChanged(call, children); 117 } 118 } 119 120 @Override 121 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) { 122 super.onConferenceableCallsChanged(call, conferenceableCalls); 123 if (getCallbacks() != null) { 124 getCallbacks().onConferenceableCallsChanged(call, conferenceableCalls); 125 } 126 } 127 128 @Override 129 public void onCallDestroyed(Call call) { 130 super.onCallDestroyed(call); 131 if (getCallbacks() != null) { 132 getCallbacks().onCallDestroyed(call); 133 } 134 } 135 136 @Override 137 public void onDetailsChanged(Call call, Call.Details details) { 138 super.onDetailsChanged(call, details); 139 if (getCallbacks() != null) { 140 getCallbacks().onDetailsChanged(call, details); 141 } 142 } 143 144 @Override 145 public void onPostDialWait(Call call, String remainingPostDialSequence) { 146 super.onPostDialWait(call, remainingPostDialSequence); 147 if (getCallbacks() != null) { 148 getCallbacks().onPostDialWait(call, remainingPostDialSequence); 149 } 150 } 151 152 @Override 153 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) { 154 super.onCannedTextResponsesLoaded(call, cannedTextResponses); 155 if (getCallbacks() != null) { 156 getCallbacks().onCannedTextResponsesLoaded(call, cannedTextResponses); 157 } 158 } 159 160 @Override 161 public void onConnectionEvent(Call call, String event, Bundle extras) { 162 super.onConnectionEvent(call, event, extras); 163 if (getCallbacks() != null) { 164 getCallbacks().onConnectionEvent(call, event, extras); 165 } 166 } 167 168 @Override 169 public void onRttModeChanged(Call call, int mode) { 170 super.onRttModeChanged(call, mode); 171 if (getCallbacks() != null) { 172 getCallbacks().onRttModeChanged(call, mode); 173 } 174 } 175 176 @Override 177 public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) { 178 super.onRttStatusChanged(call, enabled, rttCall); 179 if (getCallbacks() != null) { 180 getCallbacks().onRttStatusChanged(call, enabled, rttCall); 181 } 182 } 183 184 @Override 185 public void onRttRequest(Call call, int id) { 186 super.onRttRequest(call, id); 187 if (getCallbacks() != null) { 188 getCallbacks().onRttRequest(call, id); 189 } 190 } 191 192 @Override 193 public void onRttInitiationFailure(Call call, int reason) { 194 super.onRttInitiationFailure(call, reason); 195 if (getCallbacks() != null) { 196 getCallbacks().onRttInitiationFailure(call, reason); 197 } 198 } 199 200 @Override 201 public void onHandoverComplete(Call call) { 202 super.onHandoverComplete(call); 203 if (getCallbacks() != null) { 204 getCallbacks().onHandoverComplete(call); 205 } 206 } 207 208 @Override 209 public void onHandoverFailed(Call call, int failureReason) { 210 super.onHandoverFailed(call, failureReason); 211 if (getCallbacks() != null) { 212 getCallbacks().onHandoverFailed(call, failureReason); 213 } 214 } 215 }; 216 saveVideoCall(Call call, VideoCall videoCall)217 private void saveVideoCall(Call call, VideoCall videoCall) { 218 if (videoCall != null) { 219 if (!mVideoCallCallbacks.containsKey(call)) { 220 MockVideoCallCallback listener = new MockVideoCallCallback(call); 221 videoCall.registerCallback(listener); 222 mVideoCallCallbacks.put(call, listener); 223 } 224 } else { 225 mVideoCallCallbacks.remove(call); 226 } 227 } 228 229 @Override onBind(android.content.Intent intent)230 public android.os.IBinder onBind(android.content.Intent intent) { 231 Log.i(LOG_TAG, "Service bounded"); 232 if (getCallbacks() != null) { 233 getCallbacks().setService(this); 234 } 235 mIsServiceBound = true; 236 return super.onBind(intent); 237 } 238 239 @Override onCallAdded(Call call)240 public void onCallAdded(Call call) { 241 super.onCallAdded(call); 242 if (call.getDetails().hasProperty(Call.Details.PROPERTY_CONFERENCE) == true) { 243 if (!mConferenceCalls.contains(call)) { 244 mConferenceCalls.add(call); 245 call.registerCallback(mCallCallback); 246 } 247 } else { 248 if (!mCalls.contains(call)) { 249 mCalls.add(call); 250 call.registerCallback(mCallCallback); 251 VideoCall videoCall = call.getVideoCall(); 252 if (videoCall != null) { 253 saveVideoCall(call, videoCall); 254 } 255 } 256 } 257 if (getCallbacks() != null) { 258 getCallbacks().onCallAdded(call, mCalls.size() + mConferenceCalls.size()); 259 } 260 } 261 262 @Override onCallRemoved(Call call)263 public void onCallRemoved(Call call) { 264 super.onCallRemoved(call); 265 if (call.getDetails().hasProperty(Call.Details.PROPERTY_CONFERENCE) == true) { 266 mConferenceCalls.remove(call); 267 } else { 268 mCalls.remove(call); 269 } 270 if (getCallbacks() != null) { 271 getCallbacks().onCallRemoved(call, mCalls.size() + mConferenceCalls.size()); 272 saveVideoCall(call, null /* remove videoCall */); 273 } 274 } 275 276 @Override onCanAddCallChanged(boolean canAddCall)277 public void onCanAddCallChanged(boolean canAddCall) { 278 super.onCanAddCallChanged(canAddCall); 279 if (getCallbacks() != null) { 280 getCallbacks().onCanAddCallsChanged(canAddCall); 281 } 282 } 283 284 @Override onBringToForeground(boolean showDialpad)285 public void onBringToForeground(boolean showDialpad) { 286 super.onBringToForeground(showDialpad); 287 if (getCallbacks() != null) { 288 getCallbacks().onBringToForeground(showDialpad); 289 } 290 } 291 292 @Override onCallAudioStateChanged(CallAudioState audioState)293 public void onCallAudioStateChanged(CallAudioState audioState) { 294 super.onCallAudioStateChanged(audioState); 295 if (getCallbacks() != null) { 296 getCallbacks().onCallAudioStateChanged(audioState); 297 } 298 } 299 300 @Override onSilenceRinger()301 public void onSilenceRinger(){ 302 super.onSilenceRinger(); 303 if(getCallbacks() != null) { 304 getCallbacks().onSilenceRinger(); 305 } 306 } 307 308 /** 309 * @return the number of calls currently added to the {@code InCallService}. 310 */ getCallCount()311 public int getCallCount() { 312 return mCalls.size(); 313 } 314 315 /** 316 * @return the number of conference calls currently added to the {@code InCallService}. 317 */ getConferenceCallCount()318 public int getConferenceCallCount() { 319 return mConferenceCalls.size(); 320 } 321 322 /** 323 * @return the most recently added call that exists inside the {@code InCallService} 324 */ getLastCall()325 public Call getLastCall() { 326 if (!mCalls.isEmpty()) { 327 return mCalls.get(mCalls.size() - 1); 328 } 329 return null; 330 } 331 332 /** 333 * @return the most recently added conference call that exists inside the {@code InCallService} 334 */ getLastConferenceCall()335 public Call getLastConferenceCall() { 336 if (!mConferenceCalls.isEmpty()) { 337 return mConferenceCalls.get(mConferenceCalls.size() - 1); 338 } 339 return null; 340 } 341 disconnectLastCall()342 public void disconnectLastCall() { 343 final Call call = getLastCall(); 344 if (call != null) { 345 call.disconnect(); 346 } 347 } 348 disconnectLastConferenceCall()349 public void disconnectLastConferenceCall() { 350 final Call call = getLastConferenceCall(); 351 if (call != null) { 352 call.disconnect(); 353 } 354 } 355 disconnectAllCalls()356 public void disconnectAllCalls() { 357 synchronized (mCalls) { 358 for (final Call call : mCalls) { 359 call.disconnect(); 360 } 361 } 362 } 363 disconnectAllConferenceCalls()364 public void disconnectAllConferenceCalls() { 365 synchronized (mConferenceCalls) { 366 for (final Call call : mConferenceCalls) { 367 call.disconnect(); 368 } 369 } 370 } 371 setCallbacks(InCallServiceCallbacks callbacks)372 public static void setCallbacks(InCallServiceCallbacks callbacks) { 373 synchronized (sLock) { 374 sCallbacks = callbacks; 375 } 376 } 377 getCallbacks()378 private InCallServiceCallbacks getCallbacks() { 379 synchronized (sLock) { 380 if (sCallbacks != null) { 381 sCallbacks.setService(this); 382 } 383 return sCallbacks; 384 } 385 } 386 387 /** 388 * Determines if a video callback has been registered for the passed in call. 389 * 390 * @param call The call. 391 * @return {@code true} if a video callback has been registered. 392 */ isVideoCallbackRegistered(Call call)393 public boolean isVideoCallbackRegistered(Call call) { 394 return mVideoCallCallbacks.containsKey(call); 395 } 396 397 /** 398 * Retrieves the video callbacks associated with a call. 399 * @param call The call. 400 * @return The {@link MockVideoCallCallback} instance associated with the call. 401 */ getVideoCallCallback(Call call)402 public MockVideoCallCallback getVideoCallCallback(Call call) { 403 return mVideoCallCallbacks.get(call); 404 } 405 406 @Override onUnbind(Intent intent)407 public boolean onUnbind(Intent intent) { 408 Log.i(LOG_TAG, "Service has been unbound"); 409 assertTrue(mIsServiceBound); 410 mIsServiceBound = false; 411 mCalls.clear(); 412 return super.onUnbind(intent); 413 } 414 isServiceBound()415 public static boolean isServiceBound() { 416 return mIsServiceBound; 417 } 418 } 419