1 /* 2 * Copyright (C) 2022 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 17 package com.android.internal.infra; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.annotation.Nullable; 24 import android.app.Service; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.os.IBinder; 28 import android.os.Process; 29 import android.os.UserHandle; 30 31 import androidx.annotation.NonNull; 32 import androidx.test.platform.app.InstrumentationRegistry; 33 import androidx.test.runner.AndroidJUnit4; 34 35 import com.android.frameworks.coretests.aidl.ITestServiceConnectorService; 36 import com.android.internal.infra.ServiceConnectorTest.CapturingServiceLifecycleCallbacks.ServiceLifeCycleEvent; 37 38 import org.junit.Before; 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 import java.util.ArrayList; 43 import java.util.concurrent.CancellationException; 44 import java.util.concurrent.ExecutionException; 45 import java.util.concurrent.TimeUnit; 46 import java.util.concurrent.TimeoutException; 47 48 /** 49 * Unit tests for {@link ServiceConnector} 50 */ 51 @RunWith(AndroidJUnit4.class) 52 public class ServiceConnectorTest { 53 54 private final CapturingServiceLifecycleCallbacks mCapturingServiceLifecycleCallbacks = 55 new CapturingServiceLifecycleCallbacks(); 56 private ServiceConnector<ITestServiceConnectorService> mServiceConnector; 57 58 @Before setup()59 public void setup() { 60 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 61 Intent testServiceConnectorServiceIntent = new Intent(TestService.ACTION_TEST_SERVICE); 62 testServiceConnectorServiceIntent.setPackage(context.getPackageName()); 63 64 ServiceConnector.Impl<ITestServiceConnectorService> serviceConnector = 65 new ServiceConnector.Impl<ITestServiceConnectorService>( 66 context, 67 testServiceConnectorServiceIntent, 68 /* bindingFlags= */ 0, 69 UserHandle.myUserId(), 70 ITestServiceConnectorService.Stub::asInterface); 71 serviceConnector.setServiceLifecycleCallbacks(mCapturingServiceLifecycleCallbacks); 72 mServiceConnector = serviceConnector; 73 } 74 75 @Test connect_invokesLifecycleCallbacks()76 public void connect_invokesLifecycleCallbacks() throws Exception { 77 connectAndWaitForDone(); 78 79 assertThat(mCapturingServiceLifecycleCallbacks.getCapturedLifecycleEvents()) 80 .containsExactly(ServiceLifeCycleEvent.ON_CONNECTED) 81 .inOrder(); 82 } 83 84 @Test connect_alreadyConnected_invokesLifecycleCallbacksOnce()85 public void connect_alreadyConnected_invokesLifecycleCallbacksOnce() throws Exception { 86 connectAndWaitForDone(); 87 connectAndWaitForDone(); 88 89 assertThat(mCapturingServiceLifecycleCallbacks.getCapturedLifecycleEvents()) 90 .containsExactly(ServiceLifeCycleEvent.ON_CONNECTED) 91 .inOrder(); 92 } 93 94 @Test unbind_neverConnected_noLifecycleCallbacks()95 public void unbind_neverConnected_noLifecycleCallbacks() { 96 unbindAndWaitForDone(); 97 98 assertThat(mCapturingServiceLifecycleCallbacks.getCapturedLifecycleEvents()) 99 .isEmpty(); 100 } 101 102 @Test unbind_whileConnected_invokesLifecycleCallbacks()103 public void unbind_whileConnected_invokesLifecycleCallbacks() throws Exception { 104 connectAndWaitForDone(); 105 unbindAndWaitForDone(); 106 107 assertThat(mCapturingServiceLifecycleCallbacks.getCapturedLifecycleEvents()) 108 .containsExactly( 109 ServiceLifeCycleEvent.ON_CONNECTED, 110 ServiceLifeCycleEvent.ON_DISCONNECTED) 111 .inOrder(); 112 } 113 114 115 @Test unbind_alreadyUnbound_invokesLifecycleCallbacks()116 public void unbind_alreadyUnbound_invokesLifecycleCallbacks() throws Exception { 117 connectAndWaitForDone(); 118 unbindAndWaitForDone(); 119 unbindAndWaitForDone(); 120 121 assertThat(mCapturingServiceLifecycleCallbacks.getCapturedLifecycleEvents()) 122 .containsExactly( 123 ServiceLifeCycleEvent.ON_CONNECTED, 124 ServiceLifeCycleEvent.ON_DISCONNECTED) 125 .inOrder(); 126 } 127 128 @Test binds_connectsAndUnbindsMultipleTimes_invokesLifecycleCallbacks()129 public void binds_connectsAndUnbindsMultipleTimes_invokesLifecycleCallbacks() throws Exception { 130 connectAndWaitForDone(); 131 unbindAndWaitForDone(); 132 connectAndWaitForDone(); 133 unbindAndWaitForDone(); 134 connectAndWaitForDone(); 135 136 assertThat(mCapturingServiceLifecycleCallbacks.getCapturedLifecycleEvents()) 137 .containsExactly( 138 ServiceLifeCycleEvent.ON_CONNECTED, 139 ServiceLifeCycleEvent.ON_DISCONNECTED, 140 ServiceLifeCycleEvent.ON_CONNECTED, 141 ServiceLifeCycleEvent.ON_DISCONNECTED, 142 ServiceLifeCycleEvent.ON_CONNECTED) 143 .inOrder(); 144 } 145 146 @Test processCrashes_whileConnected_invokesLifecycleCallbacks()147 public void processCrashes_whileConnected_invokesLifecycleCallbacks() throws Exception { 148 connectAndWaitForDone(); 149 waitForDone(mServiceConnector.post(service -> service.crashProcess())); 150 151 assertThat(mCapturingServiceLifecycleCallbacks.getCapturedLifecycleEvents()) 152 .containsExactly( 153 ServiceLifeCycleEvent.ON_CONNECTED, 154 ServiceLifeCycleEvent.ON_BINDER_DIED) 155 .inOrder(); 156 } 157 connectAndWaitForDone()158 private void connectAndWaitForDone() { 159 waitForDone(mServiceConnector.connect()); 160 } 161 unbindAndWaitForDone()162 private void unbindAndWaitForDone() { 163 mServiceConnector.unbind(); 164 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 165 } 166 waitForDone(AndroidFuture<?> androidFuture)167 private static void waitForDone(AndroidFuture<?> androidFuture) { 168 if (androidFuture.isDone()) { 169 return; 170 } 171 172 try { 173 androidFuture.get(10, TimeUnit.SECONDS); 174 } catch (InterruptedException | TimeoutException ex) { 175 throw new RuntimeException(ex); 176 } catch (ExecutionException | CancellationException ex) { 177 // Failed and canceled futures are completed 178 return; 179 } 180 } 181 182 public static final class CapturingServiceLifecycleCallbacks implements 183 ServiceConnector.ServiceLifecycleCallbacks<ITestServiceConnectorService> { 184 public enum ServiceLifeCycleEvent { 185 ON_CONNECTED, 186 ON_DISCONNECTED, 187 ON_BINDER_DIED, 188 } 189 190 private final ArrayList<ServiceLifeCycleEvent> mCapturedLifecycleEventServices = 191 new ArrayList<>(); 192 getCapturedLifecycleEvents()193 public ArrayList<ServiceLifeCycleEvent> getCapturedLifecycleEvents() { 194 return mCapturedLifecycleEventServices; 195 } 196 197 @Override onConnected(@onNull ITestServiceConnectorService service)198 public void onConnected(@NonNull ITestServiceConnectorService service) { 199 requireNonNull(service); 200 mCapturedLifecycleEventServices.add(ServiceLifeCycleEvent.ON_CONNECTED); 201 } 202 203 @Override onDisconnected(@onNull ITestServiceConnectorService service)204 public void onDisconnected(@NonNull ITestServiceConnectorService service) { 205 requireNonNull(service); 206 mCapturedLifecycleEventServices.add(ServiceLifeCycleEvent.ON_DISCONNECTED); 207 } 208 209 @Override onBinderDied()210 public void onBinderDied() { 211 mCapturedLifecycleEventServices.add(ServiceLifeCycleEvent.ON_BINDER_DIED); 212 } 213 } 214 215 public static final class TestService extends Service { 216 217 public static String ACTION_TEST_SERVICE = "android.intent.action.BIND_TEST_SERVICE"; 218 219 @Nullable 220 @Override onBind(@ullable Intent intent)221 public IBinder onBind(@Nullable Intent intent) { 222 if (intent == null) { 223 return null; 224 } 225 226 if (!intent.getAction().equals(ACTION_TEST_SERVICE)) { 227 return null; 228 } 229 230 return new TestServiceConnectorService().asBinder(); 231 } 232 } 233 234 private static final class TestServiceConnectorService extends 235 ITestServiceConnectorService.Stub { 236 @Override crashProcess()237 public void crashProcess() { 238 Process.killProcess(Process.myPid()); 239 } 240 } 241 } 242 243