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