1 /* 2 * Copyright (C) 2023 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.apps; 18 19 import static android.os.SystemClock.sleep; 20 import static android.telecom.cts.apps.StackTraceUtil.appendStackTraceList; 21 22 import static org.junit.Assert.assertEquals; 23 24 import android.os.SystemClock; 25 import android.telecom.Connection; 26 import android.telecom.ConnectionRequest; 27 28 import java.util.List; 29 import java.util.Objects; 30 import java.util.concurrent.CountDownLatch; 31 32 public class WaitUntil { 33 public static final long DEFAULT_TIMEOUT_MS = 10000; 34 private static final String CLASS_NAME = WaitUntil.class.getCanonicalName(); 35 private static final String TELECOM_ID_TOKEN = "_"; 36 37 // NOTE: 38 // - This method should NOT be called from a telecom test app. The assertEquals will cause 39 // a DeadObjectException which will make any test failure log unreadable! 40 // - This can be used for classes like BindUtils, BaseAppVerifierImpl, etc. that are running 41 // in the CTS test process waitUntilConditionIsTrueOrTimeout( Condition condition, long timeout, String description)42 public static void waitUntilConditionIsTrueOrTimeout( 43 Condition condition, 44 long timeout, 45 String description) { 46 47 long startTimeMillis = SystemClock.elapsedRealtime(); 48 long remainingTimeMillis = timeout; 49 long elapsedTimeMillis; 50 51 while (!Objects.equals(condition.expected(), condition.actual()) 52 && remainingTimeMillis > 0) { 53 sleep(50); 54 elapsedTimeMillis = SystemClock.elapsedRealtime() - startTimeMillis; 55 remainingTimeMillis = timeout - elapsedTimeMillis; 56 } 57 assertEquals(description, condition.expected(), condition.actual()); 58 } 59 60 // NOTE: 61 // - This method should NOT be called from a telecom test app. The assertEquals will cause 62 // a DeadObjectException which will make any test failure log unreadable! 63 // - This can be used for classes like BindUtils, BaseAppVerifierImpl, etc. that are running 64 // in the CTS test process waitUntilConditionIsTrueOrTimeout( Condition condition)65 public static void waitUntilConditionIsTrueOrTimeout( 66 Condition condition) { 67 long startTimeMillis = SystemClock.elapsedRealtime(); 68 long remainingTimeMillis = DEFAULT_TIMEOUT_MS; 69 long elapsedTimeMillis; 70 71 while (!Objects.equals(condition.expected(), condition.actual()) 72 && remainingTimeMillis > 0) { 73 sleep(50); 74 elapsedTimeMillis = SystemClock.elapsedRealtime() - startTimeMillis; 75 remainingTimeMillis = DEFAULT_TIMEOUT_MS - elapsedTimeMillis; 76 } 77 assertEquals(condition.expected(), condition.actual()); 78 } 79 80 // This helper is intended for test apps! waitUntilConditionIsTrueOrReturnFalse( Condition condition)81 private static boolean waitUntilConditionIsTrueOrReturnFalse( 82 Condition condition) { 83 long startTimeMillis = SystemClock.elapsedRealtime(); 84 long remainingTimeMillis = DEFAULT_TIMEOUT_MS; 85 long elapsedTimeMillis; 86 87 while (!Objects.equals(condition.expected(), condition.actual()) 88 && remainingTimeMillis > 0) { 89 sleep(50); 90 elapsedTimeMillis = SystemClock.elapsedRealtime() - startTimeMillis; 91 remainingTimeMillis = DEFAULT_TIMEOUT_MS - elapsedTimeMillis; 92 } 93 return (condition.expected().equals(condition.actual())); 94 } 95 96 97 public interface ConnectionServiceImpl { getLastConnection()98 Connection getLastConnection(); 99 getLastFailedRequest()100 ConnectionRequest getLastFailedRequest(); 101 getCreateConnectionLatch(boolean isOutgoing)102 CountDownLatch getCreateConnectionLatch(boolean isOutgoing); 103 } 104 waitUntilIdIsSet( String packageName, List<String> stackTrace, Connection connection)105 public static String waitUntilIdIsSet( 106 String packageName, 107 List<String> stackTrace, 108 Connection connection) { 109 110 boolean success = waitUntilConditionIsTrueOrReturnFalse( 111 new Condition() { 112 @Override 113 public Object expected() { 114 return true; 115 } 116 117 @Override 118 public Object actual() { 119 return connection.getTelecomCallId() != null 120 && !(connection.getTelecomCallId().equals("")); 121 } 122 } 123 ); 124 125 if (!success) { 126 throw new TestAppException(packageName, 127 appendStackTraceList(stackTrace, 128 CLASS_NAME + ".waitUntilIdIsSet"), 129 "expected:<Connection#getCallId() to return an id within the time window>" 130 + "actual:<hit timeout waiting for the Connection#getCallId() to be " 131 + "set>"); 132 } 133 134 return extractTelecomId(connection); 135 } 136 waitUntilCallAudioStateIsSet( String packageName, List<String> stackTrace, boolean isManaged, Connection connection)137 public static void waitUntilCallAudioStateIsSet( 138 String packageName, 139 List<String> stackTrace, 140 boolean isManaged, 141 Connection connection) { 142 boolean success = waitUntilConditionIsTrueOrReturnFalse( 143 new Condition() { 144 @Override 145 public Object expected() { 146 return true; 147 } 148 149 @Override 150 public Object actual() { 151 if (isManaged) { 152 return ((ManagedConnection) connection).getCurrentCallEndpointFromCallback() 153 != null; 154 } else { 155 return ((VoipConnection) connection).getCurrentCallEndpointFromCallback() 156 != null; 157 } 158 } 159 } 160 ); 161 162 if (!success) { 163 throw new TestAppException(packageName, 164 appendStackTraceList(stackTrace, 165 CLASS_NAME + ".waitUntilCallAudioStateIsSet"), 166 "expected:<Connection#onCallEndpointChanged() to set" 167 + " Connection#mCallEndpoints within the time window> " 168 + "actual:<hit timeout waiting for the CallEndpoint to be set>"); 169 } 170 } 171 waitUntilAvailableEndpointsIsSet( String packageName, List<String> stackTrace, boolean isManaged, Connection connection)172 public static void waitUntilAvailableEndpointsIsSet( 173 String packageName, 174 List<String> stackTrace, 175 boolean isManaged, 176 Connection connection) { 177 boolean success = waitUntilConditionIsTrueOrReturnFalse( 178 new Condition() { 179 @Override 180 public Object expected() { 181 return true; 182 } 183 184 @Override 185 public Object actual() { 186 if (isManaged) { 187 return ((ManagedConnection) connection).getCallEndpoints() 188 != null; 189 } else { 190 return ((VoipConnection) connection).getCallEndpoints() 191 != null; 192 } 193 } 194 } 195 ); 196 197 if (!success) { 198 throw new TestAppException(packageName, 199 appendStackTraceList(stackTrace, 200 CLASS_NAME + ".waitUntilAvailableEndpointsIsSet"), 201 "expected:<Connection#onAvailableCallEndpointsChanged() to set" 202 + " ManagedConnection#mCallEndpoints within the time window> " 203 + "actual:<hit timeout waiting for the CallEndpoints to be set>"); 204 } 205 } 206 waitUntilConnectionIsNonNull( String packageName, List<String> stackTrace, ConnectionServiceImpl s)207 public static Connection waitUntilConnectionIsNonNull( 208 String packageName, 209 List<String> stackTrace, 210 ConnectionServiceImpl s) { 211 212 boolean success = waitUntilConditionIsTrueOrReturnFalse( 213 new Condition() { 214 @Override 215 public Object expected() { 216 return true; 217 } 218 219 @Override 220 public Object actual() { 221 return getLastConnection(s) != null; 222 } 223 } 224 ); 225 226 if (!success) { 227 throw new TestAppException(packageName, 228 appendStackTraceList(stackTrace, 229 CLASS_NAME + ".waitUntilConnectionIsNonNull_Voip"), 230 "expected:<Connection to be added to the ConnectionService> " 231 + "actual:<hit timeout waiting for Connection>"); 232 } 233 234 return getLastConnection(s); 235 } 236 waitUntilConnectionFails( String packageName, List<String> stackTrace, ConnectionServiceImpl s)237 public static ConnectionRequest waitUntilConnectionFails( 238 String packageName, List<String> stackTrace, ConnectionServiceImpl s) { 239 240 boolean success = 241 waitUntilConditionIsTrueOrReturnFalse( 242 new Condition() { 243 @Override 244 public Object expected() { 245 return true; 246 } 247 248 @Override 249 public Object actual() { 250 return getLastFailedRequest(s) != null; 251 } 252 }); 253 254 if (!success) { 255 throw new TestAppException( 256 packageName, 257 appendStackTraceList(stackTrace, CLASS_NAME + ".waitUntilConnectionFails"), 258 "expected:<Connection failed to be added to the ConnectionService> " 259 + "actual:<no failed Connection detected>"); 260 } 261 262 return getLastFailedRequest(s); 263 } 264 waitUntilManagedCreateConnectionInvoked( ConnectionServiceImpl s, boolean isOutgoing)265 public static boolean waitUntilManagedCreateConnectionInvoked( 266 ConnectionServiceImpl s, boolean isOutgoing) { 267 268 return waitUntilConditionIsTrueOrReturnFalse( 269 new Condition() { 270 @Override 271 public Object expected() { 272 return true; 273 } 274 275 @Override 276 public Object actual() { 277 return getCreateConnectionLatch(s, isOutgoing).getCount() == 0; 278 } 279 }); 280 } 281 282 private static String extractTelecomId(Connection connection) { 283 String str = connection.getTelecomCallId(); 284 return str.substring(0, str.indexOf(TELECOM_ID_TOKEN)); 285 } 286 287 private static Connection getLastConnection(ConnectionServiceImpl s) { 288 return s.getLastConnection(); 289 } 290 291 private static ConnectionRequest getLastFailedRequest(ConnectionServiceImpl s) { 292 return s.getLastFailedRequest(); 293 } 294 295 private static CountDownLatch getCreateConnectionLatch( 296 ConnectionServiceImpl s, boolean isOutgoing) { 297 return s.getCreateConnectionLatch(isOutgoing); 298 } 299 300 public static void waitUntilCurrentCallEndpointIsSet( 301 String packageName, 302 List<String> stackTrace, 303 TransactionalCallEvents events) throws TestAppException { 304 boolean success = WaitUntil.waitUntilConditionIsTrueOrReturnFalse( 305 new Condition() { 306 @Override 307 public Object expected() { 308 return true; 309 } 310 311 @Override 312 public Object actual() { 313 return events.getCurrentCallEndpoint() != null; 314 } 315 } 316 ); 317 318 if (!success) { 319 throw new TestAppException(packageName, 320 appendStackTraceList(stackTrace, 321 CLASS_NAME + ".waitUntilCurrentCallEndpointIsSet"), 322 "expected:<TransactionalCallEvents#onCallEndpointChanged() to set" 323 + " TransactionalCallEvents#mCurrentCallEndpoint within time window>" 324 + " actual:<hit timeout waiting for the CallEndpoint to be set>"); 325 } 326 } 327 328 public static void waitUntilAvailableEndpointAreSet( 329 String packageName, 330 List<String> stackTrace, 331 TransactionalCallEvents events) throws TestAppException { 332 333 boolean success = WaitUntil.waitUntilConditionIsTrueOrReturnFalse( 334 new Condition() { 335 @Override 336 public Object expected() { 337 return true; 338 } 339 340 @Override 341 public Object actual() { 342 return events.getCallEndpoints() != null; 343 } 344 } 345 ); 346 347 if (!success) { 348 throw new TestAppException(packageName, 349 appendStackTraceList(stackTrace, 350 CLASS_NAME + ".waitUntilAvailableEndpointAreSet"), 351 "expected:<TransactionalCallEvents#onAvailableCallEndpointsChanged() to set" 352 + " TransactionalCallEvents#mCallEndpoints within the time window> " 353 + "actual:<hit timeout waiting for the CallEndpoints to be set>"); 354 } 355 } 356 } 357