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