summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEtan Cohen <etancohen@google.com>2017-01-19 19:55:25 -0800
committerEtan Cohen <etancohen@google.com>2017-01-25 07:32:43 -0800
commitdd4dcab629d1045b08f58f699a4a09ecc8cd23e3 (patch)
tree509281f562e867bc9c129442c9b5680a0ad102bc
parent6dada2c3bc67349ae08befd0f1fa76cf7e6ea028 (diff)
Wi-Fi HAL device manager: baseline for init/start/stop
Wi-Fi HAL device manager. All Wi-Fi services should use this manager to start/stop and monitor status of Wi-Fi. Baseline: will be extended to coordinate interface. Bug: 34474043 Test: unit tests Change-Id: I3846cb57f301bcd91534f1b5943d996f4c84ed63
-rw-r--r--service/Android.mk4
-rw-r--r--service/java/com/android/server/wifi/HalDeviceManager.java428
-rw-r--r--tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java270
3 files changed, 702 insertions, 0 deletions
diff --git a/service/Android.mk b/service/Android.mk
index 3b9eb43bd..33705e430 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -60,6 +60,10 @@ LOCAL_SRC_FILES := $(call all-java-files-under, java) \
$(call all-proto-files-under, proto)
LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt jsr305 services
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android.hardware.wifi@1.0-java-static \
+ android.hidl.base@1.0-java-static \
+ android.hidl.manager@1.0-java-static
LOCAL_REQUIRED_MODULES := services
LOCAL_MODULE_TAGS :=
LOCAL_MODULE := wifi-service
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
new file mode 100644
index 000000000..da8a993cf
--- /dev/null
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.hardware.wifi.V1_0.IWifi;
+import android.hardware.wifi.V1_0.IWifiEventCallback;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Handles device management through the HAL (HIDL) interface.
+ */
+public class HalDeviceManager {
+ private static final String TAG = "HalDeviceManager";
+ private static final boolean DBG = true;
+
+ // public API
+ public HalDeviceManager() {
+ // empty
+ }
+
+ /**
+ * Actually starts the HalDeviceManager: separate from constructor since may want to phase
+ * at a later time.
+ *
+ * TODO: if decide that no need for separating construction from initialization (e.g. both are
+ * done at injector) then move to constructor.
+ */
+ public void initialize() {
+ initializeInternal();
+ }
+
+ /**
+ * Register a ManagerStatusCallback to get information about status of Wi-Fi. Use the
+ * isStarted() method to check status immediately after registration - don't expect callbacks
+ * for current status (no 'sticky' behavior).
+ *
+ * It is safe to re-register the same callback object - duplicates are detected and only a
+ * single copy kept.
+ *
+ * @param callback ManagerStatusCallback callback object.
+ * @param looper Looper on which to dispatch callbacks. Null implies current looper.
+ */
+ public void registerStatusCallback(ManagerStatusCallback callback, Looper looper) {
+ synchronized (mLock) {
+ if (!mManagerStatusCallbacks.add(new ManagerStatusCallbackProxy(callback,
+ looper == null ? Looper.myLooper() : looper))) {
+ Log.w(TAG, "registerStatusCallback: duplicate registration ignored");
+ }
+ }
+ }
+
+ /**
+ * Returns the current status of Wi-Fi: started (true) or stopped (false).
+ *
+ * Note: direct call to HIDL.
+ */
+ public boolean isStarted() {
+ return isWifiStarted();
+ }
+
+ /**
+ * Attempts to start Wi-Fi (using HIDL). Returns the success (true) or failure (false) or
+ * the start operation. Will also dispatch any registered ManagerStatusCallback.onStart() on
+ * success.
+ *
+ * Note: direct call to HIDL.
+ */
+ public boolean start() {
+ return startWifi();
+ }
+
+ /**
+ * Stops Wi-Fi. Will also dispatch any registeredManagerStatusCallback.onStop().
+ *
+ * Note: direct call to HIDL - failure is not-expected.
+ */
+ public void stop() {
+ stopWifi();
+ }
+
+ /**
+ * HAL device manager status callbacks.
+ */
+ public interface ManagerStatusCallback {
+ /**
+ * Indicates that Wi-Fi is up.
+ */
+ void onStart();
+
+ /**
+ * Indicates that Wi-Fi is down.
+ */
+ void onStop();
+ }
+
+ // internal state
+ private final Object mLock = new Object();
+
+ private IServiceManager mServiceManager;
+ private IWifi mWifi;
+ private final WifiEventCallback mWifiEventCallback = new WifiEventCallback();
+ private final Set<ManagerStatusCallbackProxy> mManagerStatusCallbacks = new HashSet<>();
+
+ /**
+ * Wrapper function to access the HIDL services. Created to be mockable in unit-tests.
+ */
+ protected IWifi getWifiServiceMockable() {
+ try {
+ return IWifi.getService();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception getting IWifi service: " + e);
+ return null;
+ }
+ }
+
+ protected IServiceManager getServiceManagerMockable() {
+ try {
+ return IServiceManager.getService();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception getting IServiceManager: " + e);
+ return null;
+ }
+ }
+
+ // internal implementation
+ private void initializeInternal() {
+ initIServiceManagerIfNecessary();
+ }
+
+ /**
+ * Failures of IServiceManager are most likely system breaking in any case. Behavior here
+ * will be to WTF and continue.
+ */
+ private void initIServiceManagerIfNecessary() {
+ if (DBG) Log.d(TAG, "initIServiceManagerIfNecessary");
+
+ synchronized (mLock) {
+ if (mServiceManager != null) {
+ return;
+ }
+
+ mServiceManager = getServiceManagerMockable();
+ if (mServiceManager == null) {
+ Log.wtf(TAG, "Failed to get IServiceManager instance");
+ } else {
+ try {
+ if (!mServiceManager.linkToDeath(cookie -> {
+ Log.wtf(TAG, "IServiceManager died: cookie=" + cookie);
+ synchronized (mLock) {
+ mServiceManager = null;
+ // theoretically can call initServiceManager again here - but
+ // there's no point since most likely system is going to reboot
+ }
+ }, /* don't care */ 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
+ mServiceManager = null;
+ return;
+ }
+
+ if (!mServiceManager.registerForNotifications(IWifi.kInterfaceName, "",
+ new IServiceNotification.Stub() {
+ @Override
+ public void onRegistration(String fqName, String name,
+ boolean preexisting) {
+ Log.d(TAG, "IWifi registration notification: fqName=" + fqName
+ + ", name=" + name + ", preexisting=" + preexisting);
+ mWifi = null; // get rid of old copy!
+ initIWifiIfNecessary();
+ }
+ })) {
+ Log.wtf(TAG, "Failed to register a listener for IWifi service");
+ mServiceManager = null;
+ }
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
+ mServiceManager = null;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Initialize IWifi and register death listener and event callback.
+ *
+ * - It is possible that IWifi is not ready - we have a listener on IServiceManager for it.
+ * - It is not expected that any of the registrations will fail. Possible indication that
+ * service died after we obtained a handle to it.
+ *
+ * Here and elsewhere we assume that death listener will do the right thing!
+ */
+ private void initIWifiIfNecessary() {
+ if (DBG) Log.d(TAG, "initIWifiIfNecessary");
+
+ synchronized (mLock) {
+ if (mWifi != null) {
+ return;
+ }
+
+ try {
+ mWifi = getWifiServiceMockable();
+ if (mWifi == null) {
+ Log.e(TAG, "IWifi not (yet) available - but have a listener for it ...");
+ return;
+ }
+
+ if (!mWifi.linkToDeath(cookie -> {
+ Log.e(TAG, "IWifi HAL service died! Have a listener for it ... cookie="
+ + cookie);
+ synchronized (mLock) { // prevents race condition with surrounding method
+ mWifi = null;
+ managerStatusCallbackDispatchStop();
+ // don't restart: wait for registration notification
+ }
+ }, /* don't care */ 0)) {
+ Log.e(TAG, "Error on linkToDeath on IWifi - will retry later");
+ return;
+ }
+
+ WifiStatus status = mWifi.registerEventCallback(mWifiEventCallback);
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "IWifi.registerEventCallback failed: " + statusString(status));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while operating on IWifi: " + e);
+ }
+ }
+ }
+
+ private boolean isWifiStarted() {
+ if (DBG) Log.d(TAG, "isWifiStart");
+
+ synchronized (mLock) {
+ try {
+ if (mWifi == null) {
+ Log.w(TAG, "isWifiStarted called but mWifi is null!?");
+ return false;
+ } else {
+ return mWifi.isStarted();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "isWifiStarted exception: " + e);
+ return false;
+ }
+ }
+ }
+
+ private boolean startWifi() {
+ if (DBG) Log.d(TAG, "startWifi");
+
+ synchronized (mLock) {
+ try {
+ if (mWifi == null) {
+ Log.w(TAG, "startWifi called but mWifi is null!?");
+ return false;
+ } else {
+ WifiStatus status = mWifi.start();
+ boolean success = status.code == WifiStatusCode.SUCCESS;
+ if (success) {
+ managerStatusCallbackDispatchStart();
+ } else {
+ Log.e(TAG, "Cannot start IWifi: " + statusString(status));
+ }
+ return success;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "startWifi exception: " + e);
+ return false;
+ }
+ }
+ }
+
+ private void stopWifi() {
+ if (DBG) Log.d(TAG, "stopWifi");
+
+ synchronized (mLock) {
+ try {
+ if (mWifi == null) {
+ Log.w(TAG, "stopWifi called but mWifi is null!?");
+ } else {
+ WifiStatus status = mWifi.stop();
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "Cannot stop IWifi: " + statusString(status));
+ }
+
+ // calling onStop for the callbacks even on failure since WTF??
+ managerStatusCallbackDispatchStop();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "stopWifi exception: " + e);
+ }
+ }
+ }
+
+ private class WifiEventCallback extends IWifiEventCallback.Stub {
+ @Override
+ public void onStart() throws RemoteException {
+ if (DBG) Log.d(TAG, "IWifiEventCallback.onStart");
+ // NOP: only happens in reaction to my calls - will handle directly
+ }
+
+ @Override
+ public void onStop() throws RemoteException {
+ if (DBG) Log.d(TAG, "IWifiEventCallback.onStop");
+ // NOP: only happens in reaction to my calls - will handle directly
+ }
+
+ @Override
+ public void onFailure(WifiStatus status) throws RemoteException {
+ Log.e(TAG, "IWifiEventCallback.onFailure: " + statusString(status));
+ managerStatusCallbackDispatchStop();
+
+ // No need to do anything else: listeners may (will) re-start Wi-Fi
+ }
+ }
+
+ private void managerStatusCallbackDispatchStart() {
+ synchronized (mLock) {
+ for (ManagerStatusCallbackProxy cb : mManagerStatusCallbacks) {
+ cb.onStart();
+ }
+ }
+ }
+
+ private void managerStatusCallbackDispatchStop() {
+ synchronized (mLock) {
+ for (ManagerStatusCallbackProxy cb : mManagerStatusCallbacks) {
+ cb.onStop();
+ }
+ }
+ }
+
+ private class ManagerStatusCallbackProxy {
+ private static final int CALLBACK_ON_START = 0;
+ private static final int CALLBACK_ON_STOP = 1;
+
+ private ManagerStatusCallback mCallback;
+ private Handler mHandler;
+
+ void onStart() {
+ mHandler.sendMessage(mHandler.obtainMessage(CALLBACK_ON_START));
+ }
+
+ void onStop() {
+ mHandler.sendMessage(mHandler.obtainMessage(CALLBACK_ON_STOP));
+ }
+
+ // override equals & hash to make sure that the container HashSet is unique with respect to
+ // the contained callback
+ @Override
+ public boolean equals(Object obj) {
+ return mCallback == ((ManagerStatusCallbackProxy) obj).mCallback;
+ }
+
+ @Override
+ public int hashCode() {
+ return mCallback.hashCode();
+ }
+
+ ManagerStatusCallbackProxy(ManagerStatusCallback callback, Looper looper) {
+ mCallback = callback;
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) {
+ Log.d(TAG, "ManagerStatusCallbackProxy.handleMessage: what=" + msg.what);
+ }
+ switch (msg.what) {
+ case CALLBACK_ON_START:
+ mCallback.onStart();
+ break;
+ case CALLBACK_ON_STOP:
+ mCallback.onStop();
+ break;
+ default:
+ Log.e(TAG,
+ "ManagerStatusCallbackProxy.handleMessage: unknown message "
+ + "what="
+ + msg.what);
+ }
+ }
+ };
+ }
+ }
+
+ private String statusString(WifiStatus status) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(status.code).append(" (").append(status.description).append(")");
+ return sb.toString();
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("HalDeviceManager:");
+ pw.println(" mServiceManager: " + mServiceManager);
+ pw.println(" mWifi: " + mWifi);
+ pw.println(" mManagerStatusCallbacks: " + mManagerStatusCallbacks);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
new file mode 100644
index 000000000..f762ced13
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.hardware.wifi.V1_0.IWifi;
+import android.hardware.wifi.V1_0.IWifiEventCallback;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IHwBinder;
+import android.os.test.TestLooper;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Unit test harness for HalDeviceManagerTest.
+ */
+public class HalDeviceManagerTest {
+ private HalDeviceManager mDut;
+ @Mock IServiceManager mServiceManagerMock;
+ @Mock IWifi mWifiMock;
+ @Mock HalDeviceManager.ManagerStatusCallback mManagerStatusCallbackMock;
+ TestLooper mTestLooper;
+ private ArgumentCaptor<IHwBinder.DeathRecipient> mDeathRecipientCaptor =
+ ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
+ private ArgumentCaptor<IServiceNotification.Stub> mServiceNotificationCaptor =
+ ArgumentCaptor.forClass(IServiceNotification.Stub.class);
+ private ArgumentCaptor<IWifiEventCallback> mWifiEventCallbackCaptor = ArgumentCaptor.forClass(
+ IWifiEventCallback.class);
+ private InOrder mInOrder;
+ @Rule public ErrorCollector collector = new ErrorCollector();
+ private WifiStatus mStatusOk;
+ private WifiStatus mStatusFail;
+
+ private class HalDeviceManagerSpy extends HalDeviceManager {
+ @Override
+ protected IWifi getWifiServiceMockable() {
+ return mWifiMock;
+ }
+
+ @Override
+ protected IServiceManager getServiceManagerMockable() {
+ return mServiceManagerMock;
+ }
+ }
+
+ @Before
+ public void before() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestLooper = new TestLooper();
+
+ // initialize dummy status objects
+ mStatusOk = new WifiStatus();
+ mStatusOk.code = WifiStatusCode.SUCCESS;
+ mStatusFail = new WifiStatus();
+ mStatusFail.code = WifiStatusCode.ERROR_UNKNOWN;
+
+ when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
+ any(IServiceNotification.Stub.class))).thenReturn(true);
+ when(mWifiMock.linkToDeath(any(IHwBinder.DeathRecipient.class), anyLong())).thenReturn(
+ true);
+ when(mWifiMock.registerEventCallback(any(IWifiEventCallback.class))).thenReturn(mStatusOk);
+ when(mWifiMock.start()).thenReturn(mStatusOk);
+ when(mWifiMock.stop()).thenReturn(mStatusOk);
+
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, mManagerStatusCallbackMock);
+
+ mDut = new HalDeviceManagerSpy();
+ executeAndValidateInitializationSequence();
+ }
+
+ /**
+ * Print out the dump of the device manager after each test. Not used in test validation
+ * (internal state) - but can help in debugging failed tests.
+ */
+ @After
+ public void after() throws Exception {
+ dumpDut("after: ");
+ }
+
+ /**
+ * Test basic startup flow:
+ * - IServiceManager registrations
+ * - IWifi registrations
+ * - IWifi startup delayed
+ * - Start Wi-Fi -> onStart
+ * - Stop Wi-Fi -> onStop
+ */
+ @Test
+ public void testStartStopFlow() throws Exception {
+ executeAndValidateStartupSequence();
+
+ // act: stop Wi-Fi
+ mDut.stop();
+ mTestLooper.dispatchAll();
+
+ // verify: onStop called
+ mInOrder.verify(mWifiMock).stop();
+ mInOrder.verify(mManagerStatusCallbackMock).onStop();
+
+ verifyNoMoreInteractions(mServiceManagerMock, mWifiMock, mManagerStatusCallbackMock);
+ }
+
+ /**
+ * Validate that multiple callback registrations are called and that duplicate ones are
+ * only called once.
+ */
+ @Test
+ public void testMultipleCallbackRegistrations() throws Exception {
+ // register another 2 callbacks - one of them twice
+ HalDeviceManager.ManagerStatusCallback callback1 = mock(
+ HalDeviceManager.ManagerStatusCallback.class);
+ HalDeviceManager.ManagerStatusCallback callback2 = mock(
+ HalDeviceManager.ManagerStatusCallback.class);
+ mDut.registerStatusCallback(callback2, mTestLooper.getLooper());
+ mDut.registerStatusCallback(callback1, mTestLooper.getLooper());
+ mDut.registerStatusCallback(callback2, mTestLooper.getLooper());
+
+ // startup
+ executeAndValidateStartupSequence();
+
+ // verify
+ verify(callback1).onStart();
+ verify(callback2).onStart();
+
+ verifyNoMoreInteractions(mServiceManagerMock, mWifiMock, mManagerStatusCallbackMock,
+ callback1, callback2);
+ }
+
+ /**
+ * Validate IWifi death listener and registration flow.
+ */
+ @Test
+ public void testWifiDeathAndRegistration() throws Exception {
+ executeAndValidateStartupSequence();
+
+ // act: IWifi service death
+ mDeathRecipientCaptor.getValue().serviceDied(0);
+ mTestLooper.dispatchAll();
+
+ // verify: getting onStop
+ mInOrder.verify(mManagerStatusCallbackMock).onStop();
+
+ // act: service startup
+ mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", false);
+
+ // verify: initialization of IWifi
+ mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
+ mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
+
+ // act: start
+ mDut.start();
+ mWifiEventCallbackCaptor.getValue().onStart();
+ mTestLooper.dispatchAll();
+
+ // verify: service and callback calls
+ mInOrder.verify(mWifiMock).start();
+ mInOrder.verify(mManagerStatusCallbackMock).onStart();
+
+ verifyNoMoreInteractions(mServiceManagerMock, mWifiMock, mManagerStatusCallbackMock);
+ }
+
+ /**
+ * Validate IWifi onFailure causes notification
+ */
+ @Test
+ public void testWifiFail() throws Exception {
+ executeAndValidateStartupSequence();
+
+ // act: IWifi failure
+ mWifiEventCallbackCaptor.getValue().onFailure(mStatusFail);
+ mTestLooper.dispatchAll();
+
+ // verify: getting onStop
+ mInOrder.verify(mManagerStatusCallbackMock).onStop();
+
+ // act: start again
+ mDut.start();
+ mWifiEventCallbackCaptor.getValue().onStart();
+ mTestLooper.dispatchAll();
+
+ // verify: service and callback calls
+ mInOrder.verify(mWifiMock).start();
+ mInOrder.verify(mManagerStatusCallbackMock).onStart();
+
+ verifyNoMoreInteractions(mServiceManagerMock, mWifiMock, mManagerStatusCallbackMock);
+ }
+
+
+ // utilities
+ private void dumpDut(String prefix) {
+ StringWriter sw = new StringWriter();
+ mDut.dump(null, new PrintWriter(sw), null);
+ Log.e("HalDeviceManager", prefix + sw.toString());
+ }
+
+ private void executeAndValidateInitializationSequence() throws Exception {
+ // act:
+ mDut.initialize();
+
+ // verify: service manager initialization sequence
+ mInOrder.verify(mServiceManagerMock).linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong());
+ mInOrder.verify(mServiceManagerMock).registerForNotifications(eq(IWifi.kInterfaceName),
+ eq(""), mServiceNotificationCaptor.capture());
+
+ // act: get the service started (which happens even when service was already up)
+ mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", true);
+
+ // verify: wifi initialization sequence
+ mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
+ mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
+ }
+
+ private void executeAndValidateStartupSequence() throws Exception {
+ // act: register listener & start Wi-Fi
+ mDut.registerStatusCallback(mManagerStatusCallbackMock, mTestLooper.getLooper());
+ mDut.start();
+
+ // verify
+ mInOrder.verify(mWifiMock).start();
+
+ // act: trigger onStart callback of IWifiEventCallback
+ mWifiEventCallbackCaptor.getValue().onStart();
+ mTestLooper.dispatchAll();
+
+ // verify: onStart called on registered listener
+ mInOrder.verify(mManagerStatusCallbackMock).onStart();
+ }
+}