1 /*
2  * Copyright (C) 2021 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.telephony.cts;
18 
19 import android.content.Intent;
20 import android.os.Bundle;
21 import android.telecom.Call;
22 import android.telecom.InCallService;
23 import android.util.Log;
24 
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.Semaphore;
30 import java.util.concurrent.TimeUnit;
31 
32 public class InCallServiceStateValidator  extends InCallService {
33 
34     private static final String LOG_TAG = "InCallServiceStateValidator";
35     private static final Object sLock = new Object();
36     private static InCallServiceCallbacks sCallbacks;
37     private static CountDownLatch sLatch  = new CountDownLatch(1);
38 
39     private final List<Call> mCalls = Collections.synchronizedList(new ArrayList<>());
40     private final List<Call> mConferenceCalls = Collections.synchronizedList(new ArrayList<>());
41     private final Object mLock = new Object();
42     private boolean mIsServiceBound = false;
43 
44     public static class InCallServiceCallbacks {
45         private InCallServiceStateValidator mService = null;
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) {};
onChildrenChanged(Call call, List<Call> children)51         public void onChildrenChanged(Call call, List<Call> children) {};
onConnectionEvent(Call call, String event, Bundle extras)52         public void onConnectionEvent(Call call, String event, Bundle extras) {};
53 
getService()54         public InCallServiceStateValidator getService() {
55             if (mService == null) {
56                 try {
57                     sLatch.await(3000, TimeUnit.MILLISECONDS);
58                 } catch (InterruptedException e) {
59                     Log.d(LOG_TAG, "InterruptedException");
60                 }
61                 sLatch  = new CountDownLatch(1);
62             }
63             return mService;
64         }
65 
setService(InCallServiceStateValidator service)66         public void setService(InCallServiceStateValidator service) {
67             mService = service;
68         }
69 
resetLock()70         public void resetLock() {
71             lock = new Semaphore(0);
72         }
73     }
74 
setCallbacks(InCallServiceCallbacks callbacks)75     public static void setCallbacks(InCallServiceCallbacks callbacks) {
76         synchronized (sLock) {
77             sCallbacks = callbacks;
78         }
79     }
80 
getCallbacks()81     private InCallServiceCallbacks getCallbacks() {
82         synchronized (sLock) {
83             if (sCallbacks != null) {
84                 sCallbacks.setService(this);
85             }
86             return sCallbacks;
87         }
88     }
89 
90     /**
91      * Note that the super implementations of the callback methods are all no-ops, but we call
92      * them anyway to make sure that the CTS coverage tool detects that we are testing them.
93      */
94     private Call.Callback mCallCallback = new Call.Callback() {
95         @Override
96         public void onStateChanged(Call call, int state) {
97             super.onStateChanged(call, state);
98             if (getCallbacks() != null) {
99                 getCallbacks().onCallStateChanged(call, state);
100             }
101         }
102 
103         @Override
104         public void onChildrenChanged(Call call, List<Call> children) {
105             super.onChildrenChanged(call, children);
106             if (getCallbacks() != null) {
107                 getCallbacks().onChildrenChanged(call, children);
108             }
109         }
110 
111         @Override
112         public void onConnectionEvent(Call call, String event, Bundle extras) {
113             super.onConnectionEvent(call, event, extras);
114             if (getCallbacks() != null) {
115                 getCallbacks().onConnectionEvent(call, event, extras);
116             }
117         }
118     };
119 
120     @Override
onBind(android.content.Intent intent)121     public android.os.IBinder onBind(android.content.Intent intent) {
122         if (getCallbacks() != null) {
123             getCallbacks().setService(this);
124         }
125         mIsServiceBound = true;
126         sLatch.countDown();
127         return super.onBind(intent);
128     }
129 
130     @Override
onUnbind(Intent intent)131     public boolean onUnbind(Intent intent) {
132         mIsServiceBound = false;
133         mCalls.clear();
134         return super.onUnbind(intent);
135     }
136 
137     @Override
onCallAdded(Call call)138     public void onCallAdded(Call call) {
139         super.onCallAdded(call);
140         if (call.getDetails().hasProperty(Call.Details.PROPERTY_CONFERENCE)) {
141             if (!mConferenceCalls.contains(call)) {
142                 mConferenceCalls.add(call);
143                 call.registerCallback(mCallCallback);
144             }
145         } else {
146             if (!mCalls.contains(call)) {
147                 mCalls.add(call);
148                 call.registerCallback(mCallCallback);
149             }
150         }
151 
152         if (getCallbacks() != null) {
153             getCallbacks().onCallAdded(call, mCalls.size());
154         }
155     }
156 
157     @Override
onCallRemoved(Call call)158     public void onCallRemoved(Call call) {
159         super.onCallRemoved(call);
160         if (call.getDetails().hasProperty(Call.Details.PROPERTY_CONFERENCE)) {
161             mConferenceCalls.remove(call);
162         } else {
163             mCalls.remove(call);
164         }
165 
166         if (getCallbacks() != null) {
167             getCallbacks().onCallRemoved(call, mCalls.size());
168         }
169         call.unregisterCallback(mCallCallback);
170     }
171 
172     /**
173     * @return the number of calls currently added to the {@code InCallService}.
174     */
getCallCount()175     public int getCallCount() {
176         return mCalls.size();
177     }
178 
isServiceBound()179     public boolean isServiceBound() {
180         return mIsServiceBound;
181     }
182 
isServiceUnBound()183     public boolean isServiceUnBound() {
184         return !mIsServiceBound;
185     }
186 
187     /**
188      * @return the number of conference calls currently added to the {@code InCallService}.
189      */
getConferenceCallCount()190     public int getConferenceCallCount() {
191         Log.d(LOG_TAG, "getConferenceCallCount = " + mConferenceCalls.size());
192         return mConferenceCalls.size();
193     }
194 
195     /**
196      * @return the most recently added conference call that exists inside the {@code InCallService}
197      */
getLastConferenceCall()198     public Call getLastConferenceCall() {
199         if (!mConferenceCalls.isEmpty()) {
200             return mConferenceCalls.get(mConferenceCalls.size() - 1);
201         }
202         return null;
203     }
204 }
205