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