diff options
5 files changed, 456 insertions, 28 deletions
diff --git a/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java index 2925d966b..9e3b4fb2c 100644 --- a/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java +++ b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java @@ -17,6 +17,8 @@ package com.android.server.wifi; import android.annotation.NonNull; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; import android.os.Binder; import android.os.IBinder; import android.os.Message; @@ -31,7 +33,9 @@ import com.android.internal.util.Preconditions; * @hide */ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { - private final int mUid; + static final int HOTSPOT_NO_ERROR = -1; + + private final int mPid; private final IBinder mBinder; private final RequestingApplicationDeathCallback mCallback; private final Messenger mMessenger; @@ -48,7 +52,7 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { LocalOnlyHotspotRequestInfo(@NonNull IBinder binder, @NonNull Messenger messenger, @NonNull RequestingApplicationDeathCallback callback) { - mUid = Binder.getCallingUid(); + mPid = Binder.getCallingPid(); mBinder = Preconditions.checkNotNull(binder); mMessenger = Preconditions.checkNotNull(messenger); mCallback = Preconditions.checkNotNull(callback); @@ -76,21 +80,45 @@ public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient { } /** - * Send a message to WifiManager for the calling application. + * Send a HOTSPOT_FAILED message to WifiManager for the calling application with the error code. + * + * @param reasonCode error code for the message + * + * @throws RemoteException + */ + public void sendHotspotFailedMessage(int reasonCode) throws RemoteException { + Message message = Message.obtain(); + message.what = WifiManager.HOTSPOT_FAILED; + message.arg1 = reasonCode; + mMessenger.send(message); + } + + /** + * Send a HOTSPOT_STARTED message to WifiManager for the calling application with the config. + * + * @param config WifiConfiguration for the callback * - * @param what Message type to send - * @param arg1 arg1 for the message + * @throws RemoteException + */ + public void sendHotspotStartedMessage(WifiConfiguration config) throws RemoteException { + Message message = Message.obtain(); + message.what = WifiManager.HOTSPOT_STARTED; + message.obj = config; + mMessenger.send(message); + } + + /** + * Send a HOTSPOT_STOPPED message to WifiManager for the calling application. * * @throws RemoteException */ - public void sendMessage(int what, int arg1) throws RemoteException { + public void sendHotspotStoppedMessage() throws RemoteException { Message message = Message.obtain(); - message.what = what; - message.arg1 = arg1; + message.what = WifiManager.HOTSPOT_STOPPED; mMessenger.send(message); } - public int getUid() { - return mUid; + public int getPid() { + return mPid; } } diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 6a5c6d2f5..ce9784d2e 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -16,7 +16,18 @@ package com.android.server.wifi; +import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON; +import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; +import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC; +import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL; +import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; + import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED; +import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR; import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED; import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED; import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED; @@ -475,6 +486,21 @@ public class WifiServiceImpl extends IWifiManager.Stub { }, new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED)); + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int currentState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, + WIFI_AP_STATE_DISABLED); + final int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_AP_STATE, + WIFI_AP_STATE_DISABLED); + final int errorCode = intent.getIntExtra(EXTRA_WIFI_AP_FAILURE_REASON, + HOTSPOT_NO_ERROR); + handleWifiApStateChange(currentState, prevState, errorCode); + } + }, + new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)); + // Adding optimizations of only receiving broadcasts when wifi is enabled // can result in race conditions when apps toggle wifi in the background // without active user involvement. Always receive broadcasts. @@ -907,6 +933,108 @@ public class WifiServiceImpl extends IWifiManager.Stub { } /** + * Private method to handle SoftAp state changes + */ + private void handleWifiApStateChange(int currentState, int previousState, int errorCode) { + // The AP state update from WifiStateMachine for softap + Slog.d(TAG, "handleWifiApStateChange: currentState=" + currentState + + " previousState=" + previousState + " errorCode= " + errorCode); + + // check if we have a failure - since it is possible (worst case scenario where + // WifiController and WifiStateMachine are out of sync wrt modes) to get two FAILED + // notifications in a row, we need to handle this first. + if (currentState == WIFI_AP_STATE_FAILED) { + // update registered LOHS callbacks if we see a failure + synchronized (mLocalOnlyHotspotRequests) { + int errorToReport = ERROR_GENERIC; + if (errorCode == SAP_START_FAILURE_NO_CHANNEL) { + errorToReport = ERROR_NO_CHANNEL; + } + // holding the required lock: send message to requestors and clear the list + sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( + errorToReport); + } + return; + } + + if (currentState == WIFI_AP_STATE_DISABLING || currentState == WIFI_AP_STATE_DISABLED) { + // softap is shutting down or is down... let requestors know via the onStopped call + synchronized (mLocalOnlyHotspotRequests) { + // holding the required lock: send message to requestors and clear the list + sendHotspotStoppedMessageToAllLOHSRequestInfoEntriesLocked(); + } + return; + } + + // remaining states are enabling or enabled... those are not used for the callbacks + } + + /** + * Helper method to send a HOTSPOT_FAILED message to all registered LocalOnlyHotspotRequest + * callers and clear the registrations. + * + * Callers should already hold the mLocalOnlyHotspotRequests lock. + */ + private void sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(int arg1) { + for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) { + try { + requestor.sendHotspotFailedMessage(arg1); + } catch (RemoteException e) { + // This will be cleaned up by binder death handling + } + } + + // Since all callers were notified, now clear the registrations. + mLocalOnlyHotspotRequests.clear(); + } + + /** + * Helper method to send a HOTSPOT_STOPPED message to all registered LocalOnlyHotspotRequest + * callers and clear the registrations. + * + * Callers should already hold the mLocalOnlyHotspotRequests lock. + */ + private void sendHotspotStoppedMessageToAllLOHSRequestInfoEntriesLocked() { + for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) { + try { + requestor.sendHotspotStoppedMessage(); + } catch (RemoteException e) { + // This will be cleaned up by binder death handling + } + } + + // Since all callers were notified, now clear the registrations. + mLocalOnlyHotspotRequests.clear(); + } + + /** + * Helper method to send a HOTSPOT_STARTED message to all registered LocalOnlyHotspotRequest + * callers. + * + * Callers should already hold the mLocalOnlyHotspotRequests lock. + */ + private void sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked() { + for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) { + try { + requestor.sendHotspotStartedMessage(mLocalOnlyHotspotConfig); + } catch (RemoteException e) { + // This will be cleaned up by binder death handling + } + } + } + + /** + * Temporary method used for testing while startLocalOnlyHotspot is not fully implemented. This + * method allows unit tests to register callbacks directly for testing mechanisms triggered by + * softap mode changes. + * TODO: remove when startLocalOnlyHotspot is implemented. + */ + @VisibleForTesting + void registerLOHSForTest(int pid, LocalOnlyHotspotRequestInfo request) { + mLocalOnlyHotspotRequests.put(pid, request); + } + + /** * Method to start LocalOnlyHotspot. In this method, permissions, settings and modes are * checked to verify that we can enter softapmode. This method returns * {@link LocalOnlyHotspotCallback#REQUEST_REGISTERED} if we will attempt to start, otherwise, @@ -980,10 +1108,10 @@ public class WifiServiceImpl extends IWifiManager.Stub { * Helper method to unregister LocalOnlyHotspot requestors and stop the hotspot if needed. */ private void unregisterCallingAppAndStopLocalOnlyHotspot(LocalOnlyHotspotRequestInfo request) { - mLog.trace("unregisterCallingAppAndStopLocalOnlyHotspot uid=%").c(request.getUid()).flush(); + mLog.trace("unregisterCallingAppAndStopLocalOnlyHotspot pid=%").c(request.getPid()).flush(); synchronized (mLocalOnlyHotspotRequests) { - if (mLocalOnlyHotspotRequests.remove(request.getUid()) == null) { + if (mLocalOnlyHotspotRequests.remove(request.getPid()) == null) { mLog.trace("LocalOnlyHotspotRequestInfo not found to remove"); return; } diff --git a/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java b/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java index 0e8ead2e6..5f170b8f3 100644 --- a/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java +++ b/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java @@ -17,11 +17,10 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -129,23 +128,48 @@ public class LocalOnlyHotspotRequestInfoTest { } /** - * Verify the uid is properly set. + * Verify the pid is properly set. */ @Test - public void verifyUid() { + public void verifyPid() { mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); - assertEquals(Process.myUid(), mLOHSRequestInfo.getUid()); + assertEquals(Process.myPid(), mLOHSRequestInfo.getPid()); } /** - * Verify that sendMessage does send a Message properly + * Verify that sendHotspotFailedMessage does send a Message properly */ @Test - public void verifySendMessenger() throws Exception { + public void verifySendFailedMessage() throws Exception { mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); - mLOHSRequestInfo.sendMessage(1, 1); + mLOHSRequestInfo.sendHotspotFailedMessage( + WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC); Message message = mTestLooper.nextMessage(); - assertEquals(1, message.what); - assertEquals(1, message.arg1); + assertEquals(WifiManager.HOTSPOT_FAILED, message.what); + assertEquals(WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC, message.arg1); + } + + /** + * Verify that sendHotspotStartedMessage does send a Message properly + */ + @Test + public void verifySendStartedMessage() throws Exception { + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + WifiConfiguration config = mock(WifiConfiguration.class); + mLOHSRequestInfo.sendHotspotStartedMessage(config); + Message message = mTestLooper.nextMessage(); + assertEquals(WifiManager.HOTSPOT_STARTED, message.what); + assertEquals(config, (WifiConfiguration) message.obj); + } + + /** + * Verify that sendHotspotStoppedMessage does send a Message properly + */ + @Test + public void verifySendStoppedMessage() throws Exception { + mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback); + mLOHSRequestInfo.sendHotspotStoppedMessage(); + Message message = mTestLooper.nextMessage(); + assertEquals(WifiManager.HOTSPOT_STOPPED, message.what); } } diff --git a/tests/wifitests/src/com/android/server/wifi/TestUtil.java b/tests/wifitests/src/com/android/server/wifi/TestUtil.java index 3b43ad8c5..90df07a61 100644 --- a/tests/wifitests/src/com/android/server/wifi/TestUtil.java +++ b/tests/wifitests/src/com/android/server/wifi/TestUtil.java @@ -71,6 +71,21 @@ public class TestUtil { } /** + * Send {@link WifiManager#WIFI_AP_STATE_CHANGED} broadcast. + */ + public static void sendWifiApStateChanged(BroadcastReceiver broadcastReceiver, + Context context, int apState, int previousState, int error) { + Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, apState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousState); + if (apState == WifiManager.WIFI_AP_STATE_FAILED) { + //only set reason number when softAP start failed + intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, error); + } + broadcastReceiver.onReceive(context, intent); + } + + /** * Send {@link ConnectivityManager#ACTION_TETHER_STATE_CHANGED} broadcast. */ public static void sendTetherStateChanged(BroadcastReceiver broadcastReceiver, diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index bef3fc104..f1c19e6b3 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -16,12 +16,21 @@ package com.android.server.wifi; +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 android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED; +import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL; +import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; import static android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY; import static android.provider.Settings.Secure.LOCATION_MODE_OFF; +import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR; import static com.android.server.wifi.WifiController.CMD_SET_AP; import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; @@ -35,8 +44,10 @@ import static org.mockito.Mockito.*; import android.app.ActivityManager; import android.app.AppOpsManager; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.IntentFilter; import android.content.res.Resources; import android.net.IpConfiguration; import android.net.wifi.ScanSettings; @@ -64,6 +75,8 @@ import com.android.server.wifi.util.WifiPermissionsUtil; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; @@ -89,14 +102,21 @@ public class WifiServiceImplTest { private static final String TEST_PACKAGE_NAME = "TestPackage"; private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; private static final String SYSUI_PACKAGE_NAME = "com.android.systemui"; - private static final int TEST_UID = 6789; + private static final int TEST_PID = 6789; + private static final int TEST_PID2 = 9876; private WifiServiceImpl mWifiServiceImpl; private TestLooper mLooper; private PowerManager mPowerManager; private Handler mHandler; private Messenger mAppMessenger; - private int mUid; + private int mPid; + + final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + final ArgumentCaptor<IntentFilter> mIntentFilterCaptor = + ArgumentCaptor.forClass(IntentFilter.class); + @Mock Context mContext; @Mock WifiInjector mWifiInjector; @@ -182,10 +202,10 @@ public class WifiServiceImplTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); mLooper = new TestLooper(); - mHandler = new Handler(mLooper.getLooper()); + mHandler = spy(new Handler(mLooper.getLooper())); mAppMessenger = new Messenger(mHandler); - when(mRequestInfo.getUid()).thenReturn(mUid); + when(mRequestInfo.getPid()).thenReturn(mPid); when(mWifiInjector.getUserManager()).thenReturn(mUserManager); when(mWifiInjector.getWifiController()).thenReturn(mWifiController); when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); @@ -824,6 +844,219 @@ public class WifiServiceImplTest { verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(0), eq(0)); } + private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> { + @Override + public boolean matches(IntentFilter filter) { + return filter.hasAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + } + } + + /** + * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE + * broadcast is received. + */ + @Test + public void testRegisteredCallbacksTriggeredOnSoftApFailureGeneric() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL); + + verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); + } + + /** + * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE + * broadcast is received with the SAP_START_FAILURE_NO_CHANNEL error. + */ + @Test + public void testRegisteredCallbacksTriggeredOnSoftApFailureNoChannel() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_NO_CHANNEL); + + verify(mRequestInfo).sendHotspotFailedMessage(ERROR_NO_CHANNEL); + } + + /** + * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE + * broadcast is received with WIFI_AP_STATE_DISABLING. + */ + @Test + public void testRegisteredCallbacksTriggeredOnSoftApDisabling() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + + verify(mRequestInfo).sendHotspotStoppedMessage(); + } + + + /** + * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE + * broadcast is received with WIFI_AP_STATE_DISABLED. + */ + @Test + public void testRegisteredCallbacksTriggeredOnSoftApDisabled() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + + verify(mRequestInfo).sendHotspotStoppedMessage(); + } + + /** + * Verify that no callbacks are called for registered LOHS callers when a WIFI_AP_STATE_CHANGE + * broadcast is received and the softap started. + */ + @Test + public void testRegisteredCallbacksNotTriggeredOnSoftApStart() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR); + verifyNoMoreInteractions(mRequestInfo); + } + + /** + * Verify that onStopped is called only once for registered LOHS callers when + * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLING and + * WIFI_AP_STATE_DISABLED. + */ + @Test + public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApDisabling() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + + verify(mRequestInfo).sendHotspotStoppedMessage(); + } + + /** + * Verify that onFailed is called only once for registered LOHS callers when + * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED twice. + */ + @Test + public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApFailsTwice() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + + verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); + } + + /** + * Verify that onFailed is called for all registered LOHS callers when + * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED. + */ + @Test + public void testAllRegisteredCallbacksTriggeredWhenSoftApFails() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + // make an additional request for this test + LocalOnlyHotspotRequestInfo request2 = mock(LocalOnlyHotspotRequestInfo.class); + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + mWifiServiceImpl.registerLOHSForTest(TEST_PID2, request2); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC); + + verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC); + verify(request2).sendHotspotFailedMessage(ERROR_GENERIC); + } + + /** + * Verify that onFailed is called for all registered LOHS callers when + * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLED. + */ + @Test + public void testAllRegisteredCallbacksTriggeredWhenSoftApStops() throws Exception { + when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mWifiServiceImpl.checkAndStartWifi(); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + + // make an additional request for this test + LocalOnlyHotspotRequestInfo request2 = mock(LocalOnlyHotspotRequestInfo.class); + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); + mWifiServiceImpl.registerLOHSForTest(TEST_PID2, request2); + + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR); + TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext, + WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR); + + verify(mRequestInfo).sendHotspotStoppedMessage(); + verify(request2).sendHotspotStoppedMessage(); + } + /** * Verify that a call to startWatchLocalOnlyHotspot is only allowed from callers with the * signature only NETWORK_SETTINGS permission. |