From 686bb2d337517b0b3f4ff4a6062c84a3ee730307 Mon Sep 17 00:00:00 2001 From: Sudheer Shanka Date: Mon, 20 Mar 2017 12:16:32 -0700 Subject: [PATCH] Add tests for verifying network availability on activity start. Bug: 27803922 Test: runtest -c com.android.server.net.ConnOnActivityStartTest frameworks-services Change-Id: I771e1fd9edbe14f1ff7a7a84c3896af6dd6f20a0 --- services/tests/servicestests/Android.mk | 4 + .../tests/servicestests/AndroidManifest.xml | 2 + services/tests/servicestests/aidl/Android.mk | 23 + .../aidl/INetworkStateObserver.aidl | 27 ++ .../tests/servicestests/res/raw/conntestapp | Bin 0 -> 9172 bytes .../server/net/ConnOnActivityStartTest.java | 443 ++++++++++++++++++ .../test-apps/ConnTestApp/Android.mk | 30 ++ .../test-apps/ConnTestApp/AndroidManifest.xml | 28 ++ .../apps/conntestapp/ConnTestActivity.java | 170 +++++++ 9 files changed, 727 insertions(+) create mode 100644 services/tests/servicestests/aidl/Android.mk create mode 100644 services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl create mode 100644 services/tests/servicestests/res/raw/conntestapp create mode 100644 services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java create mode 100644 services/tests/servicestests/test-apps/ConnTestApp/Android.mk create mode 100644 services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml create mode 100644 services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index bb4507d8386a2..d47a67c563178 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -27,6 +27,10 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ ShortcutManagerTestUtils \ truth-prebuilt +LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl + +LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl + LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksServicesTests diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 6c7f146ed6804..ca3fcea5b5f5b 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -48,6 +48,8 @@ + + diff --git a/services/tests/servicestests/aidl/Android.mk b/services/tests/servicestests/aidl/Android.mk new file mode 100644 index 0000000000000..0c9b839628338 --- /dev/null +++ b/services/tests/servicestests/aidl/Android.mk @@ -0,0 +1,23 @@ +# 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_SDK_VERSION := current +LOCAL_SRC_FILES := \ + com/android/servicestests/aidl/INetworkStateObserver.aidl +LOCAL_MODULE := servicestests-aidl +include $(BUILD_STATIC_JAVA_LIBRARY) \ No newline at end of file diff --git a/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl b/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl new file mode 100644 index 0000000000000..ca9fc4c439d24 --- /dev/null +++ b/services/tests/servicestests/aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl @@ -0,0 +1,27 @@ +/* + * 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.servicestests.aidl; + +oneway interface INetworkStateObserver { + /** + * {@param resultData} will be in the format + * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo. + * For detailed info, see + * servicestests/test-apps/ConnTestApp/.../ConnTestActivity#checkNetworkStatus + */ + void onNetworkStateChecked(String resultData); +} \ No newline at end of file diff --git a/services/tests/servicestests/res/raw/conntestapp b/services/tests/servicestests/res/raw/conntestapp new file mode 100644 index 0000000000000000000000000000000000000000..6093303658b54706fea24724a5d12992c6e0824c GIT binary patch literal 9172 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 literal 0 HcmV?d00001 diff --git a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java new file mode 100644 index 0000000000000..f9719711cad92 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java @@ -0,0 +1,443 @@ +/* + * 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.server.net; + +import static android.util.DebugUtils.valueToString; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.android.frameworks.servicestests.R; +import com.android.servicestests.aidl.INetworkStateObserver; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.uiautomator.UiDevice; +import android.util.Log; + +import libcore.io.IoUtils; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Tests for verifying network availability on activity start. + * + * To run the tests, use + * + * runtest -c com.android.server.net.ConnOnActivityStartTest frameworks-services + * + * or the following steps: + * + * Build: m FrameworksServicesTests + * Install: adb install -r \ + * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk + * Run: adb shell am instrument -e class com.android.server.net.ConnOnActivityStartTest -w \ + * com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner + */ +@LargeTest +@RunWith(AndroidJUnit4.class) +public class ConnOnActivityStartTest { + private static final String TAG = ConnOnActivityStartTest.class.getSimpleName(); + + private static final String ACTION_INSTALL_COMPLETE = "com.android.server.net.INSTALL_COMPLETE"; + + private static final String TEST_APP_URI = + "android.resource://com.android.frameworks.servicestests/raw/conntestapp"; + private static final String TEST_PKG = "com.android.servicestests.apps.conntestapp"; + private static final String TEST_ACTIVITY_CLASS = TEST_PKG + ".ConnTestActivity"; + + private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH"; + + private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer"; + + private static final int WAIT_FOR_INSTALL_TIMEOUT_MS = 2000; // 2 sec + + private static final int NETWORK_CHECK_TIMEOUT_MS = 6000; // 6 sec + + private static final int SCREEN_ON_DELAY_MS = 500; // 0.5 sec + + private static final String NETWORK_STATUS_SEPARATOR = "\\|"; + + private static final int REPEAT_TEST_COUNT = 5; + + private static Context mContext; + private static UiDevice mUiDevice; + private static int mTestPkgUid; + + @BeforeClass + public static void setUpOnce() throws Exception { + mContext = InstrumentationRegistry.getContext(); + mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + installAppAndAssertInstalled(); + mContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); + mTestPkgUid = mContext.getPackageManager().getPackageUid(TEST_PKG, 0); + } + + @AfterClass + public static void tearDownOnce() { + mContext.getPackageManager().deletePackage(TEST_PKG, + new IPackageDeleteObserver.Stub() { + @Override + public void packageDeleted(String packageName, int returnCode) + throws RemoteException { + Log.e(TAG, packageName + " deleted, returnCode: " + returnCode); + } + }, 0); + } + + @Test + public void testStartActivity_batterySaver() throws Exception { + setBatterySaverMode(true); + try { + testConnOnActivityStart("testStartActivity_batterySaver"); + } finally { + setBatterySaverMode(false); + } + } + + @Test + public void testStartActivity_dataSaver() throws Exception { + setDataSaverMode(true); + try { + testConnOnActivityStart("testStartActivity_dataSaver"); + } finally { + setDataSaverMode(false); + } + } + + @Test + public void testStartActivity_dozeMode() throws Exception { + setDozeMode(true); + try { + testConnOnActivityStart("testStartActivity_dozeMode"); + } finally { + setDozeMode(false); + } + } + + @Test + public void testStartActivity_appStandby() throws Exception { + try{ + turnBatteryOff(); + setAppIdle(true); + SystemClock.sleep(30000); + turnScreenOn(); + startActivityAndCheckNetworkAccess(); + } finally { + turnBatteryOn(); + setAppIdle(false); + } + } + + @Test + public void testStartActivity_backgroundRestrict() throws Exception { + updateRestrictBackgroundBlacklist(true); + try { + testConnOnActivityStart("testStartActivity_backgroundRestrict"); + } finally { + updateRestrictBackgroundBlacklist(false); + } + } + + private void testConnOnActivityStart(String testName) throws Exception { + for (int i = 1; i <= REPEAT_TEST_COUNT; ++i) { + try { + Log.d(TAG, testName + " Start #" + i); + turnScreenOn(); + SystemClock.sleep(SCREEN_ON_DELAY_MS); + startActivityAndCheckNetworkAccess(); + Log.d(TAG, testName + " end #" + i); + } finally { + finishActivity(); + } + } + } + + // TODO: Some of these methods are also used in CTS, so instead of duplicating code, + // create a static library which can be used by both servicestests and cts. + private void setBatterySaverMode(boolean enabled) throws Exception { + if (enabled) { + turnBatteryOff(); + executeCommand("settings put global low_power 1"); + } else { + executeCommand("settings put global low_power 0"); + turnBatteryOn(); + } + final String result = executeCommand("settings get global low_power"); + assertEquals(enabled ? "1" : "0", result); + } + + private void setDataSaverMode(boolean enabled) throws Exception { + executeCommand("cmd netpolicy set restrict-background " + enabled); + final String output = executeCommand("cmd netpolicy get restrict-background"); + final String expectedSuffix = enabled ? "enabled" : "disabled"; + assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'", + output.endsWith(expectedSuffix)); + } + + private void setDozeMode(boolean enabled) throws Exception { + if (enabled) { + turnBatteryOff(); + turnScreenOff(); + executeCommand("dumpsys deviceidle force-idle deep"); + } else { + turnScreenOn(); + turnBatteryOn(); + executeCommand("dumpsys deviceidle unforce"); + } + assertDelayedCommandResult("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE", + 5 /* maxTries */, 500 /* napTimeMs */); + } + + private void setAppIdle(boolean enabled) throws Exception { + executeCommand("am set-inactive " + TEST_PKG + " " + enabled); + assertDelayedCommandResult("am get-inactive " + TEST_PKG, "Idle=" + enabled, + 10 /* maxTries */, 2000 /* napTimeMs */); + } + + private void updateRestrictBackgroundBlacklist(boolean add) throws Exception { + if (add) { + executeCommand("cmd netpolicy add restrict-background-blacklist " + mTestPkgUid); + } else { + executeCommand("cmd netpolicy remove restrict-background-blacklist " + mTestPkgUid); + } + assertRestrictBackground("restrict-background-blacklist", mTestPkgUid, add); + } + + private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception { + final int maxTries = 5; + boolean actual = false; + final String expectedUid = Integer.toString(uid); + String uids = ""; + for (int i = 1; i <= maxTries; i++) { + final String output = executeCommand("cmd netpolicy list " + list); + uids = output.split(":")[1]; + for (String candidate : uids.split(" ")) { + actual = candidate.trim().equals(expectedUid); + if (expected == actual) { + return; + } + } + Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected " + + expected + ", got " + actual + "); sleeping 1s before polling again"); + SystemClock.sleep(1000); + } + fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual + + ". Full list: " + uids); + } + + private void turnBatteryOff() throws Exception { + executeCommand("cmd battery unplug"); + } + + private void turnBatteryOn() throws Exception { + executeCommand("cmd battery reset"); + } + + private void turnScreenOff() throws Exception { + executeCommand("input keyevent KEYCODE_SLEEP"); + } + + private void turnScreenOn() throws Exception { + executeCommand("input keyevent KEYCODE_WAKEUP"); + executeCommand("wm dismiss-keyguard"); + } + + private String executeCommand(String cmd) throws IOException { + final String result = mUiDevice.executeShellCommand(cmd).trim(); + Log.d(TAG, String.format("Result for '%s': %s", cmd, result)); + return result; + } + + private void assertDelayedCommandResult(String cmd, String expectedResult, + int maxTries, int napTimeMs) throws IOException { + String result = ""; + for (int i = 1; i <= maxTries; ++i) { + result = executeCommand(cmd); + if (expectedResult.equals(result)) { + return; + } + Log.v(TAG, "Command '" + cmd + "' returned '" + result + " instead of '" + + expectedResult + "' on attempt #" + i + + "; sleeping " + napTimeMs + "ms before trying again"); + SystemClock.sleep(napTimeMs); + } + fail("Command '" + cmd + "' did not return '" + expectedResult + "' after " + + maxTries + " attempts. Last result: '" + result + "'"); + } + + private void startActivityAndCheckNetworkAccess() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final Intent launchIntent = new Intent().setComponent( + new ComponentName(TEST_PKG, TEST_ACTIVITY_CLASS)); + final Bundle extras = new Bundle(); + final String[] errors = new String[] {null}; + extras.putBinder(EXTRA_NETWORK_STATE_OBSERVER, new INetworkStateObserver.Stub() { + @Override + public void onNetworkStateChecked(String resultData) { + errors[0] = checkForAvailability(resultData); + latch.countDown(); + } + }); + launchIntent.putExtras(extras); + mContext.startActivity(launchIntent); + if (latch.await(NETWORK_CHECK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + if (!errors[0].isEmpty()) { + fail("Network not available for test app " + mTestPkgUid); + } + } else { + fail("Timed out waiting for network availability status from test app " + mTestPkgUid); + } + } + + private void finishActivity() { + final Intent finishIntent = new Intent(ACTION_FINISH_ACTIVITY) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mContext.sendBroadcast(finishIntent); + } + + private String checkForAvailability(String resultData) { + if (resultData == null) { + assertNotNull("Network status from app2 is null, Uid: " + mTestPkgUid, resultData); + } + // Network status format is described on MyBroadcastReceiver.checkNetworkStatus() + final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR); + assertEquals("Wrong network status: " + resultData + ", Uid: " + mTestPkgUid, + 5, parts.length); // Sanity check + final NetworkInfo.State state = parts[0].equals("null") + ? null : NetworkInfo.State.valueOf(parts[0]); + final NetworkInfo.DetailedState detailedState = parts[1].equals("null") + ? null : NetworkInfo.DetailedState.valueOf(parts[1]); + final boolean connected = Boolean.valueOf(parts[2]); + final String connectionCheckDetails = parts[3]; + final String networkInfo = parts[4]; + + final StringBuilder errors = new StringBuilder(); + final NetworkInfo.State expectedState = NetworkInfo.State.CONNECTED; + final NetworkInfo.DetailedState expectedDetailedState = NetworkInfo.DetailedState.CONNECTED; + + if (true != connected) { + errors.append(String.format("External site connection failed: expected %s, got %s\n", + true, connected)); + } + if (expectedState != state || expectedDetailedState != detailedState) { + errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n", + expectedState, expectedDetailedState, state, detailedState)); + } + + if (errors.length() > 0) { + errors.append("\tnetworkInfo: " + networkInfo + "\n"); + errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n"); + } + return errors.toString(); + } + + private static void installAppAndAssertInstalled() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final int[] result = {PackageInstaller.STATUS_SUCCESS}; + final BroadcastReceiver installStatusReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String pkgName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME); + if (!TEST_PKG.equals(pkgName)) { + return; + } + result[0] = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + latch.countDown(); + } + }; + mContext.registerReceiver(installStatusReceiver, new IntentFilter(ACTION_INSTALL_COMPLETE)); + try { + installApp(); + if (latch.await(WAIT_FOR_INSTALL_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + if (result[0] != PackageInstaller.STATUS_SUCCESS) { + fail("Couldn't install test app, result: " + + valueToString(PackageInstaller.class, "STATUS_", result[0])); + } + } else { + fail("Timed out waiting for the test app to install"); + } + } finally { + mContext.unregisterReceiver(installStatusReceiver); + } + } + + private static void installApp() throws Exception { + final Uri packageUri = Uri.parse(TEST_APP_URI); + final InputStream in = mContext.getContentResolver().openInputStream(packageUri); + + final PackageInstaller packageInstaller + = mContext.getPackageManager().getPackageInstaller(); + final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_FULL_INSTALL); + params.setAppPackageName(TEST_PKG); + + final int sessionId = packageInstaller.createSession(params); + final PackageInstaller.Session session = packageInstaller.openSession(sessionId); + + OutputStream out = null; + try { + out = session.openWrite(TAG, 0, -1); + final byte[] buffer = new byte[65536]; + int c; + while ((c = in.read(buffer)) != -1) { + out.write(buffer, 0, c); + } + session.fsync(out); + } finally { + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(out); + } + session.commit(createIntentSender(mContext, sessionId)); + } + + private static IntentSender createIntentSender(Context context, int sessionId) { + PendingIntent pendingIntent = PendingIntent.getBroadcast( + context, sessionId, new Intent(ACTION_INSTALL_COMPLETE), 0); + return pendingIntent.getIntentSender(); + } +} \ No newline at end of file diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.mk b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk new file mode 100644 index 0000000000000..02afe83efb994 --- /dev/null +++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.mk @@ -0,0 +1,30 @@ +# 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_SDK_VERSION := current + +LOCAL_STATIC_JAVA_LIBRARIES := servicestests-aidl +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := ConnTestApp +LOCAL_CERTIFICATE := platform +LOCAL_DEX_PREOPT := false +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) \ No newline at end of file diff --git a/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml new file mode 100644 index 0000000000000..0da3562c9afa6 --- /dev/null +++ b/services/tests/servicestests/test-apps/ConnTestApp/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java new file mode 100644 index 0000000000000..11ebfca67ad4d --- /dev/null +++ b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java @@ -0,0 +1,170 @@ +/* + * 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.servicestests.apps.conntestapp; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +import com.android.servicestests.aidl.INetworkStateObserver; + +import java.net.HttpURLConnection; +import java.net.URL; + +public class ConnTestActivity extends Activity { + private static final String TAG = ConnTestActivity.class.getSimpleName(); + + private static final String TEST_PKG = ConnTestActivity.class.getPackage().getName(); + private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH"; + private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer"; + + private static final int NETWORK_TIMEOUT_MS = 5 * 1000; + + private static final String NETWORK_STATUS_TEMPLATE = "%s|%s|%s|%s|%s"; + + private BroadcastReceiver finishCommandReceiver = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + notifyNetworkStateObserver(); + + finishCommandReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + ConnTestActivity.this.finish(); + } + }; + registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY)); + } + + @Override + public void onStop() { + if (finishCommandReceiver != null) { + unregisterReceiver(finishCommandReceiver); + } + super.onStop(); + } + + private void notifyNetworkStateObserver() { + if (getIntent() == null) { + return; + } + + final Bundle extras = getIntent().getExtras(); + if (extras == null) { + return; + } + final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface( + extras.getBinder(EXTRA_NETWORK_STATE_OBSERVER)); + if (observer != null) { + AsyncTask.execute(() -> { + try { + observer.onNetworkStateChecked(checkNetworkStatus(ConnTestActivity.this)); + } catch (RemoteException e) { + Log.e(TAG, "Error occured while notifying the observer: " + e); + } + }); + } + } + + /** + * Checks whether the network is available and return a string which can then be send as a + * result data for the ordered broadcast. + * + *

