summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Gabriel Biren <gbiren@google.com> 2024-11-28 00:34:45 +0000
committer Gabriel Biren <gbiren@google.com> 2024-12-05 17:41:38 +0000
commit68d6c7f560a8bf107c5985abc38f8d84f0dfac30 (patch)
tree81158318d8092188245f6f2a38fcfa490c1fd3c0
parentad74642fd980415a75555b74c4bd9e5c4f3a10e5 (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
-rw-r--r--service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java78
-rw-r--r--service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java45
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());
}
}