blob: 4f2f3476bd9fd4f5e84fe642718ac3f5563c2dfc [file] [log] [blame]
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi.hotspot2;
import com.android.org.conscrypt.TrustManagerImpl;
import static org.junit.Assert.*;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.Network;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid;
import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.ProvisioningCallback;
import android.os.Handler;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.net.URL;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
/**
* Unit tests for {@link com.android.server.wifi.hotspot2.PasspointProvisioner}.
*/
@SmallTest
public class PasspointProvisionerTest {
private static final String TAG = "PasspointProvisionerTest";
private static final int TEST_UID = 1500;
private PasspointProvisioner mPasspointProvisioner;
private TestLooper mLooper = new TestLooper();
private Handler mHandler;
private OsuNetworkConnection.Callbacks mOsuNetworkCallbacks;
private PasspointProvisioner.OsuServerCallbacks mOsuServerCallbacks;
private ArgumentCaptor<OsuNetworkConnection.Callbacks> mOsuNetworkCallbacksCaptor =
ArgumentCaptor.forClass(OsuNetworkConnection.Callbacks.class);
private ArgumentCaptor<PasspointProvisioner.OsuServerCallbacks> mOsuServerCallbacksCaptor =
ArgumentCaptor.forClass(PasspointProvisioner.OsuServerCallbacks.class);
private ArgumentCaptor<Handler> mHandlerCaptor = ArgumentCaptor.forClass(Handler.class);
private OsuProvider mOsuProvider;
private TrustManagerImpl mDelegate;
@Mock PasspointObjectFactory mObjectFactory;
@Mock Context mContext;
@Mock WifiManager mWifiManager;
@Mock IProvisioningCallback mCallback;
@Mock OsuNetworkConnection mOsuNetworkConnection;
@Mock OsuServerConnection mOsuServerConnection;
@Mock Network mNetwork;
@Mock WfaKeyStore mWfaKeyStore;
@Mock KeyStore mKeyStore;
@Mock SSLContext mTlsContext;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
when(mObjectFactory.makeOsuNetworkConnection(any(Context.class)))
.thenReturn(mOsuNetworkConnection);
when(mObjectFactory.makeOsuServerConnection()).thenReturn(mOsuServerConnection);
when(mWfaKeyStore.get()).thenReturn(mKeyStore);
when(mObjectFactory.makeWfaKeyStore()).thenReturn(mWfaKeyStore);
when(mObjectFactory.getSSLContext(any(String.class))).thenReturn(mTlsContext);
doReturn(mWifiManager).when(mContext)
.getSystemService(eq(Context.WIFI_SERVICE));
mPasspointProvisioner = new PasspointProvisioner(mContext, mObjectFactory);
when(mOsuNetworkConnection.connect(any(WifiSsid.class), any())).thenReturn(true);
when(mOsuServerConnection.connect(any(URL.class), any(Network.class))).thenReturn(true);
when(mOsuServerConnection.validateProvider(any(String.class))).thenReturn(true);
when(mOsuServerConnection.canValidateServer()).thenReturn(true);
mPasspointProvisioner.enableVerboseLogging(1);
mOsuProvider = PasspointProvisioningTestUtil.generateOsuProvider(true);
mDelegate = new TrustManagerImpl(PasspointProvisioningTestUtil.createFakeKeyStore());
when(mObjectFactory.getTrustManagerImpl(any(KeyStore.class))).thenReturn(mDelegate);
}
private void initAndStartProvisioning() {
mPasspointProvisioner.init(mLooper.getLooper());
verify(mOsuNetworkConnection).init(mHandlerCaptor.capture());
mHandler = mHandlerCaptor.getValue();
assertEquals(mHandler.getLooper(), mLooper.getLooper());
mLooper.dispatchAll();
assertTrue(mPasspointProvisioner.startSubscriptionProvisioning(
TEST_UID, mOsuProvider, mCallback));
// Runnable posted by the provisioning start request
assertEquals(mHandler.hasMessagesOrCallbacks(), true);
mLooper.dispatchAll();
assertEquals(mHandler.hasMessagesOrCallbacks(), false);
verify(mOsuNetworkConnection, atLeastOnce())
.setEventCallback(mOsuNetworkCallbacksCaptor.capture());
mOsuNetworkCallbacks = mOsuNetworkCallbacksCaptor.getAllValues().get(0);
verify(mOsuServerConnection, atLeastOnce())
.setEventCallback(mOsuServerCallbacksCaptor.capture());
mOsuServerCallbacks = mOsuServerCallbacksCaptor.getAllValues().get(0);
}
/**
* Verifies initialization and starting subscription provisioning flow
*/
@Test
public void verifyInitAndStartProvisioning() throws RemoteException {
initAndStartProvisioning();
}
/**
* Verifies initialization and starting subscription provisioning flow
*/
@Test
public void verifyProvisioningUnavailable() throws RemoteException {
when(mOsuServerConnection.canValidateServer()).thenReturn(false);
initAndStartProvisioning();
verify(mCallback).onProvisioningFailure(
ProvisioningCallback.OSU_FAILURE_PROVISIONING_NOT_AVAILABLE);
}
/**
* Verifies existing provisioning flow is aborted before starting another one.
*/
@Test
public void verifyProvisioningAbort() throws RemoteException {
initAndStartProvisioning();
verify(mCallback).onProvisioningStatus(
ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
IProvisioningCallback mCallback2 = mock(IProvisioningCallback.class);
assertTrue(mPasspointProvisioner.startSubscriptionProvisioning(
TEST_UID, mOsuProvider, mCallback2));
// Runnable posted by the provisioning start request
assertEquals(mHandler.hasMessagesOrCallbacks(), true);
mLooper.dispatchAll();
assertEquals(mHandler.hasMessagesOrCallbacks(), false);
verify(mCallback).onProvisioningFailure(
ProvisioningCallback.OSU_FAILURE_PROVISIONING_ABORTED);
}
/**
* Verifies that the right provisioning callbacks are invoked as the provisioner progresses
* to associating with the OSU AP, connecting to the OSU Server, validating its certificates
* and verifying the OSU provider.
*/
@Test
public void verifyProvisioningFlow() throws RemoteException {
initAndStartProvisioning();
// state exptected is WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected is OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
// state expected is OSU_SERVER_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_CONNECTED);
// Wait for server validation status
assertFalse(mHandler.hasMessagesOrCallbacks());
// Server validation passed
mOsuServerCallbacks.onServerValidationStatus(mOsuServerCallbacks.getSessionId(), true);
assertTrue(mHandler.hasMessagesOrCallbacks());
mLooper.dispatchAll();
// state expected is OSU_SERVER_VALIDATED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_VALIDATED);
// state expected is OSU_PROVIDER_VALIDATED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_PROVIDER_VERIFIED);
// Osu provider verification is the last current step in the flow, no more runnables posted.
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that if connection attempt to OSU AP fails, corresponding error callback is invoked.
*/
@Test
public void verifyConnectAttemptFailure() throws RemoteException {
when(mOsuNetworkConnection.connect(any(WifiSsid.class), any())).thenReturn(false);
initAndStartProvisioning();
// Since connection attempt fails, directly move to FAILED_STATE
verify(mCallback).onProvisioningFailure(ProvisioningCallback.OSU_FAILURE_AP_CONNECTION);
// Failure case, no more runnables posted
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that after connection attemp succeeds and a network disconnection occurs, the
* corresponding failure callback is invoked.
*/
@Test
public void verifyConnectAttemptedAndConnectionFailed() throws RemoteException {
initAndStartProvisioning();
// state expected is WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP fails
mOsuNetworkCallbacks.onDisconnected();
// Move to failed state
verify(mCallback).onProvisioningFailure(ProvisioningCallback.OSU_FAILURE_AP_CONNECTION);
// Failure case, no more runnables posted
assertEquals(mHandler.hasMessagesOrCallbacks(), false);
}
/**
* Verifies that a connection drop is reported via callback.
*/
@Test
public void verifyConnectionDrop() throws RemoteException {
initAndStartProvisioning();
// state expected WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
// state expected OSU_SERVER_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_CONNECTED);
// Disconnect received
mOsuNetworkCallbacks.onDisconnected();
// Move to failed state
verify(mCallback).onProvisioningFailure(ProvisioningCallback.OSU_FAILURE_AP_CONNECTION);
// No more callbacks, Osu server validation not initiated
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that Wifi Disable while provisioning is communicated as provisioning failure
*/
@Test
public void verifyFailureDueToWifiDisable() throws RemoteException {
initAndStartProvisioning();
// state expected WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected is OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
// state expecte is OSU_SERVER_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_CONNECTED);
// Wifi disabled notification
mOsuNetworkCallbacks.onWifiDisabled();
// Wifi Disable is processed first and move to failed state
verify(mCallback).onProvisioningFailure(ProvisioningCallback.OSU_FAILURE_AP_CONNECTION);
// OSU server connection event is not handled
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that the right provisioning callbacks are invoked as the provisioner connects
* to OSU AP and OSU server and that invalid server URL generates the right error callback.
*/
@Test
public void verifyInvalidOsuServerURL() throws RemoteException {
mOsuProvider = PasspointProvisioningTestUtil.generateInvalidServerUrlOsuProvider();
initAndStartProvisioning();
// Attempting to connect to OSU server fails due to invalid server URL, move to failed state
verify(mCallback).onProvisioningFailure(
ProvisioningCallback.OSU_FAILURE_SERVER_URL_INVALID);
// No further runnables posted
assertEquals(mHandler.hasMessagesOrCallbacks(), false);
}
/**
* Verifies that the right provisioning callbacks are invoked as the provisioner progresses
* to associating with the OSU AP and connection to OSU server fails.
*/
@Test
public void verifyServerConnectionFailure() throws RemoteException {
initAndStartProvisioning();
when(mOsuServerConnection.connect(any(URL.class), any(Network.class))).thenReturn(false);
// expected state is WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP is successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected is OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
// Connection to OSU Server fails, move to failed state
verify(mCallback).onProvisioningFailure(ProvisioningCallback.OSU_FAILURE_SERVER_CONNECTION);
// No further runnables posted
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that the right provisioning callbacks are invoked as the provisioner is unable
* to validate the OSU Server
*/
@Test
public void verifyServerValidationFailure() throws RemoteException {
initAndStartProvisioning();
// state expected is WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP is successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected is OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
// state expected is OSU_SERVER_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_CONNECTED);
// OSU Server validation of certs fails
mOsuServerCallbacks.onServerValidationStatus(mOsuServerCallbacks.getSessionId(), false);
// Runnable posted by server callback
assertTrue(mHandler.hasMessagesOrCallbacks());
mLooper.dispatchAll();
// Server validation failure, move to failed state
verify(mCallback).onProvisioningFailure(ProvisioningCallback.OSU_FAILURE_SERVER_VALIDATION);
// No further runnables posted
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that the status for server validation from a previous session is ignored
* by the provisioning state machine
*/
@Test
public void verifyServerValidationFailurePreviousSession() throws RemoteException {
initAndStartProvisioning();
// state expected is WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP is successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected is OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
// state expected is OSU_SERVER_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_CONNECTED);
// OSU Server validation of certs failure but from a previous session
mOsuServerCallbacks.onServerValidationStatus(mOsuServerCallbacks.getSessionId() - 1, false);
// Runnable posted by server callback
assertTrue(mHandler.hasMessagesOrCallbacks());
mLooper.dispatchAll();
// Server validation failure, move to failed state
verify(mCallback, never()).onProvisioningFailure(
ProvisioningCallback.OSU_FAILURE_SERVER_VALIDATION);
// No further runnables posted
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that the status for server validation from a previous session is ignored
* by the provisioning state machine
*/
@Test
public void verifyServerValidationSuccessPreviousSession() throws RemoteException {
initAndStartProvisioning();
// state expected is WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP is successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected is OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
// state expected is OSU_SERVER_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_CONNECTED);
// OSU Server validation success but from a previous session
mOsuServerCallbacks.onServerValidationStatus(mOsuServerCallbacks.getSessionId() - 1, true);
// Runnable posted by server callback
assertTrue(mHandler.hasMessagesOrCallbacks());
mLooper.dispatchAll();
// Server validation failure, move to failed state
verify(mCallback, never()).onProvisioningStatus(
ProvisioningCallback.OSU_STATUS_SERVER_VALIDATED);
// No further runnables posted
assertFalse(mHandler.hasMessagesOrCallbacks());
}
/**
* Verifies that the right provisioning callbacks are invoked when provisioner is unable
* to validate the OSU provider
*/
@Test
public void verifyProviderVerificationFailure() throws RemoteException {
initAndStartProvisioning();
when(mOsuServerConnection.validateProvider(any(String.class))).thenReturn(false);
// state expected is WAITING_TO_CONNECT
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTING);
// Connection to OSU AP successful
mOsuNetworkCallbacks.onConnected(mNetwork);
// state expected is OSU_AP_CONNECTED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_AP_CONNECTED);
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_CONNECTED);
// Wait for OSU server validation callback
mOsuServerCallbacks.onServerValidationStatus(mOsuServerCallbacks.getSessionId(), true);
// Runnable posted by server callback
assertTrue(mHandler.hasMessagesOrCallbacks());
// OSU server validation success posts another runnable to validate the provider
mLooper.dispatchAll();
// state expected is OSU_SERVER_VALIDATED
verify(mCallback).onProvisioningStatus(ProvisioningCallback.OSU_STATUS_SERVER_VALIDATED);
// Provider validation failure is processed next, move to failed state
verify(mCallback).onProvisioningFailure(
ProvisioningCallback.OSU_FAILURE_PROVIDER_VERIFICATION);
// No further runnables posted
assertFalse(mHandler.hasMessagesOrCallbacks());
}
}