• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.app.stubs.shared;
18 
19 import android.app.Notification;
20 import android.app.NotificationChannel;
21 import android.app.NotificationManager;
22 import android.os.Bundle;
23 import android.os.ConditionVariable;
24 import android.service.notification.Adjustment;
25 import android.service.notification.NotificationAssistantService;
26 import android.service.notification.StatusBarNotification;
27 import android.util.Log;
28 
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.concurrent.CountDownLatch;
34 
35 public class TestNotificationAssistant extends NotificationAssistantService {
36     public static final String TAG = "TestNotificationAssistant";
37     public static final String PKG = "android.app.stubs";
38     private static final long CONNECTION_TIMEOUT_MS = 5000;
39 
40     private static TestNotificationAssistant sNotificationAssistantInstance = null;
41     boolean mIsConnected;
42     public List<String> mCurrentCapabilities = new ArrayList<>();
43     public boolean mIsPanelOpen = false;
44     public String mSnoozedKey;
45     public String mSnoozedUntilContext;
46     public boolean mNotificationVisible = false;
47     public int mNotificationId = 1357;
48     public int mNotificationSeenCount = 0;
49     public int mNotificationClickCount = 0;
50     public int mNotificationRank = -1;
51     public int mNotificationFeedback = 0;
52 
53     public boolean mMarkSensitiveContent = false;
54     public ArrayList<Notification.Action> mSmartActions = null;
55     public ArrayList<CharSequence> mSmartReplies = null;
56     private NotificationManager mNotificationManager;
57 
58     public Map<String, Integer> mRemoved = new HashMap<>();
59     private CountDownLatch mAllowedAdjustmentsLatch = null;
60 
61     /**
62      * This controls whether there is a listener connected or not. Depending on the method, if the
63      * caller tries to use a listener after it has disconnected, NMS can throw a SecurityException.
64      *
65      * There is no race between onListenerConnected() and onListenerDisconnected() because they are
66      * called in the same thread. The value that getInstance() sees is guaranteed to be the value
67      * that was set by onListenerConnected() because of the happens-before established by the
68      * condition variable.
69      */
70     private static final ConditionVariable INSTANCE_AVAILABLE = new ConditionVariable(false);
71 
72     @Override
onCreate()73     public void onCreate() {
74         super.onCreate();
75         mNotificationManager = getSystemService(NotificationManager.class);
76     }
77 
resetData()78     public void resetData() {
79         mIsPanelOpen = false;
80         mCurrentCapabilities.clear();
81         mNotificationVisible = false;
82         mNotificationSeenCount = 0;
83         mNotificationClickCount = 0;
84         mNotificationRank = -1;
85         mNotificationFeedback = 0;
86         mSnoozedKey = null;
87         mSnoozedUntilContext = null;
88         mRemoved.clear();
89     }
90 
91     @Override
onListenerConnected()92     public void onListenerConnected() {
93         super.onListenerConnected();
94         sNotificationAssistantInstance = this;
95         mCurrentCapabilities = mNotificationManager.getAllowedAssistantAdjustments();
96         INSTANCE_AVAILABLE.open();
97         Log.d(TAG, "TestNotificationAssistant connected");
98         mIsConnected = true;
99     }
100 
101     @Override
onListenerDisconnected()102     public void onListenerDisconnected() {
103         INSTANCE_AVAILABLE.close();
104         Log.d(TAG, "TestNotificationAssistant disconnected");
105         sNotificationAssistantInstance = null;
106         mIsConnected = false;
107     }
108 
getInstance()109     public static TestNotificationAssistant getInstance() {
110         if (INSTANCE_AVAILABLE.block(CONNECTION_TIMEOUT_MS)) {
111             return sNotificationAssistantInstance;
112         }
113         return null;
114     }
115 
116     @Override
onNotificationSnoozedUntilContext(StatusBarNotification statusBarNotification, String s)117     public void onNotificationSnoozedUntilContext(StatusBarNotification statusBarNotification,
118             String s) {
119         mSnoozedKey = statusBarNotification.getKey();
120         mSnoozedUntilContext = s;
121     }
122 
123     @Override
onNotificationEnqueued(StatusBarNotification sbn)124     public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
125         return null;
126     }
127 
128     @Override
onNotificationEnqueued(StatusBarNotification sbn, NotificationChannel channel, RankingMap rankingMap)129     public Adjustment onNotificationEnqueued(StatusBarNotification sbn, NotificationChannel channel,
130             RankingMap rankingMap) {
131         Bundle signals = new Bundle();
132         Ranking ranking = new Ranking();
133         rankingMap.getRanking(sbn.getKey(), ranking);
134         mNotificationRank = ranking.getRank();
135         signals.putInt(Adjustment.KEY_USER_SENTIMENT, Ranking.USER_SENTIMENT_POSITIVE);
136         if (mMarkSensitiveContent) {
137             signals.putBoolean(Adjustment.KEY_SENSITIVE_CONTENT, true);
138         }
139         if (mSmartActions != null) {
140             signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, mSmartActions);
141         }
142         if (mSmartReplies != null) {
143             signals.putCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES, mSmartReplies);
144         }
145         return new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
146                 sbn.getUser());
147     }
148 
149     @Override
onAllowedAdjustmentsChanged()150     public void onAllowedAdjustmentsChanged() {
151         mCurrentCapabilities = mNotificationManager.getAllowedAssistantAdjustments();
152         maybeUpdateLatch(mAllowedAdjustmentsLatch);
153     }
154 
resetNotificationVisibilityCounts()155     public void resetNotificationVisibilityCounts() {
156         mNotificationSeenCount = 0;
157     }
158 
159     @Override
onNotificationVisibilityChanged(String key, boolean isVisible)160     public void onNotificationVisibilityChanged(String key, boolean isVisible) {
161         if (key.contains(getPackageName() + "|" + mNotificationId)) {
162             mNotificationVisible = isVisible;
163         }
164     }
165 
166     @Override
onNotificationsSeen(List<String> keys)167     public void onNotificationsSeen(List<String> keys) {
168         mNotificationSeenCount += keys.size();
169     }
170 
171     @Override
onPanelHidden()172     public void onPanelHidden() {
173         mIsPanelOpen = false;
174     }
175 
176     @Override
onPanelRevealed(int items)177     public void onPanelRevealed(int items) {
178         mIsPanelOpen = true;
179     }
180 
resetNotificationClickCount()181     public void resetNotificationClickCount() {
182         mNotificationClickCount = 0;
183     }
184 
185     @Override
onNotificationClicked(String key)186     public void onNotificationClicked(String key) {
187         mNotificationClickCount++;
188     }
189 
190     @Override
onNotificationFeedbackReceived(String key, RankingMap rankingMap, Bundle feedback)191     public void onNotificationFeedbackReceived(String key, RankingMap rankingMap, Bundle feedback) {
192         mNotificationFeedback = feedback.getInt(FEEDBACK_RATING, 0);
193     }
194 
195     @Override
onNotificationPosted(StatusBarNotification sbn)196     public void onNotificationPosted(StatusBarNotification sbn) {
197 
198     }
199 
200     @Override
onNotificationRemoved(StatusBarNotification sbn)201     public void onNotificationRemoved(StatusBarNotification sbn) {
202         if (sbn == null) {
203             return;
204         }
205         mRemoved.put(sbn.getKey(), -1);
206     }
207 
208     @Override
onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason)209     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
210             int reason) {
211         if (sbn == null) {
212             return;
213         }
214         mRemoved.put(sbn.getKey(), reason);
215     }
216 
setAllowedAdjustmentCountdown(int countDownNumber)217     public CountDownLatch setAllowedAdjustmentCountdown(int countDownNumber) {
218         mAllowedAdjustmentsLatch = new CountDownLatch(countDownNumber);
219         return mAllowedAdjustmentsLatch;
220     }
221 
maybeUpdateLatch(CountDownLatch latch)222     private void maybeUpdateLatch(CountDownLatch latch) {
223         if (latch != null) {
224             latch.countDown();
225         }
226     }
227 }
228