Cover more PendingIntent whitelist scenarios.
BUG: 29480440 Change-Id: I961b765f40135efc06fbb3e5a4a94e8e333453da
This commit is contained in:
@@ -132,16 +132,29 @@ abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetwor
|
||||
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
|
||||
|
||||
@@ -69,6 +69,18 @@ abstract class AbstractRestrictBackgroundNetworkTestCase extends Instrumentation
|
||||
"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;
|
||||
@@ -735,10 +747,12 @@ abstract class AbstractRestrictBackgroundNetworkTestCase extends Instrumentation
|
||||
+ "--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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@ public class MyNotificationListenerService extends NotificationListenerService {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,16 @@ public final class Common {
|
||||
"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();
|
||||
|
||||
@@ -25,8 +25,16 @@ import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
|
||||
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;
|
||||
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;
|
||||
@@ -230,21 +239,66 @@ public class MyBroadcastReceiver extends BroadcastReceiver {
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user