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