1 /*
2  * Copyright (C) 2022 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.server.notification;
18 
19 import static junit.framework.Assert.assertEquals;
20 import static junit.framework.Assert.assertFalse;
21 import static junit.framework.Assert.assertTrue;
22 
23 import android.content.Context;
24 import android.content.Intent;
25 import android.provider.Settings;
26 import android.testing.AndroidTestingRunner;
27 
28 import androidx.test.filters.SmallTest;
29 
30 import com.android.server.UiServiceTestCase;
31 
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 
36 @RunWith(AndroidTestingRunner.class)
37 @SmallTest
38 public class ReviewNotificationPermissionsReceiverTest extends UiServiceTestCase {
39 
40     // Simple mock class that just overrides the reschedule and cancel behavior so that it's easy
41     // to tell whether the receiver has sent requests to either reschedule or cancel the
42     // notification (or both).
43     private class MockReviewNotificationPermissionsReceiver
44             extends ReviewNotificationPermissionsReceiver {
45         boolean mCanceled = false;
46         boolean mRescheduled = false;
47 
48         @Override
cancelNotification(Context context)49         protected void cancelNotification(Context context) {
50             mCanceled = true;
51         }
52 
53         @Override
rescheduleNotification(Context context)54         protected void rescheduleNotification(Context context) {
55             mRescheduled = true;
56         }
57     }
58 
59     private MockReviewNotificationPermissionsReceiver mReceiver;
60     private Intent mIntent;
61 
62     @Before
setUp()63     public void setUp() {
64         mReceiver = new MockReviewNotificationPermissionsReceiver();
65         mIntent = new Intent();  // actions will be set in test cases
66     }
67 
68     @Test
testReceive_remindMeLater_firstTime()69     public void testReceive_remindMeLater_firstTime() {
70         // Test what happens when we receive a "remind me later" intent coming from
71         // a previously-not-interacted notification
72         Settings.Global.putInt(mContext.getContentResolver(),
73                 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
74                 NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW);
75 
76         // set up Intent action
77         mIntent.setAction(NotificationManagerService.REVIEW_NOTIF_ACTION_REMIND);
78 
79         // Upon receipt of the intent, the following things should happen:
80         //   - notification rescheduled
81         //   - notification explicitly canceled
82         //   - settings state updated to indicate user has interacted
83         mReceiver.onReceive(mContext, mIntent);
84         assertTrue(mReceiver.mRescheduled);
85         assertTrue(mReceiver.mCanceled);
86         assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_USER_INTERACTED,
87                 Settings.Global.getInt(mContext.getContentResolver(),
88                         Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
89                         NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
90     }
91 
92     @Test
testReceive_remindMeLater_laterTimes()93     public void testReceive_remindMeLater_laterTimes() {
94         // Test what happens when we receive a "remind me later" intent coming from
95         // a previously-interacted notification that has been rescheduled
96         Settings.Global.putInt(mContext.getContentResolver(),
97                 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
98                 NotificationManagerService.REVIEW_NOTIF_STATE_RESHOWN);
99 
100         // set up Intent action
101         mIntent.setAction(NotificationManagerService.REVIEW_NOTIF_ACTION_REMIND);
102 
103         // Upon receipt of the intent, the following things should still happen
104         // regardless of the fact that the user has interacted before:
105         //   - notification rescheduled
106         //   - notification explicitly canceled
107         //   - settings state still indicate user has interacted
108         mReceiver.onReceive(mContext, mIntent);
109         assertTrue(mReceiver.mRescheduled);
110         assertTrue(mReceiver.mCanceled);
111         assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_USER_INTERACTED,
112                 Settings.Global.getInt(mContext.getContentResolver(),
113                         Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
114                         NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
115     }
116 
117     @Test
testReceive_dismiss()118     public void testReceive_dismiss() {
119         // Test that dismissing the notification does *not* reschedule the notification,
120         // does cancel it, and writes that it has been dismissed to settings
121         Settings.Global.putInt(mContext.getContentResolver(),
122                 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
123                 NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW);
124 
125         // set up Intent action
126         mIntent.setAction(NotificationManagerService.REVIEW_NOTIF_ACTION_DISMISS);
127 
128         // send intent, watch what happens
129         mReceiver.onReceive(mContext, mIntent);
130         assertFalse(mReceiver.mRescheduled);
131         assertTrue(mReceiver.mCanceled);
132         assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_DISMISSED,
133                 Settings.Global.getInt(mContext.getContentResolver(),
134                         Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
135                         NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
136     }
137 
138     @Test
testReceive_notificationCanceled_firstSwipe()139     public void testReceive_notificationCanceled_firstSwipe() {
140         // Test the basic swipe away case: the first time the user swipes the notification
141         // away, it will not have been interacted with yet, so make sure it's rescheduled
142         Settings.Global.putInt(mContext.getContentResolver(),
143                 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
144                 NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW);
145 
146         // set up Intent action, would be called from notification's delete intent
147         mIntent.setAction(NotificationManagerService.REVIEW_NOTIF_ACTION_CANCELED);
148 
149         // send intent, make sure it gets:
150         //   - rescheduled
151         //   - not explicitly canceled, the notification was already canceled
152         //   - noted that it's been interacted with
153         mReceiver.onReceive(mContext, mIntent);
154         assertTrue(mReceiver.mRescheduled);
155         assertFalse(mReceiver.mCanceled);
156         assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_USER_INTERACTED,
157                 Settings.Global.getInt(mContext.getContentResolver(),
158                         Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
159                         NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
160     }
161 
162     @Test
testReceive_notificationCanceled_secondSwipe()163     public void testReceive_notificationCanceled_secondSwipe() {
164         // Test the swipe away case for a rescheduled notification: in this case
165         // it should not be rescheduled anymore
166         Settings.Global.putInt(mContext.getContentResolver(),
167                 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
168                 NotificationManagerService.REVIEW_NOTIF_STATE_RESHOWN);
169 
170         // set up Intent action, would be called from notification's delete intent
171         mIntent.setAction(NotificationManagerService.REVIEW_NOTIF_ACTION_CANCELED);
172 
173         // send intent, make sure it gets:
174         //   - not rescheduled on the second+ swipe
175         //   - not explicitly canceled, the notification was already canceled
176         //   - mark as user interacted
177         mReceiver.onReceive(mContext, mIntent);
178         assertFalse(mReceiver.mRescheduled);
179         assertFalse(mReceiver.mCanceled);
180         assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_USER_INTERACTED,
181                 Settings.Global.getInt(mContext.getContentResolver(),
182                         Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
183                         NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
184     }
185 
186     @Test
testReceive_notificationCanceled_fromDismiss()187     public void testReceive_notificationCanceled_fromDismiss() {
188         // Test that if the notification delete intent is called due to us canceling
189         // the notification from the receiver, we don't do anything extra
190         Settings.Global.putInt(mContext.getContentResolver(),
191                 Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
192                 NotificationManagerService.REVIEW_NOTIF_STATE_DISMISSED);
193 
194         // set up Intent action, would be called from notification's delete intent
195         mIntent.setAction(NotificationManagerService.REVIEW_NOTIF_ACTION_CANCELED);
196 
197         // nothing should happen, nothing at all
198         mReceiver.onReceive(mContext, mIntent);
199         assertFalse(mReceiver.mRescheduled);
200         assertFalse(mReceiver.mCanceled);
201         assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_DISMISSED,
202                 Settings.Global.getInt(mContext.getContentResolver(),
203                         Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
204                         NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN));
205     }
206 }
207