1 /*
2  * Copyright (C) 2018 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.thirdptyincallservice;
18 
19 import android.content.Intent;
20 import android.os.Bundle;
21 import android.telecom.Call;
22 import android.telecom.cts.MockInCallService;
23 import android.util.Log;
24 
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.concurrent.CountDownLatch;
28 import java.util.concurrent.TimeUnit;
29 
30 public class CtsThirdPartyInCallService extends MockInCallService {
31 
32     public static final String PACKAGE_NAME = "android.telecom.cts.thirdptyincallservice";
33     private static final String TAG = CtsThirdPartyInCallService.class.getSimpleName();
34     protected static final int TIMEOUT = 10000;
35 
36     private static CtsThirdPartyInCallService sInstance;
37     private static CountDownLatch sServiceBoundLatch = new CountDownLatch(1);
38     private static CountDownLatch sServiceUnboundlatch = new CountDownLatch(1);
39     private CountDownLatch mExtrasChangedLatch = new CountDownLatch(1);
40     private String mExpectedKey;
41     private String mExpectedValue;
42 
43     private static Set<Call> sCalls = new HashSet<>();
44 
45     private Call.Callback mCallback = new Call.Callback() {
46         @Override
47         public void onDetailsChanged(Call call, Call.Details details) {
48             Bundle extras = details.getExtras();
49             if (extras != null && extras.containsKey(mExpectedKey)
50                     && extras.getString(mExpectedKey).equals(mExpectedValue)) {
51                 mExtrasChangedLatch.countDown();
52             }
53         }
54     };
55 
56     /**
57      * Used to bind a call
58      * @param intent
59      * @return
60      */
61     @Override
onBind(Intent intent)62     public android.os.IBinder onBind(Intent intent) {
63         sInstance = this;
64         long olderState = sServiceBoundLatch.getCount();
65         sServiceBoundLatch.countDown();
66         Log.d(TAG, "In Call Service on bind, " + olderState + " -> " + sServiceBoundLatch);
67         mExtrasChangedLatch = new CountDownLatch(1);
68         return super.onBind(intent);
69     }
70 
71     /**
72      * Used to unbind a call
73      * @param intent
74      * @return
75      */
76     @Override
onUnbind(Intent intent)77     public boolean onUnbind(Intent intent) {
78         long olderState = sServiceUnboundlatch.getCount();
79         sServiceUnboundlatch.countDown();
80         Log.d(TAG, "In Call Service unbind, " + olderState + " -> " + sServiceUnboundlatch);
81         return super.onUnbind(intent);
82     }
83 
84     @Override
onCallAdded(Call call)85     public void onCallAdded(Call call) {
86         Log.i(TAG, "onCallAdded");
87         super.onCallAdded(call);
88         sCalls.add(call);
89     }
90 
91     @Override
onCallRemoved(Call call)92     public void onCallRemoved(Call call) {
93         Log.i(TAG, "onCallRemoved");
94         super.onCallRemoved(call);
95         sCalls.add(call);
96     }
97 
checkLatch(CountDownLatch latch)98     private static boolean checkLatch(CountDownLatch latch) {
99         synchronized (sLock) {
100             boolean success = false;
101             try {
102                 success = latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
103             } catch (InterruptedException e) {
104                 success = false;
105             }
106             return success;
107         }
108     }
109 
resetLatchForServiceBound(boolean bind)110     public static void resetLatchForServiceBound(boolean bind) {
111         synchronized (sLock) {
112             if (bind) {
113                 // Set bind to true, reset unbind latch for unbinding.
114                 sServiceUnboundlatch = new CountDownLatch(1);
115             } else {
116                 // Set bind to false, reset bind latch for binding.
117                 sServiceBoundLatch = new CountDownLatch(1);
118             }
119         }
120     }
121 
checkBindStatus(boolean bind)122     public static boolean checkBindStatus(boolean bind) {
123         Log.i(CtsThirdPartyInCallService.class.getSimpleName(),
124                 "checking latch status: service " + (bind ? "bound" : "not bound"));
125         return bind ?
126                 checkLatch(sServiceBoundLatch)
127                 : checkLatch(sServiceUnboundlatch);
128     }
129 
resetCalls()130     public static void resetCalls() {
131         synchronized (sLock) {
132             Log.i(TAG, "clearing all the third party calls.");
133             for (Call c : sCalls) {
134                 c.disconnect();
135             }
136             sCalls.clear();
137         }
138     }
139 
getLocalCallCount()140     public static int getLocalCallCount() {
141         synchronized (sLock) {
142             return sCalls.size();
143         }
144     }
145 
getInstance()146     public static CtsThirdPartyInCallService getInstance() {
147         return sInstance;
148     }
149 
setExpectedExtra(String newKey, String newValue)150     public void setExpectedExtra(String newKey, String newValue) {
151         mExpectedKey = newKey;
152         mExpectedValue = newValue;
153     }
154 
waitForExtrasChanged()155     public boolean waitForExtrasChanged() {
156         try {
157             mExtrasChangedLatch.await(5000, TimeUnit.MILLISECONDS);
158         } catch (InterruptedException e) {
159             return false;
160         }
161 
162         // Make sure one of the calls had the expected extra
163         return sCalls.stream().filter(c -> c.getDetails().getExtras() != null
164                 && c.getDetails().getExtras().containsKey(mExpectedKey)
165                 && c.getDetails().getExtras().getString(mExpectedKey).equals(mExpectedValue))
166                 .count() > 0;
167     }
168 }
169