Home | History | Annotate | Download | only in am
      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