WifiManager: implement start/stopLocalOnlyHotspot
Implement new calls to startLocalOnlyHotspot and
cancelLocalOnlyHotspotRequest along with the classes
LocalOnlyHotspotCallback and LocalOnlyHotspotReservation.
Added tests for starting LOHS and cancelling a LOHS request.
The calls will be exposed in a later CL.
Bug: 36704763
Test: compiles
Test: frameworks/base/wifi/tests/runtests.sh
Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh
Change-Id: If54a89cb8dfd235bc18ef3e6c89f9d30882136a3
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6f1324e..f3e5493 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -40,6 +40,7 @@
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
@@ -862,6 +863,12 @@
/** @hide */
public static final int HOTSPOT_OBSERVER_REGISTERED = 3;
+ private final Object mLock = new Object(); // lock guarding access to the following vars
+ @GuardedBy("mLock")
+ private LocalOnlyHotspotCallbackProxy mLOHSCallbackProxy;
+ @GuardedBy("mLock")
+ private LocalOnlyHotspotObserverProxy mLOHSObserverProxy;
+
/**
* Create a new WifiManager instance.
* Applications will almost always want to use
@@ -1880,7 +1887,24 @@
*/
public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback,
@Nullable Handler handler) {
- throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ synchronized (mLock) {
+ Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
+ LocalOnlyHotspotCallbackProxy proxy =
+ new LocalOnlyHotspotCallbackProxy(this, looper, callback);
+ try {
+ WifiConfiguration config = mService.startLocalOnlyHotspot(
+ proxy.getMessenger(), new Binder());
+ if (config == null) {
+ // Send message to the proxy to make sure we call back on the correct thread
+ proxy.notifyFailed(
+ LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE);
+ return;
+ }
+ mLOHSCallbackProxy = proxy;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
@@ -1897,7 +1921,9 @@
* @hide
*/
public void cancelLocalOnlyHotspotRequest() {
- throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ synchronized (mLock) {
+ stopLocalOnlyHotspot();
+ }
}
/**
@@ -1911,7 +1937,18 @@
* method on their LocalOnlyHotspotReservation.
*/
private void stopLocalOnlyHotspot() {
- throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ synchronized (mLock) {
+ if (mLOHSCallbackProxy == null) {
+ // nothing to do, the callback was already cleaned up.
+ return;
+ }
+ mLOHSCallbackProxy = null;
+ try {
+ mService.stopLocalOnlyHotspot();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
new file mode 100644
index 0000000..75cd095
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -0,0 +1,582 @@
+/*
+ * 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 android.net.wifi;
+
+import static android.net.wifi.WifiManager.HOTSPOT_FAILED;
+import static android.net.wifi.WifiManager.HOTSPOT_STARTED;
+import static android.net.wifi.WifiManager.HOTSPOT_STOPPED;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
+import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
+import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
+import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiManager}.
+ */
+@SmallTest
+public class WifiManagerTest {
+
+ private static final int ERROR_NOT_SET = -1;
+ private static final int ERROR_TEST_REASON = 5;
+
+ @Mock Context mContext;
+ @Mock IWifiManager mWifiService;
+ @Mock ApplicationInfo mApplicationInfo;
+ @Mock WifiConfiguration mApConfig;
+ @Mock IBinder mAppBinder;
+
+ private Handler mHandler;
+ private TestLooper mLooper;
+ private WifiManager mWifiManager;
+ private Messenger mWifiServiceMessenger;
+ final ArgumentCaptor<Messenger> mMessengerCaptor = ArgumentCaptor.forClass(Messenger.class);
+
+ @Before public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mLooper = new TestLooper();
+ mHandler = spy(new Handler(mLooper.getLooper()));
+ when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
+
+ mWifiServiceMessenger = new Messenger(mHandler);
+ mWifiManager = new WifiManager(mContext, mWifiService, mLooper.getLooper());
+ }
+
+ /**
+ * Check the call to startSoftAp calls WifiService to startSoftAp with the provided
+ * WifiConfiguration. Verify that the return value is propagated to the caller.
+ */
+ @Test
+ public void testStartSoftApCallsServiceWithWifiConfig() throws Exception {
+ when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(true);
+ assertTrue(mWifiManager.startSoftAp(mApConfig));
+
+ when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(false);
+ assertFalse(mWifiManager.startSoftAp(mApConfig));
+ }
+
+ /**
+ * Check the call to startSoftAp calls WifiService to startSoftAp with a null config. Verify
+ * that the return value is propagated to the caller.
+ */
+ @Test
+ public void testStartSoftApCallsServiceWithNullConfig() throws Exception {
+ when(mWifiService.startSoftAp(eq(null))).thenReturn(true);
+ assertTrue(mWifiManager.startSoftAp(null));
+
+ when(mWifiService.startSoftAp(eq(null))).thenReturn(false);
+ assertFalse(mWifiManager.startSoftAp(null));
+ }
+
+ /**
+ * Check the call to stopSoftAp calls WifiService to stopSoftAp.
+ */
+ @Test
+ public void testStopSoftApCallsService() throws Exception {
+ when(mWifiService.stopSoftAp()).thenReturn(true);
+ assertTrue(mWifiManager.stopSoftAp());
+
+ when(mWifiService.stopSoftAp()).thenReturn(false);
+ assertFalse(mWifiManager.stopSoftAp());
+ }
+
+ /**
+ * Test creation of a LocalOnlyHotspotReservation and verify that close properly calls
+ * WifiService.stopLocalOnlyHotspot.
+ */
+ @Test
+ public void testCreationAndCloseOfLocalOnlyHotspotReservation() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+
+ callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig));
+
+ assertEquals(mApConfig, callback.mRes.getConfig());
+ callback.mRes.close();
+ verify(mWifiService).stopLocalOnlyHotspot();
+ }
+
+ /**
+ * Verify stopLOHS is called when try-with-resources is used properly.
+ */
+ @Test
+ public void testLocalOnlyHotspotReservationCallsStopProperlyInTryWithResources()
+ throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+
+ callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(mApConfig));
+
+ try (WifiManager.LocalOnlyHotspotReservation res = callback.mRes) {
+ assertEquals(mApConfig, res.getConfig());
+ }
+
+ verify(mWifiService).stopLocalOnlyHotspot();
+ }
+
+ /**
+ * Test creation of a LocalOnlyHotspotSubscription.
+ * TODO: when registrations are tracked, verify removal on close.
+ */
+ @Test
+ public void testCreationOfLocalOnlyHotspotSubscription() throws Exception {
+ try (WifiManager.LocalOnlyHotspotSubscription sub =
+ mWifiManager.new LocalOnlyHotspotSubscription()) {
+ sub.close();
+ }
+ }
+
+ public class TestLocalOnlyHotspotCallback extends LocalOnlyHotspotCallback {
+ public boolean mOnStartedCalled = false;
+ public boolean mOnStoppedCalled = false;
+ public int mFailureReason = -1;
+ public LocalOnlyHotspotReservation mRes = null;
+ public long mCallingThreadId = -1;
+
+ @Override
+ public void onStarted(LocalOnlyHotspotReservation r) {
+ mRes = r;
+ mOnStartedCalled = true;
+ mCallingThreadId = Thread.currentThread().getId();
+ }
+
+ @Override
+ public void onStopped() {
+ mOnStoppedCalled = true;
+ mCallingThreadId = Thread.currentThread().getId();
+ }
+
+ @Override
+ public void onFailed(int reason) {
+ mFailureReason = reason;
+ mCallingThreadId = Thread.currentThread().getId();
+ }
+ }
+
+ /**
+ * Verify callback is properly plumbed when called.
+ */
+ @Test
+ public void testLocalOnlyHotspotCallback() {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ assertFalse(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(ERROR_NOT_SET, callback.mFailureReason);
+ assertEquals(null, callback.mRes);
+
+ // test onStarted
+ WifiManager.LocalOnlyHotspotReservation res =
+ mWifiManager.new LocalOnlyHotspotReservation(mApConfig);
+ callback.onStarted(res);
+ assertEquals(res, callback.mRes);
+ assertTrue(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(ERROR_NOT_SET, callback.mFailureReason);
+
+ // test onStopped
+ callback.onStopped();
+ assertEquals(res, callback.mRes);
+ assertTrue(callback.mOnStartedCalled);
+ assertTrue(callback.mOnStoppedCalled);
+ assertEquals(ERROR_NOT_SET, callback.mFailureReason);
+
+ // test onFailed
+ callback.onFailed(ERROR_TEST_REASON);
+ assertEquals(res, callback.mRes);
+ assertTrue(callback.mOnStartedCalled);
+ assertTrue(callback.mOnStoppedCalled);
+ assertEquals(ERROR_TEST_REASON, callback.mFailureReason);
+ }
+
+ public class TestLocalOnlyHotspotObserver extends LocalOnlyHotspotObserver {
+ public boolean mOnRegistered = false;
+ public boolean mOnStartedCalled = false;
+ public boolean mOnStoppedCalled = false;
+ public WifiConfiguration mConfig = null;
+ public LocalOnlyHotspotSubscription mSub = null;
+
+ @Override
+ public void onRegistered(LocalOnlyHotspotSubscription sub) {
+ mOnRegistered = true;
+ mSub = sub;
+ }
+
+ @Override
+ public void onStarted(WifiConfiguration config) {
+ mOnStartedCalled = true;
+ mConfig = config;
+ }
+
+ @Override
+ public void onStopped() {
+ mOnStoppedCalled = true;
+ }
+ }
+
+ /**
+ * Verify observer is properly plumbed when called.
+ */
+ @Test
+ public void testLocalOnlyHotspotObserver() {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ assertFalse(observer.mOnRegistered);
+ assertFalse(observer.mOnStartedCalled);
+ assertFalse(observer.mOnStoppedCalled);
+ assertEquals(null, observer.mConfig);
+ assertEquals(null, observer.mSub);
+
+ WifiManager.LocalOnlyHotspotSubscription sub =
+ mWifiManager.new LocalOnlyHotspotSubscription();
+ observer.onRegistered(sub);
+ assertTrue(observer.mOnRegistered);
+ assertFalse(observer.mOnStartedCalled);
+ assertFalse(observer.mOnStoppedCalled);
+ assertEquals(null, observer.mConfig);
+ assertEquals(sub, observer.mSub);
+
+ observer.onStarted(mApConfig);
+ assertTrue(observer.mOnRegistered);
+ assertTrue(observer.mOnStartedCalled);
+ assertFalse(observer.mOnStoppedCalled);
+ assertEquals(mApConfig, observer.mConfig);
+ assertEquals(sub, observer.mSub);
+
+ observer.onStopped();
+ assertTrue(observer.mOnRegistered);
+ assertTrue(observer.mOnStartedCalled);
+ assertTrue(observer.mOnStoppedCalled);
+ assertEquals(mApConfig, observer.mConfig);
+ assertEquals(sub, observer.mSub);
+ }
+
+ /**
+ * Verify call to startLocalOnlyHotspot goes to WifiServiceImpl.
+ */
+ @Test
+ public void testStartLocalOnlyHotspot() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+
+ verify(mWifiService).startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ }
+
+ /**
+ * Verify a SecurityException is thrown for callers without proper permissions for
+ * startLocalOnlyHotspot.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartLocalOnlyHotspotThrowsSecurityException() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ doThrow(new SecurityException()).when(mWifiService)
+ .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ }
+
+ /**
+ * Verify an IllegalStateException is thrown for callers that already have a pending request for
+ * startLocalOnlyHotspot.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testStartLocalOnlyHotspotThrowsIllegalStateException() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ doThrow(new IllegalStateException()).when(mWifiService)
+ .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ }
+
+ /**
+ * Verify the watchLocalOnlyHotspot call currently throws an UnsupportedOperationException.
+ */
+ @Test(expected = UnsupportedOperationException.class)
+ public void testWatchLocalOnlyHotspot() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
+ }
+
+ /**
+ * Verify that the handler provided by the caller is used for the callbacks.
+ */
+ @Test
+ public void testCorrectLooperIsUsedForHandler() throws Exception {
+ // record thread from looper.getThread and check ids.
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(null);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ mLooper.dispatchAll();
+ assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
+ assertEquals(mLooper.getLooper().getThread().getId(), callback.mCallingThreadId);
+ }
+
+ /**
+ * Verify that the main looper's thread is used if a handler is not provided by the reqiestomg
+ * application.
+ */
+ @Test
+ public void testMainLooperIsUsedWhenHandlerNotProvided() throws Exception {
+ // record thread from looper.getThread and check ids.
+ TestLooper altLooper = new TestLooper();
+ when(mContext.getMainLooper()).thenReturn(altLooper.getLooper());
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(null);
+ mWifiManager.startLocalOnlyHotspot(callback, null);
+ altLooper.dispatchAll();
+ assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
+ assertEquals(altLooper.getLooper().getThread().getId(), callback.mCallingThreadId);
+ }
+
+ /**
+ * Verify the LOHS onStarted callback is triggered when WifiManager receives a HOTSPOT_STARTED
+ * message from WifiServiceImpl.
+ */
+ @Test
+ public void testOnStartedIsCalledWithReservation() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ TestLooper callbackLooper = new TestLooper();
+ Handler callbackHandler = new Handler(callbackLooper.getLooper());
+ when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class))).thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
+ callbackLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertFalse(callback.mOnStartedCalled);
+ assertEquals(null, callback.mRes);
+ // now trigger the callback
+ Message msg = new Message();
+ msg.what = HOTSPOT_STARTED;
+ msg.obj = mApConfig;
+ mMessengerCaptor.getValue().send(msg);
+ mLooper.dispatchAll();
+ callbackLooper.dispatchAll();
+ assertTrue(callback.mOnStartedCalled);
+ assertEquals(mApConfig, callback.mRes.getConfig());
+ }
+
+ /**
+ * Verify onFailed is called if WifiServiceImpl sends a HOTSPOT_STARTED message with a null
+ * config.
+ */
+ @Test
+ public void testOnStartedIsCalledWithNullConfig() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ TestLooper callbackLooper = new TestLooper();
+ Handler callbackHandler = new Handler(callbackLooper.getLooper());
+ when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class))).thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
+ callbackLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertFalse(callback.mOnStartedCalled);
+ assertEquals(null, callback.mRes);
+ // now trigger the callback
+ Message msg = new Message();
+ msg.what = HOTSPOT_STARTED;
+ mMessengerCaptor.getValue().send(msg);
+ mLooper.dispatchAll();
+ callbackLooper.dispatchAll();
+ assertFalse(callback.mOnStartedCalled);
+ assertEquals(ERROR_GENERIC, callback.mFailureReason);
+ }
+
+ /**
+ * Verify onStopped is called if WifiServiceImpl sends a HOTSPOT_STOPPED message.
+ */
+ @Test
+ public void testOnStoppedIsCalled() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ TestLooper callbackLooper = new TestLooper();
+ Handler callbackHandler = new Handler(callbackLooper.getLooper());
+ when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class))).thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
+ callbackLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertFalse(callback.mOnStoppedCalled);
+ // now trigger the callback
+ Message msg = new Message();
+ msg.what = HOTSPOT_STOPPED;
+ mMessengerCaptor.getValue().send(msg);
+ mLooper.dispatchAll();
+ callbackLooper.dispatchAll();
+ assertTrue(callback.mOnStoppedCalled);
+ }
+
+ /**
+ * Verify onFailed is called if WifiServiceImpl sends a HOTSPOT_FAILED message.
+ */
+ @Test
+ public void testOnFailedIsCalled() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ TestLooper callbackLooper = new TestLooper();
+ Handler callbackHandler = new Handler(callbackLooper.getLooper());
+ when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class))).thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
+ callbackLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertEquals(ERROR_NOT_SET, callback.mFailureReason);
+ // now trigger the callback
+ Message msg = new Message();
+ msg.what = HOTSPOT_FAILED;
+ msg.arg1 = ERROR_NO_CHANNEL;
+ mMessengerCaptor.getValue().send(msg);
+ mLooper.dispatchAll();
+ callbackLooper.dispatchAll();
+ assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason);
+ }
+
+ /**
+ * Verify the handler passed in to startLocalOnlyHotspot is correctly used for callbacks when a
+ * null WifiConfig is returned.
+ */
+ @Test
+ public void testLocalOnlyHotspotCallbackFullOnNullConfig() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(null);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ mLooper.dispatchAll();
+ assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
+ assertFalse(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(null, callback.mRes);
+ }
+
+ /**
+ * Verify a SecurityException resulting from an application without necessary permissions will
+ * bubble up through the call to start LocalOnlyHotspot and will not trigger other callbacks.
+ */
+ @Test(expected = SecurityException.class)
+ public void testLocalOnlyHotspotCallbackFullOnSecurityException() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ doThrow(new SecurityException()).when(mWifiService)
+ .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ try {
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ } catch (SecurityException e) {
+ assertEquals(ERROR_NOT_SET, callback.mFailureReason);
+ assertFalse(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(null, callback.mRes);
+ throw e;
+ }
+
+ }
+
+ /**
+ * Verify the handler passed to startLocalOnlyHotspot is correctly used for callbacks when
+ * SoftApMode fails due to a underlying error.
+ */
+ @Test
+ public void testLocalOnlyHotspotCallbackFullOnNoChannelError() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ mLooper.dispatchAll();
+ //assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason);
+ assertFalse(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(null, callback.mRes);
+ }
+
+ /**
+ * Verify that the call to cancel a LOHS request does call stopLOHS.
+ */
+ @Test
+ public void testCancelLocalOnlyHotspotRequestCallsStopOnWifiService() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ mWifiManager.cancelLocalOnlyHotspotRequest();
+ verify(mWifiService).stopLocalOnlyHotspot();
+ }
+
+ /**
+ * Verify that we do not crash if cancelLocalOnlyHotspotRequest is called without an existing
+ * callback stored.
+ */
+ @Test
+ public void testCancelLocalOnlyHotspotReturnsWithoutExistingRequest() {
+ mWifiManager.cancelLocalOnlyHotspotRequest();
+ }
+
+ /**
+ * Verify that the callback is not triggered if the LOHS request was already cancelled.
+ */
+ @Test
+ public void testCallbackAfterLocalOnlyHotspotWasCancelled() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(mApConfig);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ mWifiManager.cancelLocalOnlyHotspotRequest();
+ verify(mWifiService).stopLocalOnlyHotspot();
+ mLooper.dispatchAll();
+ assertEquals(ERROR_NOT_SET, callback.mFailureReason);
+ assertFalse(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(null, callback.mRes);
+ }
+
+ /**
+ * Verify that calling cancel LOHS request does not crash if an error callback was already
+ * handled.
+ */
+ @Test
+ public void testCancelAfterLocalOnlyHotspotCallbackTriggered() throws Exception {
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
+ when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
+ .thenReturn(null);
+ mWifiManager.startLocalOnlyHotspot(callback, mHandler);
+ mLooper.dispatchAll();
+ assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
+ assertFalse(callback.mOnStartedCalled);
+ assertFalse(callback.mOnStoppedCalled);
+ assertEquals(null, callback.mRes);
+ mWifiManager.cancelLocalOnlyHotspotRequest();
+ verify(mWifiService, never()).stopLocalOnlyHotspot();
+ }
+}