[automerger] Merge "Fix expected reverse lookup of Google DNS IP addresses" into nougat-cts-dev am: 3b416dd354 am: d4831efbe0
Change-Id: Ie85fee065ec9aef98036b75bf1b2497d760b8070
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index cc05b04..fc674a7 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -131,16 +131,29 @@
setDozeMode(true);
assertBackgroundNetworkAccess(false);
- sendNotification(42);
- assertBackgroundNetworkAccess(true);
- // Make sure access is disabled after it expires
- SystemClock.sleep(NETWORK_TIMEOUT_MS);
- assertBackgroundNetworkAccess(false);
+ testNotification(4, NOTIFICATION_TYPE_CONTENT);
+ testNotification(8, NOTIFICATION_TYPE_DELETE);
+ testNotification(15, NOTIFICATION_TYPE_FULL_SCREEN);
+ testNotification(16, NOTIFICATION_TYPE_BUNDLE);
+ testNotification(23, NOTIFICATION_TYPE_ACTION);
+ testNotification(42, NOTIFICATION_TYPE_ACTION_BUNDLE);
+ testNotification(108, NOTIFICATION_TYPE_ACTION_REMOTE_INPUT);
} finally {
resetDeviceIdleSettings();
}
}
+ private void testNotification(int id, String type) throws Exception {
+ sendNotification(id, type);
+ assertBackgroundNetworkAccess(true);
+ if (type.equals(NOTIFICATION_TYPE_ACTION)) {
+ // Make sure access is disabled after it expires. Since this check considerably slows
+ // downs the CTS tests, do it just once.
+ SystemClock.sleep(NETWORK_TIMEOUT_MS);
+ assertBackgroundNetworkAccess(false);
+ }
+ }
+
// Must override so it only tests foreground service - once an app goes to foreground, device
// leaves Doze Mode.
@Override
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index ff93d9f..bd93232 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -69,6 +69,18 @@
"com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
private static final String EXTRA_NOTIFICATION_ID =
"com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
+ private static final String EXTRA_NOTIFICATION_TYPE =
+ "com.android.cts.net.hostside.app2.extra.NOTIFICATION_TYPE";
+
+ protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
+ protected static final String NOTIFICATION_TYPE_DELETE = "DELETE";
+ protected static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN";
+ protected static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE";
+ protected static final String NOTIFICATION_TYPE_ACTION = "ACTION";
+ protected static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
+ protected static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
+
+
private static final String NETWORK_STATUS_SEPARATOR = "\\|";
private static final int SECOND_IN_MS = 1000;
static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
@@ -288,60 +300,75 @@
* Asserts whether the active network is available or not.
*/
private void assertNetworkAccess(boolean expectAvailable) throws Exception {
- final Intent intent = new Intent(ACTION_CHECK_NETWORK);
-
final int maxTries = 5;
- String resultData = null;
+ String error = null;
+ int timeoutMs = 500;
+
for (int i = 1; i <= maxTries; i++) {
- resultData = sendOrderedBroadcast(intent);
- assertNotNull("timeout waiting for ordered broadcast", resultData);
+ error = checkNetworkAccess(expectAvailable);
- // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
- final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
- assertEquals("Wrong network status: " + resultData, 5, parts.length); // Sanity check
- final State state = State.valueOf(parts[0]);
- final DetailedState detailedState = DetailedState.valueOf(parts[1]);
- final boolean connected = Boolean.valueOf(parts[2]);
- final String connectionCheckDetails = parts[3];
- final String networkInfo = parts[4];
+ if (error.isEmpty()) return;
- if (expectAvailable) {
- if (!connected) {
- // Since it's establishing a connection to an external site, it could be flaky.
- Log.w(TAG, "Failed to connect to an external site on attempt #" + i +
- " (error: " + connectionCheckDetails + ", NetworkInfo: " + networkInfo
- + "); sleeping " + NETWORK_TIMEOUT_MS + "ms before trying again");
- SystemClock.sleep(NETWORK_TIMEOUT_MS);
- continue;
- }
- if (state != State.CONNECTED) {
- Log.d(TAG, "State (" + state + ") not set to CONNECTED on attempt #" + i
- + "; sleeping 1s before trying again");
- SystemClock.sleep(SECOND_IN_MS);
- } else {
- assertEquals("wrong detailed state for " + networkInfo,
- DetailedState.CONNECTED, detailedState);
- return;
- }
- return;
- } else {
- assertFalse("should not be connected: " + connectionCheckDetails
- + " (network info: " + networkInfo + ")", connected);
- if (state != State.DISCONNECTED) {
- // When the network info state change, it's possible the app still get the
- // previous value, so we need to retry a couple times.
- Log.d(TAG, "State (" + state + ") not set to DISCONNECTED on attempt #" + i
- + "; sleeping 1s before trying again");
- SystemClock.sleep(SECOND_IN_MS);
- } else {
- assertEquals("wrong detailed state for " + networkInfo,
- DetailedState.BLOCKED, detailedState);
- return;
- }
- }
+ // TODO: ideally, it should retry only when it cannot connect to an external site,
+ // or no retry at all! But, currently, the initial change fails almost always on
+ // battery saver tests because the netd changes are made asynchronously.
+ // Once b/27803922 is fixed, this retry mechanism should be revisited.
+
+ Log.w(TAG, "Network status didn't match for expectAvailable=" + expectAvailable
+ + " on attempt #" + i + ": " + error + "\n"
+ + "Sleeping " + timeoutMs + "ms before trying again");
+ SystemClock.sleep(timeoutMs);
+ // Exponential back-off.
+ timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS);
}
fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries
- + " attempts. Last data: " + resultData);
+ + " attempts.\nLast error: " + error);
+ }
+
+ /**
+ * Checks whether the network is available as expected.
+ *
+ * @return error message with the mismatch (or empty if assertion passed).
+ */
+ private String checkNetworkAccess(boolean expectAvailable) throws Exception {
+ String resultData = sendOrderedBroadcast(new Intent(ACTION_CHECK_NETWORK));
+ if (resultData == null) {
+ return "timeout waiting for ordered broadcast";
+ }
+ // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
+ final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
+ assertEquals("Wrong network status: " + resultData, 5, parts.length); // Sanity check
+ final State state = State.valueOf(parts[0]);
+ final DetailedState detailedState = 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 State expectedState;
+ final DetailedState expectedDetailedState;
+ if (expectAvailable) {
+ expectedState = State.CONNECTED;
+ expectedDetailedState = DetailedState.CONNECTED;
+ } else {
+ expectedState = State.DISCONNECTED;
+ expectedDetailedState = DetailedState.BLOCKED;
+ }
+
+ if (expectAvailable != connected) {
+ errors.append(String.format("External site connection failed: expected %s, got %s\n",
+ expectAvailable, 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();
}
protected String executeShellCommand(String command) throws Exception {
@@ -787,10 +814,12 @@
+ "--receiver-foreground --receiver-registered-only");
}
- protected void sendNotification(int notificationId) {
+ protected void sendNotification(int notificationId, String notificationType) {
final Intent intent = new Intent(ACTION_SEND_NOTIFICATION);
intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
- Log.d(TAG, "Sending broadcast: " + intent);
+ intent.putExtra(EXTRA_NOTIFICATION_TYPE, notificationType);
+ Log.d(TAG, "Sending notification broadcast (id=" + notificationId + ", type="
+ + notificationType + ": " + intent);
mContext.sendBroadcast(intent);
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
index b9c3031..0893511 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
@@ -16,7 +16,10 @@
package com.android.cts.net.hostside;
import android.app.Notification;
+import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.app.RemoteInput;
+import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
@@ -40,22 +43,75 @@
Log.v(TAG, "ignoring notification from a different package");
return;
}
+ final PendingIntentSender sender = new PendingIntentSender();
final Notification notification = sbn.getNotification();
- if (notification.actions == null) {
- Log.w(TAG, "ignoring notification without an action");
+ if (notification.contentIntent != null) {
+ sender.send("content", notification.contentIntent);
}
- for (Notification.Action action : notification.actions) {
- Log.i(TAG, "Sending pending intent " + action.actionIntent);
- try {
- action.actionIntent.send();
- } catch (CanceledException e) {
- Log.w(TAG, "Pending Intent canceled");
+ if (notification.deleteIntent != null) {
+ sender.send("delete", notification.deleteIntent);
+ }
+ if (notification.fullScreenIntent != null) {
+ sender.send("full screen", notification.fullScreenIntent);
+ }
+ if (notification.actions != null) {
+ for (Notification.Action action : notification.actions) {
+ sender.send("action", action.actionIntent);
+ sender.send("action extras", action.getExtras());
+ final RemoteInput[] remoteInputs = action.getRemoteInputs();
+ if (remoteInputs != null && remoteInputs.length > 0) {
+ for (RemoteInput remoteInput : remoteInputs) {
+ sender.send("remote input extras", remoteInput.getExtras());
+ }
+ }
}
}
+ sender.send("notification extras", notification.extras);
}
static String getId() {
return String.format("%s/%s", MyNotificationListenerService.class.getPackage().getName(),
MyNotificationListenerService.class.getName());
}
+
+ private static final class PendingIntentSender {
+ private PendingIntent mSentIntent = null;
+ private String mReason = null;
+
+ private void send(String reason, PendingIntent pendingIntent) {
+ if (pendingIntent == null) {
+ // Could happen on action that only has extras
+ Log.v(TAG, "Not sending null pending intent for " + reason);
+ return;
+ }
+ if (mSentIntent != null || mReason != null) {
+ // Sanity check: make sure test case set up just one pending intent in the
+ // notification, otherwise it could pass because another pending intent caused the
+ // whitelisting.
+ throw new IllegalStateException("Already sent a PendingIntent (" + mSentIntent
+ + ") for reason '" + mReason + "' when requested another for '" + reason
+ + "' (" + pendingIntent + ")");
+ }
+ Log.i(TAG, "Sending pending intent for " + reason + ":" + pendingIntent);
+ try {
+ pendingIntent.send();
+ mSentIntent = pendingIntent;
+ mReason = reason;
+ } catch (CanceledException e) {
+ Log.w(TAG, "Pending intent " + pendingIntent + " canceled");
+ }
+ }
+
+ private void send(String reason, Bundle extras) {
+ if (extras != null) {
+ for (String key : extras.keySet()) {
+ Object value = extras.get(key);
+ if (value instanceof PendingIntent) {
+ send(reason + " with key '" + key + "'", (PendingIntent) value);
+ }
+ }
+ }
+ }
+
+ }
}
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 f02f651..8806e3b 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
@@ -43,6 +43,16 @@
"com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
static final String EXTRA_NOTIFICATION_ID =
"com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
+ static final String EXTRA_NOTIFICATION_TYPE =
+ "com.android.cts.net.hostside.app2.extra.NOTIFICATION_TYPE";
+
+ static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
+ static final String NOTIFICATION_TYPE_DELETE = "DELETE";
+ static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN";
+ static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE";
+ static final String NOTIFICATION_TYPE_ACTION = "ACTION";
+ static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
+ static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
static int getUid(Context context) {
final String packageName = context.getPackageName();
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 60e5de1..6d01b15 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
@@ -25,8 +25,16 @@
import static com.android.cts.net.hostside.app2.Common.ACTION_SEND_NOTIFICATION;
import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_ID;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_TYPE;
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.NOTIFICATION_TYPE_ACTION;
+import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
+import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_REMOTE_INPUT;
+import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_BUNDLE;
+import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_CONTENT;
+import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_DELETE;
+import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_FULL_SCREEN;
import static com.android.cts.net.hostside.app2.Common.TAG;
import static com.android.cts.net.hostside.app2.Common.getUid;
@@ -34,6 +42,7 @@
import android.app.Notification.Action;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.RemoteInput;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -57,7 +66,7 @@
*/
public class MyBroadcastReceiver extends BroadcastReceiver {
- private static final int NETWORK_TIMEOUT_MS = 15 * 1000;
+ private static final int NETWORK_TIMEOUT_MS = 5 * 1000;
private final String mName;
@@ -230,21 +239,66 @@
*/
private void sendNotification(Context context, Intent intent) {
final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
+ final String notificationType = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
+ Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType
+ + ", intent=" + intent);
final Intent serviceIntent = new Intent(context, MyService.class);
- final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent, 0);
- final Bundle badBundle = new Bundle();
- badBundle.putCharSequence("parcelable", "I am not");
- final Action action = new Action.Builder(
- R.drawable.ic_notification, "ACTION", pendingIntent)
- .addExtras(badBundle)
- .build();
+ final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent,
+ notificationId);
+ final Bundle bundle = new Bundle();
+ bundle.putCharSequence("parcelable", "I am not");
- final Notification notification = new Notification.Builder(context)
- .setSmallIcon(R.drawable.ic_notification)
- .setContentTitle("Light, Cameras...")
- .setContentIntent(pendingIntent)
- .addAction(action)
- .build();
+ final Notification.Builder builder = new Notification.Builder(context)
+ .setSmallIcon(R.drawable.ic_notification);
+
+ Action action = null;
+ switch (notificationType) {
+ case NOTIFICATION_TYPE_CONTENT:
+ builder
+ .setContentTitle("Light, Cameras...")
+ .setContentIntent(pendingIntent);
+ break;
+ case NOTIFICATION_TYPE_DELETE:
+ builder.setDeleteIntent(pendingIntent);
+ break;
+ case NOTIFICATION_TYPE_FULL_SCREEN:
+ builder.setFullScreenIntent(pendingIntent, true);
+ break;
+ case NOTIFICATION_TYPE_BUNDLE:
+ bundle.putParcelable("Magnum P.I. (Pending Intent)", pendingIntent);
+ builder.setExtras(bundle);
+ break;
+ case NOTIFICATION_TYPE_ACTION:
+ action = new Action.Builder(
+ R.drawable.ic_notification, "ACTION", pendingIntent)
+ .build();
+ builder.addAction(action);
+ break;
+ case NOTIFICATION_TYPE_ACTION_BUNDLE:
+ bundle.putParcelable("Magnum A.P.I. (Action Pending Intent)", pendingIntent);
+ action = new Action.Builder(
+ R.drawable.ic_notification, "ACTION WITH BUNDLE", null)
+ .addExtras(bundle)
+ .build();
+ builder.addAction(action);
+ break;
+ case NOTIFICATION_TYPE_ACTION_REMOTE_INPUT:
+ bundle.putParcelable("Magnum R.I. (Remote Input)", null);
+ final RemoteInput remoteInput = new RemoteInput.Builder("RI")
+ .addExtras(bundle)
+ .build();
+ action = new Action.Builder(
+ R.drawable.ic_notification, "ACTION WITH REMOTE INPUT", pendingIntent)
+ .addRemoteInput(remoteInput)
+ .build();
+ builder.addAction(action);
+ break;
+ default:
+ Log.e(TAG, "Unknown notification type: " + notificationType);
+ return;
+ }
+
+ final Notification notification = builder.build();
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(notificationId, notification);
}