diff options
author | 2024-11-28 00:34:45 +0000 | |
---|---|---|
committer | 2024-12-05 17:41:38 +0000 | |
commit | 68d6c7f560a8bf107c5985abc38f8d84f0dfac30 (patch) | |
tree | 81158318d8092188245f6f2a38fcfa490c1fd3c0 | |
parent | ad74642fd980415a75555b74c4bd9e5c4f3a10e5 (diff) |
Register death handler for the mainline
supplicant service.
Death handling logic is the same as in
SupplicantStaIfaceHalAidlImpl.
Bug: 365585450
Flag: android.net.wifi.flags.usd
Test: atest MainlineSupplicantTest
Test: Manual tests
1. Kill process from the command line
and check for the death notification.
2. Stop the process via the stopService
method, and check that the count down
latch is handled correctly.
Change-Id: Ib21195f73a180b681481fa1498a868560c4e2490
2 files changed, 113 insertions, 10 deletions
diff --git a/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java b/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java index 038da8b90a..e347122942 100644 --- a/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java +++ b/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java @@ -16,7 +16,10 @@ package com.android.server.wifi.mainline_supplicant; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.wifi.util.Environment; +import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.system.wifi.mainline_supplicant.IMainlineSupplicant; @@ -24,6 +27,9 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Allows us to bring up, tear down, and make calls into the mainline supplicant process. * <p> @@ -33,11 +39,16 @@ import com.android.internal.annotations.VisibleForTesting; public class MainlineSupplicant { private static final String TAG = "MainlineSupplicant"; private static final String MAINLINE_SUPPLICANT_SERVICE_NAME = "wifi_mainline_supplicant"; + private static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L; private IMainlineSupplicant mIMainlineSupplicant; private final Object mLock = new Object(); + private SupplicantDeathRecipient mDeathRecipient; + private CountDownLatch mWaitForDeathLatch; - public MainlineSupplicant() {} + public MainlineSupplicant() { + mDeathRecipient = new SupplicantDeathRecipient(); + } @VisibleForTesting protected IMainlineSupplicant getNewServiceBinderMockable() { @@ -45,6 +56,40 @@ public class MainlineSupplicant { ServiceManagerWrapper.waitForService(MAINLINE_SUPPLICANT_SERVICE_NAME)); } + private @Nullable IBinder getCurrentServiceBinder() { + synchronized (mLock) { + if (mIMainlineSupplicant == null) { + return null; + } + return mIMainlineSupplicant.asBinder(); + } + } + + private class SupplicantDeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + } + + @Override + public void binderDied(@NonNull IBinder who) { + synchronized (mLock) { + IBinder currentBinder = getCurrentServiceBinder(); + Log.i(TAG, "Death notification received. who=" + who + + ", currentBinder=" + currentBinder); + if (currentBinder == null || currentBinder != who) { + Log.i(TAG, "Ignoring stale death notification"); + return; + } + if (mWaitForDeathLatch != null) { + // Latch indicates that this event was triggered by stopService + mWaitForDeathLatch.countDown(); + } + mIMainlineSupplicant = null; + Log.i(TAG, "Service death was handled successfully"); + } + } + } + /** * Start the mainline supplicant process. * @@ -60,11 +105,21 @@ public class MainlineSupplicant { Log.i(TAG, "Service has already been started"); return true; } + mIMainlineSupplicant = getNewServiceBinderMockable(); if (mIMainlineSupplicant == null) { Log.e(TAG, "Unable to retrieve binder from the ServiceManager"); return false; } + + try { + mWaitForDeathLatch = null; + mIMainlineSupplicant.asBinder().linkToDeath(mDeathRecipient, /* flags= */ 0); + } catch (RemoteException e) { + handleRemoteException(e, "startService"); + return false; + } + Log.i(TAG, "Service was started successfully"); return true; } @@ -89,16 +144,25 @@ public class MainlineSupplicant { Log.i(TAG, "Service has already been stopped"); return; } - final String methodName = "stopService"; try { + Log.i(TAG, "Attempting to stop the service"); + mWaitForDeathLatch = new CountDownLatch(1); mIMainlineSupplicant.terminate(); - Log.i(TAG, "Service was terminated successfully"); - } catch (ServiceSpecificException e) { - handleServiceSpecificException(e, methodName); } catch (RemoteException e) { - handleRemoteException(e, methodName); + handleRemoteException(e, "stopService"); + return; } - mIMainlineSupplicant = null; + } + + // Wait for latch to confirm the service death + try { + if (mWaitForDeathLatch.await(WAIT_FOR_DEATH_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + Log.i(TAG, "Service death confirmation was received"); + } else { + Log.e(TAG, "Timed out waiting for confirmation of service death"); + } + } catch (InterruptedException e) { + Log.e(TAG, "Failed to wait for service death"); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java b/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java index 7040763b52..e27c46f5dd 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java @@ -19,12 +19,18 @@ package com.android.server.wifi.mainline_supplicant; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.net.wifi.util.Environment; +import android.os.IBinder; import android.system.wifi.mainline_supplicant.IMainlineSupplicant; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -33,8 +39,12 @@ import org.mockito.MockitoAnnotations; */ public class MainlineSupplicantTest { private @Mock IMainlineSupplicant mIMainlineSupplicantMock; + private @Mock IBinder mIBinderMock; private MainlineSupplicantSpy mDut; + private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + // Spy version of this class allows us to override methods for testing. private class MainlineSupplicantSpy extends MainlineSupplicant { MainlineSupplicantSpy() { @@ -51,17 +61,46 @@ public class MainlineSupplicantTest { public void setUp() throws Exception { assumeTrue(Environment.isSdkAtLeastB()); MockitoAnnotations.initMocks(this); + when(mIMainlineSupplicantMock.asBinder()).thenReturn(mIBinderMock); mDut = new MainlineSupplicantSpy(); } + private void validateServiceStart() throws Exception { + assertTrue(mDut.startService()); + verify(mIBinderMock).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + assertTrue(mDut.isActive()); + } + + private void validateServiceStop() { + mDut.stopService(); + mDeathRecipientCaptor.getValue().binderDied(mIBinderMock); + assertFalse(mDut.isActive()); + } + /** * Verify that the class can be started and stopped successfully. */ @Test - public void testStartAndStopSuccess() { - assertTrue(mDut.startService()); + public void testStartAndStopSuccess() throws Exception { + validateServiceStart(); + validateServiceStop(); + } + + /** + * Verify that unsolicited death notifications (ex. caused by a service crash) + * are handled correctly. + */ + @Test + public void testUnsolicitedDeathNotification() throws Exception { + validateServiceStart(); + + // Notification with an unknown binder should be ignored + IBinder otherBinder = mock(IBinder.class); + mDeathRecipientCaptor.getValue().binderDied(otherBinder); assertTrue(mDut.isActive()); - mDut.stopService(); + + // Notification with the correct binder should be handled + mDeathRecipientCaptor.getValue().binderDied(mIBinderMock); assertFalse(mDut.isActive()); } } |