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