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