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