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