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