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