Refactored how data is shared between test apps.
When running the device-site tests, it's necessary to share state
between a second app and the main test app. Currently that's achieved
through a shared preference file that is accessed by both apps (since
they use a shared id), but this approach will not work on power save
mode tests (because the test app will be in foreground and the
background restrictions won't be applied in the second app).
This change refactors the data sharing mechanism by:
- Using an ordered broadcast that is sent from the test app to the
secondary app.
- Checking for the network status in the secondary app.
- Moving the test logic to the client-side tests.
BUG: 27127112
Change-Id: I44987701b908b329fdf40e3a7a97e9f30cfadecb
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
index d69bf56..fa4cb43 100644
--- a/tests/cts/hostside/app2/AndroidManifest.xml
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -16,20 +16,28 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.net.hostside.app2"
- android:sharedUserId="com.android.cts.net.hostside.apps" >
+ package="com.android.cts.net.hostside.app2" >
- <!-- This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
- them in a shared preferences which is then read by the test app.
- It defines 2 listeners, one in the manifest and another dynamically registered by
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <!--
+ This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
+ them in a shared preferences which is then read by the test app. These broadcasts are
+ handled by 2 listeners, one defined the manifest and another dynamically registered by
a service.
+
+ The manifest-defined listener also handles ordered broadcasts used to share data with the
+ test app.
-->
<application>
- <service android:name=".MyService" />
+ <service android:name=".MyService" android:exported="true"/>
<receiver android:name=".MyBroadcastReceiver" >
<intent-filter>
<action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED" />
+ <action android:name="com.android.cts.net.hostside.app2.action.GET_COUNTERS" />
+ <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
</intent-filter>
</receiver>
</application>
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
index 91caeda..3be4261 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -18,6 +18,18 @@
public final class Common {
static final String TAG = "CtsNetApp2";
+
+ // Constants below must match values defined on app's ConnectivityManagerTest.java
static final String MANIFEST_RECEIVER = "ManifestReceiver";
static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+ static final String ACTION_GET_COUNTERS =
+ "com.android.cts.net.hostside.app2.action.GET_COUNTERS";
+ static final String ACTION_CHECK_NETWORK =
+ "com.android.cts.net.hostside.app2.action.CHECK_NETWORK";
+ static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
+ static final String EXTRA_RECEIVER_NAME =
+ "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
+ static final char RESULT_SEPARATOR = ';';
+ static final String STATUS_NETWORK_UNAVAILABLE_PREFIX = "NetworkUnavailable:";
+ static final String STATUS_NETWORK_AVAILABLE_PREFIX = "NetworkAvailable:";
}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
index 0cbf360..65c7b60 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -13,21 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.cts.net.hostside.app2;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static com.android.cts.net.hostside.app2.Common.ACTION_CHECK_NETWORK;
+import static com.android.cts.net.hostside.app2.Common.ACTION_GET_COUNTERS;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_RECEIVER_NAME;
import static com.android.cts.net.hostside.app2.Common.MANIFEST_RECEIVER;
+import static com.android.cts.net.hostside.app2.Common.RESULT_SEPARATOR;
+import static com.android.cts.net.hostside.app2.Common.STATUS_NETWORK_AVAILABLE_PREFIX;
+import static com.android.cts.net.hostside.app2.Common.STATUS_NETWORK_UNAVAILABLE_PREFIX;
import static com.android.cts.net.hostside.app2.Common.TAG;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.util.Log;
/**
- * Receiver that stores received broadcasts in a shared preference.
+ * Receiver used to:
+ * <ol>
+ * <li>Stored received RESTRICT_BACKGROUND_CHANGED broadcasts in a shared preference.
+ * <li>Returned the number of RESTRICT_BACKGROUND_CHANGED broadcasts in an ordered broadcast.
+ * </ol>
*/
public class MyBroadcastReceiver extends BroadcastReceiver {
+ private static final int NETWORK_TIMEOUT_MS = 15 * 1000;
+
private final String mName;
public MyBroadcastReceiver() {
@@ -37,15 +60,106 @@
MyBroadcastReceiver(String name) {
Log.d(TAG, "Constructing MyBroadcastReceiver named " + name);
mName = name;
- }
+ }
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive() for " + mName + ": " + intent);
+ final String action = intent.getAction();
+ switch (action) {
+ case ACTION_RESTRICT_BACKGROUND_CHANGED:
+ increaseCounter(context, action);
+ break;
+ case ACTION_GET_COUNTERS:
+ setResultDataFromCounter(context, intent);
+ break;
+ case ACTION_CHECK_NETWORK:
+ checkNetwork(context, intent);
+ break;
+ default:
+ Log.e(TAG, "received unexpected action: " + action);
+ }
+ }
+
+ private void increaseCounter(Context context, String action) {
final SharedPreferences prefs = context.getSharedPreferences(mName, Context.MODE_PRIVATE);
- final String pref = intent.getAction();
- final int value = prefs.getInt(pref, 0) + 1;
- Log.d(TAG, "Setting " + pref + " = " + value);
- prefs.edit().putInt(pref, value).apply();
+ final int value = prefs.getInt(action, 0) + 1;
+ Log.d(TAG, "increaseCounter('" + action + "'): setting '" + mName + "' to " + value);
+ prefs.edit().putInt(action, value).apply();
+ }
+
+ private int getCounter(Context context, String action, String receiverName) {
+ final SharedPreferences prefs = context.getSharedPreferences(receiverName,
+ Context.MODE_PRIVATE);
+ final int value = prefs.getInt(action, 0);
+ Log.d(TAG, "getCounter('" + action + "', '" + receiverName + "'): " + value);
+ return value;
+ }
+
+ private void checkNetwork(final Context context, Intent intent) {
+ final ConnectivityManager cm = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ final StringBuilder data = new StringBuilder();
+ final int apiStatus = cm.getRestrictBackgroundStatus();
+ String netStatus;
+ try {
+ netStatus = checkNetworkStatus(cm);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Timeout checking network status");
+ setResultData(null);
+ return;
+ }
+ data.append(apiStatus).append(RESULT_SEPARATOR).append(netStatus);
+ Log.d(TAG, "checkNetwork: returning " + data);
+ setResultData(data.toString());
+ }
+
+ private String checkNetworkStatus(final ConnectivityManager cm) throws InterruptedException {
+ final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ // TODO: connect to a hostside server instead
+ final String address = "http://example.com";
+ final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+ Log.d(TAG, "Running checkNetworkStatus() on thread "
+ + Thread.currentThread().getName()
+ + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
+ String prefix = STATUS_NETWORK_AVAILABLE_PREFIX;
+ try {
+ final URL url = new URL(address);
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+ conn.setConnectTimeout(NETWORK_TIMEOUT_MS);
+ conn.setRequestMethod("GET");
+ conn.setDoInput(true);
+ conn.connect();
+ final int response = conn.getResponseCode();
+ Log.d(TAG, "HTTP response for " + address + ": " + response);
+ } catch (Exception e) {
+ Log.d(TAG, "Exception getting " + address + ": " + e);
+ prefix = STATUS_NETWORK_UNAVAILABLE_PREFIX + "Exception " + e + ":";
+ }
+ result.offer(prefix + networkInfo);
+ }
+ }, mName).start();
+ return result.poll(NETWORK_TIMEOUT_MS * 2, TimeUnit.MILLISECONDS);
+ }
+
+ private void setResultDataFromCounter(Context context, Intent intent) {
+ final String action = intent.getStringExtra(EXTRA_ACTION);
+ if (action == null) {
+ Log.e(TAG, "Missing extra '" + EXTRA_ACTION + "' on " + intent);
+ return;
+ }
+ final String receiverName = intent.getStringExtra(EXTRA_RECEIVER_NAME);
+ if (receiverName == null) {
+ Log.e(TAG, "Missing extra '" + EXTRA_RECEIVER_NAME + "' on " + intent);
+ return;
+ }
+ final int counter = getCounter(context, action, receiverName);
+ setResultData(String.valueOf(counter));
}
}