summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Rebecca Silberstein <silberst@google.com> 2017-04-30 18:45:31 +0000
committer android-build-merger <android-build-merger@google.com> 2017-04-30 18:45:31 +0000
commit317c8634f5db2f0c94163a8063b87af206f5593a (patch)
tree97c161c91ee98f9431b123cd5d59a5071f834ccd
parent8a40a4ca2767ca2ffdeef8bf44a12e17933dd17f (diff)
parent63ca3e2414dc058548d32db60d60bf98addb6306 (diff)
Merge changes I8d149ab0,Ia0a52819,If54a89cb,I20faa733,Ib32dfd23 into oc-dev am: 07ea44909f
am: 63ca3e2414 Change-Id: Ieb34996878d9689e2bd29590a302a95dd7d2d83c
-rw-r--r--api/current.txt16
-rw-r--r--api/system-current.txt16
-rw-r--r--api/test-current.txt16
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java239
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java761
5 files changed, 1035 insertions, 13 deletions
diff --git a/api/current.txt b/api/current.txt
index c0e0a770287c..71e5c91cbdb0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26569,6 +26569,7 @@ package android.net.wifi {
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiEnabled(boolean);
+ method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
method public boolean startScan();
method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method public int updateNetwork(android.net.wifi.WifiConfiguration);
@@ -26619,6 +26620,21 @@ package android.net.wifi {
field public static final int WPS_WEP_PROHIBITED = 4; // 0x4
}
+ public static class WifiManager.LocalOnlyHotspotCallback {
+ ctor public WifiManager.LocalOnlyHotspotCallback();
+ method public void onFailed(int);
+ method public void onStarted(android.net.wifi.WifiManager.LocalOnlyHotspotReservation);
+ method public void onStopped();
+ field public static final int ERROR_GENERIC = 2; // 0x2
+ field public static final int ERROR_INCOMPATIBLE_MODE = 3; // 0x3
+ field public static final int ERROR_NO_CHANNEL = 1; // 0x1
+ }
+
+ public class WifiManager.LocalOnlyHotspotReservation implements java.lang.AutoCloseable {
+ method public void close();
+ method public android.net.wifi.WifiConfiguration getConfig();
+ }
+
public class WifiManager.MulticastLock {
method public void acquire();
method public boolean isHeld();
diff --git a/api/system-current.txt b/api/system-current.txt
index 5faac780d0d0..5081583b4969 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29151,6 +29151,7 @@ package android.net.wifi {
method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method public boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
method public boolean setWifiEnabled(boolean);
+ method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource);
method public boolean startScan();
method public boolean startScan(android.os.WorkSource);
@@ -29228,6 +29229,21 @@ package android.net.wifi {
method public abstract void onSuccess();
}
+ public static class WifiManager.LocalOnlyHotspotCallback {
+ ctor public WifiManager.LocalOnlyHotspotCallback();
+ method public void onFailed(int);
+ method public void onStarted(android.net.wifi.WifiManager.LocalOnlyHotspotReservation);
+ method public void onStopped();
+ field public static final int ERROR_GENERIC = 2; // 0x2
+ field public static final int ERROR_INCOMPATIBLE_MODE = 3; // 0x3
+ field public static final int ERROR_NO_CHANNEL = 1; // 0x1
+ }
+
+ public class WifiManager.LocalOnlyHotspotReservation implements java.lang.AutoCloseable {
+ method public void close();
+ method public android.net.wifi.WifiConfiguration getConfig();
+ }
+
public class WifiManager.MulticastLock {
method public void acquire();
method public boolean isHeld();
diff --git a/api/test-current.txt b/api/test-current.txt
index bfd722aaf092..35683bf185e2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -26677,6 +26677,7 @@ package android.net.wifi {
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
method public boolean setWifiEnabled(boolean);
+ method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
method public boolean startScan();
method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
method public int updateNetwork(android.net.wifi.WifiConfiguration);
@@ -26727,6 +26728,21 @@ package android.net.wifi {
field public static final int WPS_WEP_PROHIBITED = 4; // 0x4
}
+ public static class WifiManager.LocalOnlyHotspotCallback {
+ ctor public WifiManager.LocalOnlyHotspotCallback();
+ method public void onFailed(int);
+ method public void onStarted(android.net.wifi.WifiManager.LocalOnlyHotspotReservation);
+ method public void onStopped();
+ field public static final int ERROR_GENERIC = 2; // 0x2
+ field public static final int ERROR_INCOMPATIBLE_MODE = 3; // 0x3
+ field public static final int ERROR_NO_CHANNEL = 1; // 0x1
+ }
+
+ public class WifiManager.LocalOnlyHotspotReservation implements java.lang.AutoCloseable {
+ method public void close();
+ method public android.net.wifi.WifiConfiguration getConfig();
+ }
+
public class WifiManager.MulticastLock {
method public void acquire();
method public boolean isHeld();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7bf1d60d578b..0e1eb15ca9e5 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -40,6 +40,7 @@ import android.os.WorkSource;
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;
@@ -47,6 +48,7 @@ import com.android.server.net.NetworkPinner;
import dalvik.system.CloseGuard;
+import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
@@ -851,6 +853,22 @@ public class WifiManager {
private CountDownLatch mConnected;
private Looper mLooper;
+ /* LocalOnlyHotspot callback message types */
+ /** @hide */
+ public static final int HOTSPOT_STARTED = 0;
+ /** @hide */
+ public static final int HOTSPOT_STOPPED = 1;
+ /** @hide */
+ public static final int HOTSPOT_FAILED = 2;
+ /** @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
@@ -1864,12 +1882,27 @@ public class WifiManager {
* operating status.
* @param handler Handler to be used for callbacks. If the caller passes a null Handler, the
* main thread will be used.
- *
- * @hide
*/
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();
+ }
+ }
}
/**
@@ -1886,7 +1919,9 @@ public class WifiManager {
* @hide
*/
public void cancelLocalOnlyHotspotRequest() {
- throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ synchronized (mLock) {
+ stopLocalOnlyHotspot();
+ }
}
/**
@@ -1900,7 +1935,18 @@ public class WifiManager {
* 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();
+ }
+ }
}
/**
@@ -1922,7 +1968,18 @@ public class WifiManager {
*/
public void watchLocalOnlyHotspot(LocalOnlyHotspotObserver observer,
@Nullable Handler handler) {
- throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ synchronized (mLock) {
+ Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
+ mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, looper, observer);
+ try {
+ mService.startWatchLocalOnlyHotspot(
+ mLOHSObserverProxy.getMessenger(), new Binder());
+ mLOHSObserverProxy.registered();
+ } catch (RemoteException e) {
+ mLOHSObserverProxy = null;
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
@@ -1932,10 +1989,20 @@ public class WifiManager {
* @hide
*/
public void unregisterLocalOnlyHotspotObserver() {
- throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ synchronized (mLock) {
+ if (mLOHSObserverProxy == null) {
+ // nothing to do, the callback was already cleaned up
+ return;
+ }
+ mLOHSObserverProxy = null;
+ try {
+ mService.stopWatchLocalOnlyHotspot();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
-
/**
* Gets the Wi-Fi enabled state.
* @return One of {@link #WIFI_AP_STATE_DISABLED},
@@ -2205,8 +2272,6 @@ public class WifiManager {
* any further callbacks. If the LocalOnlyHotspot is stopped due to a
* user triggered mode change, applications will be notified via the {@link
* LocalOnlyHotspotCallback#onStopped()} callback.
- *
- * @hide
*/
public class LocalOnlyHotspotReservation implements AutoCloseable {
@@ -2249,8 +2314,6 @@ public class WifiManager {
/**
* Callback class for applications to receive updates about the LocalOnlyHotspot status.
- *
- * @hide
*/
public static class LocalOnlyHotspotCallback {
public static final int ERROR_NO_CHANNEL = 1;
@@ -2283,6 +2346,88 @@ public class WifiManager {
}
/**
+ * Callback proxy for LocalOnlyHotspotCallback objects.
+ */
+ private static class LocalOnlyHotspotCallbackProxy {
+ private final Handler mHandler;
+ private final WeakReference<WifiManager> mWifiManager;
+ private final Looper mLooper;
+ private final Messenger mMessenger;
+
+ /**
+ * Constructs a {@link LocalOnlyHotspotCallback} using the specified looper. All callbacks
+ * will be delivered on the thread of the specified looper.
+ *
+ * @param manager WifiManager
+ * @param looper Looper for delivering callbacks
+ * @param callback LocalOnlyHotspotCallback to notify the calling application.
+ */
+ LocalOnlyHotspotCallbackProxy(WifiManager manager, Looper looper,
+ final LocalOnlyHotspotCallback callback) {
+ mWifiManager = new WeakReference<>(manager);
+ mLooper = looper;
+
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, "LocalOnlyHotspotCallbackProxy: handle message what: "
+ + msg.what + " msg: " + msg);
+
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) {
+ Log.w(TAG, "LocalOnlyHotspotCallbackProxy: handle message post GC");
+ return;
+ }
+
+ switch (msg.what) {
+ case HOTSPOT_STARTED:
+ WifiConfiguration config = (WifiConfiguration) msg.obj;
+ if (config == null) {
+ Log.e(TAG, "LocalOnlyHotspotCallbackProxy: config cannot be null.");
+ callback.onFailed(LocalOnlyHotspotCallback.ERROR_GENERIC);
+ return;
+ }
+ callback.onStarted(manager.new LocalOnlyHotspotReservation(config));
+ break;
+ case HOTSPOT_STOPPED:
+ Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped");
+ callback.onStopped();
+ break;
+ case HOTSPOT_FAILED:
+ int reasonCode = msg.arg1;
+ Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: "
+ + reasonCode);
+ callback.onFailed(reasonCode);
+ Log.w(TAG, "done with the callback...");
+ break;
+ default:
+ Log.e(TAG, "LocalOnlyHotspotCallbackProxy unhandled message. type: "
+ + msg.what);
+ }
+ }
+ };
+ mMessenger = new Messenger(mHandler);
+ }
+
+ public Messenger getMessenger() {
+ return mMessenger;
+ }
+
+ /**
+ * Helper method allowing the the incoming application call to move the onFailed callback
+ * over to the desired callback thread.
+ *
+ * @param reason int representing the error type
+ */
+ public void notifyFailed(int reason) throws RemoteException {
+ Message msg = Message.obtain();
+ msg.what = HOTSPOT_FAILED;
+ msg.arg1 = reason;
+ mMessenger.send(msg);
+ }
+ }
+
+ /**
* LocalOnlyHotspotSubscription that is an AutoCloseable object for tracking applications
* watching for LocalOnlyHotspot changes.
*
@@ -2291,7 +2436,6 @@ public class WifiManager {
public class LocalOnlyHotspotSubscription implements AutoCloseable {
private final CloseGuard mCloseGuard = CloseGuard.get();
- /** @hide */
@VisibleForTesting
public LocalOnlyHotspotSubscription() {
mCloseGuard.open("close");
@@ -2343,6 +2487,75 @@ public class WifiManager {
public void onStopped() {};
}
+ /**
+ * Callback proxy for LocalOnlyHotspotObserver objects.
+ */
+ private static class LocalOnlyHotspotObserverProxy {
+ private final Handler mHandler;
+ private final WeakReference<WifiManager> mWifiManager;
+ private final Looper mLooper;
+ private final Messenger mMessenger;
+
+ /**
+ * Constructs a {@link LocalOnlyHotspotObserverProxy} using the specified looper.
+ * All callbacks will be delivered on the thread of the specified looper.
+ *
+ * @param manager WifiManager
+ * @param looper Looper for delivering callbacks
+ * @param observer LocalOnlyHotspotObserver to notify the calling application.
+ */
+ LocalOnlyHotspotObserverProxy(WifiManager manager, Looper looper,
+ final LocalOnlyHotspotObserver observer) {
+ mWifiManager = new WeakReference<>(manager);
+ mLooper = looper;
+
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ Log.d(TAG, "LocalOnlyHotspotObserverProxy: handle message what: "
+ + msg.what + " msg: " + msg);
+
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) {
+ Log.w(TAG, "LocalOnlyHotspotObserverProxy: handle message post GC");
+ return;
+ }
+
+ switch (msg.what) {
+ case HOTSPOT_OBSERVER_REGISTERED:
+ observer.onRegistered(manager.new LocalOnlyHotspotSubscription());
+ break;
+ case HOTSPOT_STARTED:
+ WifiConfiguration config = (WifiConfiguration) msg.obj;
+ if (config == null) {
+ Log.e(TAG, "LocalOnlyHotspotObserverProxy: config cannot be null.");
+ return;
+ }
+ observer.onStarted(config);
+ break;
+ case HOTSPOT_STOPPED:
+ observer.onStopped();
+ break;
+ default:
+ Log.e(TAG, "LocalOnlyHotspotObserverProxy unhandled message. type: "
+ + msg.what);
+ }
+ }
+ };
+ mMessenger = new Messenger(mHandler);
+ }
+
+ public Messenger getMessenger() {
+ return mMessenger;
+ }
+
+ public void registered() throws RemoteException {
+ Message msg = Message.obtain();
+ msg.what = HOTSPOT_OBSERVER_REGISTERED;
+ mMessenger.send(msg);
+ }
+ }
+
// Ensure that multiple ServiceHandler threads do not interleave message dispatch.
private static final Object sServiceHandlerDispatchLock = new Object();
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 000000000000..3c0fc6ef0aa8
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -0,0 +1,761 @@
+/*
+ * 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.assertNotNull;
+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;
+ public long mCallingThreadId = -1;
+
+ @Override
+ public void onRegistered(LocalOnlyHotspotSubscription sub) {
+ mOnRegistered = true;
+ mSub = sub;
+ mCallingThreadId = Thread.currentThread().getId();
+ }
+
+ @Override
+ public void onStarted(WifiConfiguration config) {
+ mOnStartedCalled = true;
+ mConfig = config;
+ mCallingThreadId = Thread.currentThread().getId();
+ }
+
+ @Override
+ public void onStopped() {
+ mOnStoppedCalled = true;
+ mCallingThreadId = Thread.currentThread().getId();
+ }
+ }
+
+ /**
+ * 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 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();
+ }
+
+ /**
+ * Verify the watchLocalOnlyHotspot call goes to WifiServiceImpl.
+ */
+ public void testWatchLocalOnlyHotspot() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+
+ mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
+ verify(mWifiService).startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ }
+
+ /**
+ * Verify a SecurityException is thrown for callers without proper permissions for
+ * startWatchLocalOnlyHotspot.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartWatchLocalOnlyHotspotThrowsSecurityException() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ doThrow(new SecurityException()).when(mWifiService)
+ .startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
+ }
+
+ /**
+ * Verify an IllegalStateException is thrown for callers that already have a pending request for
+ * watchLocalOnlyHotspot.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testStartWatchLocalOnlyHotspotThrowsIllegalStateException() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ doThrow(new IllegalStateException()).when(mWifiService)
+ .startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
+ }
+
+ /**
+ * Verify that the handler provided by the caller is used for the observer.
+ */
+ @Test
+ public void testCorrectLooperIsUsedForObserverHandler() throws Exception {
+ // record thread from looper.getThread and check ids.
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
+ mLooper.dispatchAll();
+ assertTrue(observer.mOnRegistered);
+ assertEquals(mLooper.getLooper().getThread().getId(), observer.mCallingThreadId);
+ }
+
+ /**
+ * Verify that the main looper's thread is used if a handler is not provided by the requesting
+ * application.
+ */
+ @Test
+ public void testMainLooperIsUsedWhenHandlerNotProvidedForObserver() throws Exception {
+ // record thread from looper.getThread and check ids.
+ TestLooper altLooper = new TestLooper();
+ when(mContext.getMainLooper()).thenReturn(altLooper.getLooper());
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ mWifiManager.watchLocalOnlyHotspot(observer, null);
+ altLooper.dispatchAll();
+ assertTrue(observer.mOnRegistered);
+ assertEquals(altLooper.getLooper().getThread().getId(), observer.mCallingThreadId);
+ }
+
+ /**
+ * Verify the LOHS onRegistered observer callback is triggered when WifiManager receives a
+ * HOTSPOT_OBSERVER_REGISTERED message from WifiServiceImpl.
+ */
+ @Test
+ public void testOnRegisteredIsCalledWithSubscription() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ TestLooper observerLooper = new TestLooper();
+ Handler observerHandler = new Handler(observerLooper.getLooper());
+ assertFalse(observer.mOnRegistered);
+ assertEquals(null, observer.mSub);
+ mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
+ verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class));
+ // now trigger the callback
+ observerLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertTrue(observer.mOnRegistered);
+ assertNotNull(observer.mSub);
+ }
+
+ /**
+ * Verify the LOHS onStarted observer callback is triggered when WifiManager receives a
+ * HOTSPOT_STARTED message from WifiServiceImpl.
+ */
+ @Test
+ public void testObserverOnStartedIsCalledWithWifiConfig() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ TestLooper observerLooper = new TestLooper();
+ Handler observerHandler = new Handler(observerLooper.getLooper());
+ mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
+ verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class));
+ observerLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertFalse(observer.mOnStartedCalled);
+ // now trigger the callback
+ Message msg = new Message();
+ msg.what = HOTSPOT_STARTED;
+ msg.obj = mApConfig;
+ mMessengerCaptor.getValue().send(msg);
+ mLooper.dispatchAll();
+ observerLooper.dispatchAll();
+ assertTrue(observer.mOnStartedCalled);
+ assertEquals(mApConfig, observer.mConfig);
+ }
+
+ /**
+ * Verify the LOHS onStarted observer callback is triggered not when WifiManager receives a
+ * HOTSPOT_STARTED message from WifiServiceImpl with a null config.
+ */
+ @Test
+ public void testObserverOnStartedNotCalledWithNullConfig() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ TestLooper observerLooper = new TestLooper();
+ Handler observerHandler = new Handler(observerLooper.getLooper());
+ mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
+ verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class));
+ observerLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertFalse(observer.mOnStartedCalled);
+ // now trigger the callback
+ Message msg = new Message();
+ msg.what = HOTSPOT_STARTED;
+ mMessengerCaptor.getValue().send(msg);
+ mLooper.dispatchAll();
+ observerLooper.dispatchAll();
+ assertFalse(observer.mOnStartedCalled);
+ assertEquals(null, observer.mConfig);
+ }
+
+
+ /**
+ * Verify the LOHS onStopped observer callback is triggered when WifiManager receives a
+ * HOTSPOT_STOPPED message from WifiServiceImpl.
+ */
+ @Test
+ public void testObserverOnStoppedIsCalled() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ TestLooper observerLooper = new TestLooper();
+ Handler observerHandler = new Handler(observerLooper.getLooper());
+ mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
+ verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
+ any(IBinder.class));
+ observerLooper.dispatchAll();
+ mLooper.dispatchAll();
+ assertFalse(observer.mOnStoppedCalled);
+ // now trigger the callback
+ Message msg = new Message();
+ msg.what = HOTSPOT_STOPPED;
+ mMessengerCaptor.getValue().send(msg);
+ mLooper.dispatchAll();
+ observerLooper.dispatchAll();
+ assertTrue(observer.mOnStoppedCalled);
+ }
+
+ /**
+ * Verify WifiServiceImpl is not called if there is not a registered LOHS observer callback.
+ */
+ @Test
+ public void testUnregisterWifiServiceImplNotCalledWithoutRegisteredObserver() throws Exception {
+ mWifiManager.unregisterLocalOnlyHotspotObserver();
+ verifyZeroInteractions(mWifiService);
+ }
+
+ /**
+ * Verify WifiServiceImpl is called when there is a registered LOHS observer callback.
+ */
+ @Test
+ public void testUnregisterWifiServiceImplCalledWithRegisteredObserver() throws Exception {
+ TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
+ TestLooper observerLooper = new TestLooper();
+ Handler observerHandler = new Handler(observerLooper.getLooper());
+ mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
+ mWifiManager.unregisterLocalOnlyHotspotObserver();
+ verify(mWifiService).stopWatchLocalOnlyHotspot();
+ }
+
+}