1 /* 2 * Copyright (C) 2017 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 package com.android.server.am; 17 18 import static org.mockito.ArgumentMatchers.any; 19 import static org.mockito.ArgumentMatchers.anyInt; 20 import static org.mockito.ArgumentMatchers.eq; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.when; 24 25 import android.app.admin.IDeviceAdminService; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.ServiceConnection; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.UserHandle; 34 import android.test.AndroidTestCase; 35 import android.util.Pair; 36 37 import androidx.test.filters.SmallTest; 38 39 import org.mockito.ArgumentMatchers; 40 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.Collections; 44 45 @SmallTest 46 public class PersistentConnectionTest extends AndroidTestCase { 47 private static final String TAG = "PersistentConnectionTest"; 48 49 private static class MyConnection extends PersistentConnection<IDeviceAdminService> { 50 public long uptimeMillis = 12345; 51 52 public ArrayList<Pair<Runnable, Long>> scheduledRunnables = new ArrayList<>(); 53 MyConnection(String tag, Context context, Handler handler, int userId, ComponentName componentName, long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds, long resetBackoffDelay)54 public MyConnection(String tag, Context context, Handler handler, int userId, 55 ComponentName componentName, long rebindBackoffSeconds, 56 double rebindBackoffIncrease, long rebindMaxBackoffSeconds, 57 long resetBackoffDelay) { 58 super(tag, context, handler, userId, componentName, 59 rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds, 60 resetBackoffDelay); 61 } 62 63 @Override getBindFlags()64 protected int getBindFlags() { 65 return Context.BIND_FOREGROUND_SERVICE; 66 } 67 68 @Override asInterface(IBinder binder)69 protected IDeviceAdminService asInterface(IBinder binder) { 70 return (IDeviceAdminService) binder; 71 } 72 73 @Override injectUptimeMillis()74 long injectUptimeMillis() { 75 return uptimeMillis; 76 } 77 78 @Override injectPostAtTime(Runnable r, long uptimeMillis)79 void injectPostAtTime(Runnable r, long uptimeMillis) { 80 scheduledRunnables.add(Pair.create(r, uptimeMillis)); 81 } 82 83 @Override injectRemoveCallbacks(Runnable r)84 void injectRemoveCallbacks(Runnable r) { 85 for (int i = scheduledRunnables.size() - 1; i >= 0; i--) { 86 if (scheduledRunnables.get(i).first.equals(r)) { 87 scheduledRunnables.remove(i); 88 } 89 } 90 } 91 elapse(long milliSeconds)92 void elapse(long milliSeconds) { 93 uptimeMillis += milliSeconds; 94 95 // Fire the scheduled runnables. 96 97 // Note we collect first and then run all, because sometimes a scheduled runnable 98 // calls removeCallbacks. 99 final ArrayList<Runnable> list = new ArrayList<>(); 100 101 for (int i = scheduledRunnables.size() - 1; i >= 0; i--) { 102 if (scheduledRunnables.get(i).second <= uptimeMillis) { 103 list.add(scheduledRunnables.get(i).first); 104 scheduledRunnables.remove(i); 105 } 106 } 107 108 Collections.reverse(list); 109 for (Runnable r : list) { 110 r.run(); 111 } 112 } 113 } 114 testAll()115 public void testAll() { 116 final Context context = mock(Context.class); 117 final int userId = 11; 118 final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def"); 119 final Handler handler = new Handler(Looper.getMainLooper()); 120 121 final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn, 122 /* rebindBackoffSeconds= */ 5, 123 /* rebindBackoffIncrease= */ 1.5, 124 /* rebindMaxBackoffSeconds= */ 11, 125 /* resetBackoffDelay= */ 999); 126 127 assertFalse(conn.isBound()); 128 assertFalse(conn.isConnected()); 129 assertFalse(conn.isRebindScheduled()); 130 assertEquals(5000, conn.getNextBackoffMsForTest()); 131 assertNull(conn.getServiceBinder()); 132 133 when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(), 134 any(Handler.class), any(UserHandle.class))) 135 .thenReturn(true); 136 137 // Call bind. 138 conn.bind(); 139 140 assertTrue(conn.isBound()); 141 assertTrue(conn.shouldBeBoundForTest()); 142 assertFalse(conn.isConnected()); 143 assertFalse(conn.isRebindScheduled()); 144 assertNull(conn.getServiceBinder()); 145 146 assertEquals(5000, conn.getNextBackoffMsForTest()); 147 148 verify(context).bindServiceAsUser( 149 ArgumentMatchers.argThat(intent -> cn.equals(intent.getComponent())), 150 eq(conn.getServiceConnectionForTest()), 151 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), 152 eq(handler), eq(UserHandle.of(userId))); 153 154 // AM responds... 155 conn.getServiceConnectionForTest().onServiceConnected(cn, 156 new IDeviceAdminService.Stub() {}); 157 158 assertTrue(conn.isBound()); 159 assertTrue(conn.shouldBeBoundForTest()); 160 assertTrue(conn.isConnected()); 161 assertNotNull(conn.getServiceBinder()); 162 assertFalse(conn.isRebindScheduled()); 163 164 assertEquals(5000, conn.getNextBackoffMsForTest()); 165 166 167 // Now connected. 168 169 // Call unbind... 170 conn.unbind(); 171 assertFalse(conn.isBound()); 172 assertFalse(conn.shouldBeBoundForTest()); 173 assertFalse(conn.isConnected()); 174 assertNull(conn.getServiceBinder()); 175 assertFalse(conn.isRebindScheduled()); 176 177 // Caller bind again... 178 conn.bind(); 179 180 assertTrue(conn.isBound()); 181 assertTrue(conn.shouldBeBoundForTest()); 182 assertFalse(conn.isConnected()); 183 assertFalse(conn.isRebindScheduled()); 184 assertNull(conn.getServiceBinder()); 185 186 assertEquals(5000, conn.getNextBackoffMsForTest()); 187 188 189 // Now connected again. 190 191 // The service got killed... 192 conn.getServiceConnectionForTest().onServiceDisconnected(cn); 193 194 assertTrue(conn.isBound()); 195 assertTrue(conn.shouldBeBoundForTest()); 196 assertFalse(conn.isConnected()); 197 assertNull(conn.getServiceBinder()); 198 assertFalse(conn.isRebindScheduled()); 199 200 assertEquals(5000, conn.getNextBackoffMsForTest()); 201 202 // Connected again... 203 conn.getServiceConnectionForTest().onServiceConnected(cn, 204 new IDeviceAdminService.Stub() {}); 205 206 assertTrue(conn.isBound()); 207 assertTrue(conn.shouldBeBoundForTest()); 208 assertTrue(conn.isConnected()); 209 assertNotNull(conn.getServiceBinder()); 210 assertFalse(conn.isRebindScheduled()); 211 212 assertEquals(5000, conn.getNextBackoffMsForTest()); 213 214 215 // Then the binding is "died"... 216 conn.getServiceConnectionForTest().onBindingDied(cn); 217 218 assertFalse(conn.isBound()); 219 assertTrue(conn.shouldBeBoundForTest()); 220 assertFalse(conn.isConnected()); 221 assertNull(conn.getServiceBinder()); 222 assertTrue(conn.isRebindScheduled()); 223 224 assertEquals(7500, conn.getNextBackoffMsForTest()); 225 226 assertEquals( 227 Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(), 228 conn.uptimeMillis + 5000)), 229 conn.scheduledRunnables); 230 231 // 5000 ms later... 232 conn.elapse(5000); 233 234 assertTrue(conn.isBound()); 235 assertTrue(conn.shouldBeBoundForTest()); 236 assertFalse(conn.isConnected()); 237 assertNull(conn.getServiceBinder()); 238 assertFalse(conn.isRebindScheduled()); 239 240 assertEquals(7500, conn.getNextBackoffMsForTest()); 241 242 // Connected. 243 conn.getServiceConnectionForTest().onServiceConnected(cn, 244 new IDeviceAdminService.Stub() {}); 245 246 assertTrue(conn.isBound()); 247 assertTrue(conn.shouldBeBoundForTest()); 248 assertTrue(conn.isConnected()); 249 assertNotNull(conn.getServiceBinder()); 250 assertFalse(conn.isRebindScheduled()); 251 252 assertEquals(7500, conn.getNextBackoffMsForTest()); 253 254 // Then the binding is "died"... 255 conn.getServiceConnectionForTest().onBindingDied(cn); 256 257 assertFalse(conn.isBound()); 258 assertTrue(conn.shouldBeBoundForTest()); 259 assertFalse(conn.isConnected()); 260 assertNull(conn.getServiceBinder()); 261 assertTrue(conn.isRebindScheduled()); 262 263 assertEquals(11000, conn.getNextBackoffMsForTest()); 264 265 assertEquals( 266 Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(), 267 conn.uptimeMillis + 7500)), 268 conn.scheduledRunnables); 269 270 // Later... 271 conn.elapse(7500); 272 273 assertTrue(conn.isBound()); 274 assertTrue(conn.shouldBeBoundForTest()); 275 assertFalse(conn.isConnected()); 276 assertNull(conn.getServiceBinder()); 277 assertFalse(conn.isRebindScheduled()); 278 279 assertEquals(11000, conn.getNextBackoffMsForTest()); 280 281 282 // Then the binding is "died"... 283 conn.getServiceConnectionForTest().onBindingDied(cn); 284 285 assertFalse(conn.isBound()); 286 assertTrue(conn.shouldBeBoundForTest()); 287 assertFalse(conn.isConnected()); 288 assertNull(conn.getServiceBinder()); 289 assertTrue(conn.isRebindScheduled()); 290 291 assertEquals(11000, conn.getNextBackoffMsForTest()); 292 293 assertEquals( 294 Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(), 295 conn.uptimeMillis + 11000)), 296 conn.scheduledRunnables); 297 298 // Call unbind... 299 conn.unbind(); 300 assertFalse(conn.isBound()); 301 assertFalse(conn.shouldBeBoundForTest()); 302 assertFalse(conn.isConnected()); 303 assertNull(conn.getServiceBinder()); 304 assertFalse(conn.isRebindScheduled()); 305 306 // Call bind again... And now the backoff is reset to 5000. 307 conn.bind(); 308 309 assertTrue(conn.isBound()); 310 assertTrue(conn.shouldBeBoundForTest()); 311 assertFalse(conn.isConnected()); 312 assertFalse(conn.isRebindScheduled()); 313 assertNull(conn.getServiceBinder()); 314 315 assertEquals(5000, conn.getNextBackoffMsForTest()); 316 } 317 testReconnectFiresAfterUnbind()318 public void testReconnectFiresAfterUnbind() { 319 final Context context = mock(Context.class); 320 final int userId = 11; 321 final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def"); 322 final Handler handler = new Handler(Looper.getMainLooper()); 323 324 final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn, 325 /* rebindBackoffSeconds= */ 5, 326 /* rebindBackoffIncrease= */ 1.5, 327 /* rebindMaxBackoffSeconds= */ 11, 328 /* resetBackoffDelay= */ 999); 329 330 when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(), 331 any(Handler.class), any(UserHandle.class))) 332 .thenReturn(true); 333 334 // Bind. 335 conn.bind(); 336 337 assertTrue(conn.isBound()); 338 assertTrue(conn.shouldBeBoundForTest()); 339 assertFalse(conn.isRebindScheduled()); 340 341 conn.elapse(1000); 342 343 // Service crashes. 344 conn.getServiceConnectionForTest().onBindingDied(cn); 345 346 assertFalse(conn.isBound()); 347 assertTrue(conn.shouldBeBoundForTest()); 348 assertTrue(conn.isRebindScheduled()); 349 350 assertEquals(7500, conn.getNextBackoffMsForTest()); 351 352 // Call unbind. 353 conn.unbind(); 354 assertFalse(conn.isBound()); 355 assertFalse(conn.shouldBeBoundForTest()); 356 357 // Now, at this point, it's possible that the scheduled runnable had already been fired 358 // before during the unbind() call, and waiting on mLock. 359 // To simulate it, we just call the runnable here. 360 conn.getBindForBackoffRunnableForTest().run(); 361 362 // Should still not be bound. 363 assertFalse(conn.isBound()); 364 assertFalse(conn.shouldBeBoundForTest()); 365 } 366 testResetBackoff()367 public void testResetBackoff() { 368 final Context context = mock(Context.class); 369 final int userId = 11; 370 final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def"); 371 final Handler handler = new Handler(Looper.getMainLooper()); 372 373 final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn, 374 /* rebindBackoffSeconds= */ 5, 375 /* rebindBackoffIncrease= */ 1.5, 376 /* rebindMaxBackoffSeconds= */ 11, 377 /* resetBackoffDelay= */ 20); 378 379 when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(), 380 any(Handler.class), any(UserHandle.class))) 381 .thenReturn(true); 382 383 // Bind. 384 conn.bind(); 385 386 assertTrue(conn.isBound()); 387 assertTrue(conn.shouldBeBoundForTest()); 388 assertFalse(conn.isRebindScheduled()); 389 390 conn.elapse(1000); 391 392 // Then the binding is "died"... 393 conn.getServiceConnectionForTest().onBindingDied(cn); 394 395 assertFalse(conn.isBound()); 396 assertTrue(conn.shouldBeBoundForTest()); 397 assertFalse(conn.isConnected()); 398 assertNull(conn.getServiceBinder()); 399 assertTrue(conn.isRebindScheduled()); 400 401 assertEquals(7500, conn.getNextBackoffMsForTest()); 402 403 assertEquals( 404 Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(), 405 conn.uptimeMillis + 5000)), 406 conn.scheduledRunnables); 407 408 // 5000 ms later... 409 conn.elapse(5000); 410 411 assertTrue(conn.isBound()); 412 assertTrue(conn.shouldBeBoundForTest()); 413 assertFalse(conn.isConnected()); 414 assertNull(conn.getServiceBinder()); 415 assertFalse(conn.isRebindScheduled()); 416 417 assertEquals(7500, conn.getNextBackoffMsForTest()); 418 419 // Connected. 420 conn.getServiceConnectionForTest().onServiceConnected(cn, 421 new IDeviceAdminService.Stub() {}); 422 423 assertTrue(conn.isBound()); 424 assertTrue(conn.shouldBeBoundForTest()); 425 assertTrue(conn.isConnected()); 426 assertNotNull(conn.getServiceBinder()); 427 assertFalse(conn.isRebindScheduled()); 428 429 assertEquals(7500, conn.getNextBackoffMsForTest()); 430 431 assertEquals( 432 Arrays.asList(Pair.create(conn.getStableCheckRunnableForTest(), 433 conn.uptimeMillis + 20000)), 434 conn.scheduledRunnables); 435 436 conn.elapse(20000); 437 438 assertEquals(5000, conn.getNextBackoffMsForTest()); 439 } 440 } 441