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 */
16package com.android.server.am;
17
18import static org.mockito.ArgumentMatchers.any;
19import static org.mockito.ArgumentMatchers.anyInt;
20import static org.mockito.ArgumentMatchers.eq;
21import static org.mockito.Mockito.mock;
22import static org.mockito.Mockito.verify;
23import static org.mockito.Mockito.when;
24
25import android.app.admin.IDeviceAdminService;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.ServiceConnection;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.Looper;
33import android.os.UserHandle;
34import android.test.AndroidTestCase;
35import android.test.suitebuilder.annotation.SmallTest;
36import android.util.Pair;
37
38import org.mockito.ArgumentMatchers;
39
40import java.util.ArrayList;
41import java.util.Arrays;
42import java.util.Collections;
43
44@SmallTest
45public 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