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