1 /*
2  * Copyright (C) 2016 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 
17 package com.android.cts.verifier.notifications;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.PackageManager;
24 import android.os.Bundle;
25 import android.util.Log;
26 import android.view.View;
27 import android.view.ViewGroup;
28 
29 import com.android.cts.verifier.R;
30 
31 import java.security.SecureRandom;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.concurrent.atomic.AtomicReference;
35 
36 /**
37  * Test to make sure, when an inline reply happens, the shortcut manager rate-limiting must
38  * be reset.
39  *
40  * We use the "BOT" apk here, because rate-limiting will be reset when an app shows an activity
41  * too -- so as long as this (or any) test activity is shown, CTS verifier won't be rate-limited.
42  */
43 public class ShortcutThrottlingResetActivity extends InteractiveVerifierActivity {
44     private static final String TAG = "ShortcutThrottlingReset";
45 
46     private static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
47 
48     private static final String ACTION_RESET_SETUP_NOTIFICATION =
49             "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION";
50     private static final String ACTION_OPEN_NOTIFICATION_BOT =
51             "com.android.cts.robot.OPEN_NOTIFICATION_BOT";
52 
53     private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE";
54     private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE";
55     private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION";
56     private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR";
57 
58     private static final String SUCCESS = "**SUCCESS**";
59 
60     private static final int REQUEST_CODE_OPEN_NOTIFICATION_BOT = 1;
61 
62     private String mReplyAction;
63 
64     private final AtomicReference<Intent> mReplyIntent = new AtomicReference<>(null);
65 
66     @Override
getTitleResource()67     protected int getTitleResource() {
68         return R.string.shortcut_reset_test;
69     }
70 
71     @Override
getInstructionsResource()72     protected int getInstructionsResource() {
73         return R.string.shortcut_reset_info;
74     }
75 
76     @Override
onCreate(Bundle savedState)77     protected void onCreate(Bundle savedState) {
78         super.onCreate(savedState);
79 
80         // Generate an unique reply action and register the reply receiver.
81         mReplyAction = "reply_" + new SecureRandom().nextLong();
82         final IntentFilter replyFilter = new IntentFilter(mReplyAction);
83         registerReceiver(mReplyReceiver, replyFilter, Context.RECEIVER_EXPORTED);
84     }
85 
86     @Override
onDestroy()87     protected void onDestroy() {
88         unregisterReceiver(mReplyReceiver);
89         super.onDestroy();
90     }
91 
92     @Override
createTestItems()93     protected List<InteractiveTestCase> createTestItems() {
94         List<InteractiveTestCase> tests = new ArrayList<>();
95         tests.add(new CheckForBot());
96         tests.add(new SetupNotification());
97         tests.add(new WaitForTestReply());
98         tests.add(new CheckResult());
99         return tests;
100     }
101 
102 
103     private final BroadcastReceiver mReplyReceiver = new BroadcastReceiver() {
104         @Override
105         public void onReceive(Context context, Intent intent) {
106             Log.i(TAG, "Received reply from robot helper: " + intent);
107             mReplyIntent.set(intent);
108         }
109     };
110 
111 
112     /** Make sure the helper package is installed. */
113     protected class CheckForBot extends InteractiveTestCase {
114         @Override
inflate(ViewGroup parent)115         protected View inflate(ViewGroup parent) {
116             return createAutoItem(parent, R.string.shortcut_reset_bot);
117         }
118 
119         @Override
test()120         protected void test() {
121             PackageManager pm = mContext.getPackageManager();
122             try {
123                 pm.getPackageInfo(NOTIFICATION_BOT_PACKAGE, 0);
124                 status = PASS;
125                 startActivityForResult(new Intent(ACTION_OPEN_NOTIFICATION_BOT),
126                         REQUEST_CODE_OPEN_NOTIFICATION_BOT);
127             } catch (PackageManager.NameNotFoundException e) {
128                 status = FAIL;
129                 logFail("You must install the CTS Robot helper, aka " + NOTIFICATION_BOT_PACKAGE);
130                 next();
131             }
132         }
133     }
134 
135     @Override
onActivityResult(int requestCode, int resultCode, Intent data)136     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
137         super.onActivityResult(requestCode, resultCode, data);
138         if (requestCode == REQUEST_CODE_OPEN_NOTIFICATION_BOT) {
139             next();
140         }
141     }
142 
143     /**
144      * Request the bot apk to show the notification.
145      */
146     protected class SetupNotification extends InteractiveTestCase {
147         @Override
inflate(ViewGroup parent)148         protected View inflate(ViewGroup parent) {
149             return createAutoItem(parent, R.string.shortcut_reset_start);
150         }
151 
152         @Override
test()153         protected void test() {
154             final Intent intent = new Intent(ACTION_RESET_SETUP_NOTIFICATION);
155             intent.setPackage(NOTIFICATION_BOT_PACKAGE);
156 
157             intent.putExtra(EXTRA_NOTIFICATION_TITLE, getResources().getString(getTitleResource()));
158 
159             intent.putExtra(EXTRA_RESET_REPLY_PACKAGE, getPackageName());
160             intent.putExtra(EXTRA_RESET_REPLY_ACTION, mReplyAction);
161 
162             intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
163             sendBroadcast(intent);
164             status = PASS;
165             next();
166         }
167     }
168 
169     /**
170      * Let the human tester do an inline reply, and wait for the reply broadcast from the bot apk.
171      */
172     protected class WaitForTestReply extends InteractiveTestCase {
173         @Override
inflate(ViewGroup parent)174         protected View inflate(ViewGroup parent) {
175             return createAutoItem(parent, R.string.shortcut_reset_prompt_inline_reply);
176         }
177 
178         @Override
test()179         protected void test() {
180             final Intent replyIntent = mReplyIntent.get();
181             if (replyIntent == null) {
182                 // Reply not received yet.
183                 status = RETEST;
184                 delay();
185                 return;
186             }
187             status = PASS;
188             next();
189         }
190     }
191 
192     /**
193      * Check the reply from the bot apk.
194      */
195     protected class CheckResult extends InteractiveTestCase {
196         @Override
inflate(ViewGroup parent)197         protected View inflate(ViewGroup parent) {
198             return createAutoItem(parent, R.string.shortcut_reset_check_result);
199         }
200 
201         @Override
test()202         protected void test() {
203             final Intent replyIntent = mReplyIntent.get();
204             if (replyIntent == null) {
205                 logFail("Internal error, replyIntent shouldn't be null here.");
206                 status = FAIL;
207                 return;
208             }
209             final String error = replyIntent.getStringExtra(EXTRA_RESET_REPLY_ERROR);
210             if (SUCCESS.equals(error)) {
211                 status = PASS;
212                 next();
213                 return;
214             }
215             logFail("Test failed. Error message=" + error);
216             status = FAIL;
217             next();
218         }
219     }
220 }
221