1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.cts.robot; 17 18 import android.app.Notification; 19 import android.app.Notification.Action; 20 import android.app.NotificationChannel; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.app.RemoteInput; 24 import android.content.BroadcastReceiver; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.ShortcutInfo; 29 import android.content.pm.ShortcutManager; 30 import android.os.SystemClock; 31 import android.util.Log; 32 33 import java.util.ArrayList; 34 import java.util.List; 35 36 37 public class NotificationBot extends BroadcastReceiver { 38 private static final String TAG = "NotificationBot"; 39 private static final String NOTIFICATION_CHANNEL_ID = TAG + "_high"; 40 private static final String EXTRA_ID = "ID"; 41 private static final String EXTRA_NOTIFICATION = "NOTIFICATION"; 42 private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST"; 43 private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL"; 44 private static final String ACTION_RESET_SETUP_NOTIFICATION = 45 "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION"; 46 47 private static final String ACTION_INLINE_REPLY = 48 "com.android.cts.robot.ACTION_INLINE_REPLY"; 49 50 private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE"; 51 private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION"; 52 private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE"; 53 54 private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR"; 55 56 private static final String EXTRA_RESET_REQUEST_INTENT = "EXTRA_RESET_REQUEST_INTENT"; 57 58 private static final String SUCCESS = "**SUCCESS**"; 59 60 @Override onReceive(Context context, Intent intent)61 public void onReceive(Context context, Intent intent) { 62 Log.i(TAG, "received intent: " + intent); 63 if (ACTION_POST.equals(intent.getAction())) { 64 Log.i(TAG, ACTION_POST); 65 if (!intent.hasExtra(EXTRA_NOTIFICATION) || !intent.hasExtra(EXTRA_ID)) { 66 Log.e(TAG, "received post action with missing content"); 67 return; 68 } 69 int id = intent.getIntExtra(EXTRA_ID, -1); 70 Log.i(TAG, "id: " + id); 71 Notification n = (Notification) intent.getParcelableExtra(EXTRA_NOTIFICATION); 72 Log.i(TAG, "n: " + n); 73 NotificationManager noMa = 74 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 75 noMa.notify(id, n); 76 77 } else if (ACTION_CANCEL.equals(intent.getAction())) { 78 Log.i(TAG, ACTION_CANCEL); 79 int id = intent.getIntExtra(EXTRA_ID, -1); 80 Log.i(TAG, "id: " + id); 81 if (id < 0) { 82 Log.e(TAG, "received cancel action with no ID"); 83 return; 84 } 85 NotificationManager noMa = 86 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 87 noMa.cancel(id); 88 89 } else if (ACTION_RESET_SETUP_NOTIFICATION.equals(intent.getAction())) { 90 testShortcutResetSetupNotification(context, intent); 91 92 } else if (ACTION_INLINE_REPLY.equals(intent.getAction())) { 93 testShortcutResetInlineReplyReceived(context, intent); 94 95 } else { 96 Log.i(TAG, "received unexpected action: " + intent.getAction()); 97 } 98 } 99 100 /** 101 * Test start request from CTS verifier. Show a notification with inline reply, which will 102 * trigger {@link #testShortcutResetInlineReplyReceived}. 103 */ testShortcutResetSetupNotification(Context context, Intent intent)104 private static void testShortcutResetSetupNotification(Context context, Intent intent) { 105 final NotificationManager nm = context.getSystemService(NotificationManager.class); 106 nm.cancelAll(); 107 108 final ShortcutManager sm = context.getSystemService(ShortcutManager.class); 109 110 final List<ShortcutInfo> EMPTY_LIST = new ArrayList<>(); 111 112 long timeout = SystemClock.elapsedRealtime() + 10 * 1000; 113 114 // First, make sure this package is throttled. 115 while (!sm.isRateLimitingActive()) { 116 sm.setDynamicShortcuts(EMPTY_LIST); 117 try { 118 Thread.sleep(0); 119 } catch (InterruptedException e) { 120 } 121 if (SystemClock.elapsedRealtime() >= timeout) { 122 sendShortcutResetReply(context, intent, 123 "ShortcutMager rate-limiting not activated."); 124 return; 125 } 126 } 127 128 // Show a notification with inline reply. 129 final PendingIntent receiverIntent = 130 PendingIntent.getBroadcast(context, 0, 131 new Intent(ACTION_INLINE_REPLY) 132 .setComponent(new ComponentName(context, NotificationBot.class)) 133 .putExtra(EXTRA_RESET_REQUEST_INTENT, intent), 134 PendingIntent.FLAG_UPDATE_CURRENT); 135 final RemoteInput ri = new RemoteInput.Builder("result") 136 .setLabel("Type something here and press send button").build(); 137 138 NotificationManager notificationManager = 139 context.getSystemService(NotificationManager.class); 140 notificationManager.createNotificationChannel(new NotificationChannel( 141 NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID, 142 NotificationManager.IMPORTANCE_HIGH)); 143 final Notification.Builder nb = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID) 144 .setContentTitle(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE)) 145 .setSmallIcon(android.R.drawable.ic_popup_sync) 146 .addAction(new Action.Builder(0, 147 "Type something here and press send button", receiverIntent) 148 .addRemoteInput(ri) 149 .build()); 150 notificationManager.notify(0, nb.build()); 151 } 152 153 /** 154 * Invoked when the inline reply from {@link #testShortcutResetSetupNotification} is performed. 155 * 156 * Check the shortcut manager rate-limiting state, and post the reply to CTS verifier. 157 */ testShortcutResetInlineReplyReceived(Context context, Intent intent)158 private static void testShortcutResetInlineReplyReceived(Context context, Intent intent) { 159 Log.i(TAG, "Inline reply received"); 160 161 final NotificationManager nm = context.getSystemService(NotificationManager.class); 162 nm.cancelAll(); 163 164 // Close notification shade. 165 context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); 166 167 // Check if rate-limiting has been reset. 168 final ShortcutManager sm = context.getSystemService(ShortcutManager.class); 169 170 String error; 171 final boolean success = !sm.isRateLimitingActive(); 172 if (success) { 173 error = SUCCESS; 174 } else { 175 error = "Inline reply received, but ShortcutManager rate-limiting is still active."; 176 } 177 178 // Send back the result. 179 sendShortcutResetReply(context, 180 intent.getParcelableExtra(EXTRA_RESET_REQUEST_INTENT), error); 181 } 182 183 /** 184 * Reply an error message, or {@link #SUCCESS} for success, to CTS verifier for shortcut manager 185 * reset rate-limiting test. 186 187 * @param requestIntent original intent sent from the verifier to 188 * {@link #testShortcutResetSetupNotification}. 189 * @param error error message, or {@link #SUCCESS} if success. 190 */ sendShortcutResetReply(Context context, Intent requestIntent, String error)191 private static void sendShortcutResetReply(Context context, Intent requestIntent, String error) { 192 final Intent replyIntent = new Intent(); 193 replyIntent.setAction(requestIntent.getStringExtra(EXTRA_RESET_REPLY_ACTION)); 194 replyIntent.putExtra(EXTRA_RESET_REPLY_ERROR, error); 195 196 if (error != null) { 197 Log.e(TAG, error); 198 } 199 200 context.sendBroadcast(replyIntent); 201 } 202 } 203