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