+ * The string has the following format: + * + *


+     * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo
+     * 
+ * + *

Where: + * + *

    + *
  • {@code NetinfoState}: enum value of {@link NetworkInfo.State}. + *
  • {@code NetinfoDetailedState}: enum value of {@link NetworkInfo.DetailedState}. + *
  • {@code RealConnectionCheck}: boolean value of a real connection check (i.e., an attempt + * to access an external website. + *
  • {@code RealConnectionCheckDetails}: if HTTP output core or exception string of the real + * connection attempt + *
  • {@code Netinfo}: string representation of the {@link NetworkInfo}. + *
+ * + * For example, if the connection was established fine, the result would be something like: + *


+     * CONNECTED|CONNECTED|true|200|[type: WIFI[], state: CONNECTED/CONNECTED, reason: ...]
+     * 
+ */ + private String checkNetworkStatus(Context context) { + final ConnectivityManager cm = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + final String address = "http://example.com"; + final NetworkInfo networkInfo = cm.getActiveNetworkInfo(); + Log.d(TAG, "Running checkNetworkStatus() on thread " + + Thread.currentThread().getName() + " for UID " + getUid(context) + + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address); + boolean checkStatus = false; + String checkDetails = "N/A"; + try { + final URL url = new URL(address); + final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setReadTimeout(NETWORK_TIMEOUT_MS); + conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + conn.connect(); + final int response = conn.getResponseCode(); + checkStatus = true; + checkDetails = "HTTP response for " + address + ": " + response; + } catch (Exception e) { + checkStatus = false; + checkDetails = "Exception getting " + address + ": " + e; + } + Log.d(TAG, checkDetails); + final String state, detailedState; + if (networkInfo != null) { + state = networkInfo.getState().name(); + detailedState = networkInfo.getDetailedState().name(); + } else { + state = detailedState = "null"; + } + final String status = String.format(NETWORK_STATUS_TEMPLATE, state, detailedState, + Boolean.valueOf(checkStatus), checkDetails, networkInfo); + Log.d(TAG, "Offering " + status); + return status; + } + + private int getUid(Context context) { + final String packageName = context.getPackageName(); + try { + return context.getPackageManager().getPackageUid(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalStateException("Could not get UID for " + packageName, e); + } + } +} \ No newline at end of file