summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/Osu2/src/com/android/osu/NetworkConnection.java206
-rw-r--r--packages/Osu2/src/com/android/osu/ProvisionService.java39
-rw-r--r--packages/Osu2/tests/Android.mk43
-rw-r--r--packages/Osu2/tests/AndroidManifest.xml38
-rw-r--r--packages/Osu2/tests/README.md45
-rwxr-xr-xpackages/Osu2/tests/runtests.sh24
-rw-r--r--packages/Osu2/tests/src/com/android/osu/NetworkConnectionTest.java132
7 files changed, 526 insertions, 1 deletions
diff --git a/packages/Osu2/src/com/android/osu/NetworkConnection.java b/packages/Osu2/src/com/android/osu/NetworkConnection.java
new file mode 100644
index 000000000000..9f5b929e96d3
--- /dev/null
+++ b/packages/Osu2/src/com/android/osu/NetworkConnection.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.osu;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Responsible for setup/monitor on a Wi-Fi connection.
+ */
+public class NetworkConnection {
+ private static final String TAG = "OSU_NetworkConnection";
+
+ private final WifiManager mWifiManager;
+ private final Callbacks mCallbacks;
+ private final int mNetworkId;
+ private boolean mConnected = false;
+
+ /**
+ * Callbacks on Wi-Fi connection state changes.
+ */
+ public interface Callbacks {
+ /**
+ * Invoked when network connection is established with IP connectivity.
+ *
+ * @param network {@link Network} associated with the connected network.
+ */
+ public void onConnected(Network network);
+
+ /**
+ * Invoked when the targeted network is disconnected.
+ */
+ public void onDisconnected();
+
+ /**
+ * Invoked when network connection is not established within the pre-defined timeout.
+ */
+ public void onTimeout();
+ }
+
+ /**
+ * Create an instance of {@link NetworkConnection} for the specified Wi-Fi network.
+ * The Wi-Fi network (specified by its SSID) will be added/enabled as part of this object
+ * creation.
+ *
+ * {@link #teardown} will need to be invoked once you're done with this connection,
+ * to remove the given Wi-Fi network from the framework.
+ *
+ * @param context The application context
+ * @param handler The handler to dispatch the processing of received broadcast intents
+ * @param ssid The SSID to connect to
+ * @param nai The network access identifier associated with the AP
+ * @param callbacks The callbacks to be invoked on network change events
+ * @throws IOException when failed to add/enable the specified Wi-Fi network
+ */
+ public NetworkConnection(Context context, Handler handler, WifiSsid ssid, String nai,
+ Callbacks callbacks) throws IOException {
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mCallbacks = callbacks;
+ mNetworkId = connect(ssid, nai);
+
+ // TODO(zqiu): setup alarm to timed out the connection attempt.
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ handleNetworkStateChanged(
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO),
+ intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO));
+ }
+ }
+ };
+ // Provide a Handler so that the onReceive call will be run on the specified handler
+ // thread instead of the main thread.
+ context.registerReceiver(receiver, filter, null, handler);
+ }
+
+ /**
+ * Teardown the network connection by removing the network.
+ */
+ public void teardown() {
+ mWifiManager.removeNetwork(mNetworkId);
+ }
+
+ /**
+ * Connect to a OSU Wi-Fi network specified by the given SSID. The security type of the Wi-Fi
+ * network is either open or OSEN (OSU Server-only authenticated layer 2 Encryption Network).
+ * When network access identifier is provided, OSEN is used.
+ *
+ * @param ssid The SSID to connect to
+ * @param nai Network access identifier of the network
+ *
+ * @return unique ID associated with the network
+ * @throws IOException
+ */
+ private int connect(WifiSsid ssid, String nai) throws IOException {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "\"" + ssid.toString() + "\"";
+ if (TextUtils.isEmpty(nai)) {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ } else {
+ // TODO(zqiu): configuration setup for OSEN.
+ }
+ int networkId = mWifiManager.addNetwork(config);
+ if (networkId < 0) {
+ throw new IOException("Failed to add OSU network");
+ }
+ if (!mWifiManager.enableNetwork(networkId, true)) {
+ throw new IOException("Failed to enable OSU network");
+ }
+ return networkId;
+ }
+
+ /**
+ * Handle network state changed events.
+ *
+ * @param networkInfo {@link NetworkInfo} indicating the current network state
+ * @param wifiInfo {@link WifiInfo} associated with the current network when connected
+ */
+ private void handleNetworkStateChanged(NetworkInfo networkInfo, WifiInfo wifiInfo) {
+ if (networkInfo == null) {
+ Log.e(TAG, "NetworkInfo not provided for network state changed event");
+ return;
+ }
+ switch (networkInfo.getDetailedState()) {
+ case CONNECTED:
+ handleConnectedEvent(wifiInfo);
+ break;
+ case DISCONNECTED:
+ handleDisconnectedEvent();
+ break;
+ default:
+ Log.d(TAG, "Ignore uninterested state: " + networkInfo.getDetailedState());
+ break;
+ }
+ }
+
+ /**
+ * Handle network connected event.
+ *
+ * @param wifiInfo {@link WifiInfo} associated with the current connection
+ */
+ private void handleConnectedEvent(WifiInfo wifiInfo) {
+ if (mConnected) {
+ // No-op if already connected.
+ return;
+ }
+ if (wifiInfo == null) {
+ Log.e(TAG, "WifiInfo not provided for connected event");
+ return;
+ }
+ if (wifiInfo.getNetworkId() != mNetworkId) {
+ return;
+ }
+ Network network = mWifiManager.getCurrentNetwork();
+ if (network == null) {
+ Log.e(TAG, "Current network is not set");
+ return;
+ }
+ mConnected = true;
+ mCallbacks.onConnected(network);
+ }
+
+ /**
+ * Handle network disconnected event.
+ */
+ private void handleDisconnectedEvent() {
+ if (!mConnected) {
+ // No-op if not connected, most likely a disconnect event for a different network.
+ return;
+ }
+ mConnected = false;
+ mCallbacks.onDisconnected();
+ }
+}
diff --git a/packages/Osu2/src/com/android/osu/ProvisionService.java b/packages/Osu2/src/com/android/osu/ProvisionService.java
index 7f683a7a742a..b1d43b2b0ea9 100644
--- a/packages/Osu2/src/com/android/osu/ProvisionService.java
+++ b/packages/Osu2/src/com/android/osu/ProvisionService.java
@@ -17,6 +17,7 @@
package com.android.osu;
import android.content.Context;
+import android.net.Network;
import android.net.wifi.hotspot2.OsuProvider;
import android.os.Handler;
import android.os.HandlerThread;
@@ -24,6 +25,8 @@ import android.os.Looper;
import android.os.Message;
import android.util.Log;
+import java.io.IOException;
+
/**
* Service responsible for performing Passpoint subscription provisioning tasks.
* This service will run on a separate thread to avoid blocking on the Main thread.
@@ -38,6 +41,9 @@ public class ProvisionService implements OsuService {
private final ServiceHandler mServiceHandler;
private final OsuProvider mProvider;
+ private boolean mStarted = false;
+ private NetworkConnection mNetworkConnection = null;
+
public ProvisionService(Context context, OsuProvider provider) {
mContext = context;
mProvider = provider;
@@ -68,9 +74,25 @@ public class ProvisionService implements OsuService {
public void handleMessage(Message msg) {
switch (msg.what) {
case COMMAND_START:
- Log.e(TAG, "Start provision service");
+ if (mStarted) {
+ Log.e(TAG, "Service already started");
+ return;
+ }
+ try {
+ // Initiate network connection to the OSU AP.
+ mNetworkConnection = new NetworkConnection(
+ mContext, this, mProvider.getOsuSsid(),
+ mProvider.getNetworkAccessIdentifier(), new NetworkCallbacks());
+ mStarted = true;
+ } catch (IOException e) {
+ // TODO(zqiu): broadcast failure event via LocalBroadcastManager.
+ }
break;
case COMMAND_STOP:
+ if (!mStarted) {
+ Log.e(TAG, "Service not started");
+ return;
+ }
Log.e(TAG, "Stop provision service");
break;
default:
@@ -79,4 +101,19 @@ public class ProvisionService implements OsuService {
}
}
}
+
+ private final class NetworkCallbacks implements NetworkConnection.Callbacks {
+ @Override
+ public void onConnected(Network network) {
+ Log.d(TAG, "Connected to OSU AP");
+ }
+
+ @Override
+ public void onDisconnected() {
+ }
+
+ @Override
+ public void onTimeout() {
+ }
+ }
}
diff --git a/packages/Osu2/tests/Android.mk b/packages/Osu2/tests/Android.mk
new file mode 100644
index 000000000000..4b6e0e60652b
--- /dev/null
+++ b/packages/Osu2/tests/Android.mk
@@ -0,0 +1,43 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_JACK_FLAGS := --multi-dex native
+
+LOCAL_PACKAGE_NAME := OsuTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_INSTRUMENTATION_FOR := Osu2
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-target-minus-junit4 \
+ frameworks-base-testutils
+
+# Code coverage puts us over the dex limit, so enable multi-dex for coverage-enabled builds
+ifeq (true,$(EMMA_INSTRUMENT))
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+endif # EMMA_INSTRUMENT
+
+include $(BUILD_PACKAGE)
diff --git a/packages/Osu2/tests/AndroidManifest.xml b/packages/Osu2/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..e22c1122958a
--- /dev/null
+++ b/packages/Osu2/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.osu.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:label="OsuTestDummyLabel"
+ android:name="OsuTestDummyName">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.osu"
+ android:label="OSU App Tests">
+ </instrumentation>
+
+</manifest>
diff --git a/packages/Osu2/tests/README.md b/packages/Osu2/tests/README.md
new file mode 100644
index 000000000000..dbfa79c3d26f
--- /dev/null
+++ b/packages/Osu2/tests/README.md
@@ -0,0 +1,45 @@
+# OSU Unit Tests
+This package contains unit tests for the OSU app based on the
+[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html).
+The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/)
+libraries.
+
+## Running Tests
+The easiest way to run tests is simply run
+
+```
+frameworks/base/packages/Osu2/tests/runtests.sh
+```
+
+`runtests.sh` will build the test project and all of its dependencies and push the APK to the
+connected device. It will then run the tests on the device.
+
+To enable syncing data to the device for first time after clean reflash:
+1. adb disable-verity
+2. adb reboot
+3. adb remount
+
+See below for a few example of options to limit which tests are run.
+See the
+[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html)
+for more details on the supported options.
+
+```
+runtests.sh -e package com.android.osu
+runtests.sh -e class com.android.osu.NetworkConnectionTest
+```
+
+If you manually build and push the test APK to the device you can run tests using
+
+```
+adb shell am instrument -w 'com.android.osu.tests/android.support.test.runner.AndroidJUnitRunner'
+```
+
+## Adding Tests
+Tests can be added by adding classes to the src directory. JUnit4 style test cases can
+be written by simply annotating test methods with `org.junit.Test`.
+
+## Debugging Tests
+If you are trying to debug why tests are not doing what you expected, you can add android log
+statements and use logcat to view them. The beginning and end of every tests is automatically logged
+with the tag `TestRunner`.
diff --git a/packages/Osu2/tests/runtests.sh b/packages/Osu2/tests/runtests.sh
new file mode 100755
index 000000000000..3513f5b8fc64
--- /dev/null
+++ b/packages/Osu2/tests/runtests.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+ echo "You need to source and lunch before you can use this script"
+ exit 1
+fi
+
+echo "Running tests"
+
+set -e # fail early
+
+echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/base/packages/Osu2/tests"
+# NOTE Don't actually run the command above since this shell doesn't inherit functions from the
+# caller.
+make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk MODULES-IN-frameworks-base-packages-Osu2-tests
+
+set -x # print commands
+
+adb root
+adb wait-for-device
+
+adb install -r -g "$OUT/data/app/OsuTests/OsuTests.apk"
+
+adb shell am instrument -w "$@" 'com.android.osu.tests/android.support.test.runner.AndroidJUnitRunner'
diff --git a/packages/Osu2/tests/src/com/android/osu/NetworkConnectionTest.java b/packages/Osu2/tests/src/com/android/osu/NetworkConnectionTest.java
new file mode 100644
index 000000000000..2753249aa32e
--- /dev/null
+++ b/packages/Osu2/tests/src/com/android/osu/NetworkConnectionTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.osu;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link com.android.osu.NetworkConnection}.
+ */
+@SmallTest
+public class NetworkConnectionTest {
+ private static final String TEST_SSID = "TEST SSID";
+ private static final String TEST_SSID_WITH_QUOTES = "\"" + TEST_SSID + "\"";
+ private static final int TEST_NETWORK_ID = 1;
+
+ @Mock Context mContext;
+ @Mock Handler mHandler;
+ @Mock WifiManager mWifiManager;
+ @Mock NetworkConnection.Callbacks mCallbacks;
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+ }
+
+ /**
+ * Verify that an IOException will be thrown when failed to add the network.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IOException.class)
+ public void networkAddFailed() throws Exception {
+ when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(-1);
+ new NetworkConnection(mContext, mHandler, WifiSsid.createFromAsciiEncoded(TEST_SSID),
+ null, mCallbacks);
+ }
+
+ /**
+ * Verify that an IOException will be thrown when failed to enable the network.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IOException.class)
+ public void networkEnableFailed() throws Exception {
+ when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(TEST_NETWORK_ID);
+ when(mWifiManager.enableNetwork(eq(TEST_NETWORK_ID), eq(true))).thenReturn(false);
+ new NetworkConnection(mContext, mHandler, WifiSsid.createFromAsciiEncoded(TEST_SSID),
+ null, mCallbacks);
+ }
+
+ /**
+ * Verify that the connection is established after receiving a
+ * WifiManager.NETWORK_STATE_CHANGED_ACTION intent indicating that we are connected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void openNetworkConnectionEstablished() throws Exception {
+ when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(TEST_NETWORK_ID);
+ when(mWifiManager.enableNetwork(eq(TEST_NETWORK_ID), eq(true))).thenReturn(true);
+ NetworkConnection connection = new NetworkConnection(mContext, mHandler,
+ WifiSsid.createFromAsciiEncoded(TEST_SSID), null, mCallbacks);
+
+ // Verify the WifiConfiguration being added.
+ ArgumentCaptor<WifiConfiguration> wifiConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiManager).addNetwork(wifiConfig.capture());
+ assertEquals(wifiConfig.getValue().SSID, TEST_SSID_WITH_QUOTES);
+
+ // Capture the BroadcastReceiver.
+ ArgumentCaptor<BroadcastReceiver> receiver =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContext).registerReceiver(receiver.capture(), any(), any(), any());
+
+ // Setup intent.
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setNetworkId(TEST_NETWORK_ID);
+ intent.putExtra(WifiManager.EXTRA_WIFI_INFO, wifiInfo);
+
+ // Send intent to the receiver.
+ Network network = new Network(0);
+ when(mWifiManager.getCurrentNetwork()).thenReturn(network);
+ receiver.getValue().onReceive(mContext, intent);
+
+ // Verify we are connected.
+ verify(mCallbacks).onConnected(eq(network));
+ }
+}