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