1 /*
2  * Copyright (C) 2021 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 android.companion.ICompanionDeviceManager;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.service.notification.StatusBarNotification;
23 
24 import androidx.annotation.Nullable;
25 
26 import com.android.internal.logging.InstanceIdSequence;
27 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
28 
29 import java.util.HashSet;
30 import java.util.Set;
31 
32 public class TestableNotificationManagerService extends NotificationManagerService {
33     int countSystemChecks = 0;
34     boolean isSystemUid = true;
35     boolean isSystemAppId = true;
36     int countLogSmartSuggestionsVisible = 0;
37     Set<Integer> mChannelToastsSent = new HashSet<>();
38 
39     String stringArrayResourceValue;
40     @Nullable
41     NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
42 
43     @Nullable
44     Boolean mIsVisibleToListenerReturnValue = null;
45 
46     ComponentPermissionChecker permissionChecker;
47 
48     private static class SensitiveLog {
49         public boolean hasPosted;
50         public boolean hasSensitiveContent;
51         public long lifetime;
52     }
53     public SensitiveLog lastSensitiveLog = null;
54 
55     private static class ClassificationChannelLog {
56         public boolean hasPosted;
57         public boolean isAlerting;
58         public long classification;
59         public long lifetime;
60     }
61     public ClassificationChannelLog  lastClassificationChannelLog = null;
62 
TestableNotificationManagerService(Context context, NotificationRecordLogger logger, InstanceIdSequence notificationInstanceIdSequence)63     TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
64             InstanceIdSequence notificationInstanceIdSequence) {
65         super(context, logger, notificationInstanceIdSequence);
66     }
67 
getRankingHelper()68     RankingHelper getRankingHelper() {
69         return mRankingHelper;
70     }
71 
72     /**
73      * Sets {@link #isSystemUid} and {@link #isSystemAppId} to {@code false}, so that calls to NMS
74      * methods don't succeed {@link #isCallingUidSystem()} and similar checks.
75      */
setCallerIsNormalPackage()76     void setCallerIsNormalPackage() {
77         isSystemUid = false;
78         isSystemAppId = false;
79     }
80 
81     @Override
isCallingUidSystem()82     protected boolean isCallingUidSystem() {
83         countSystemChecks++;
84         return isSystemUid;
85     }
86 
87     @Override
isCallingAppIdSystem()88     protected boolean isCallingAppIdSystem() {
89         countSystemChecks++;
90         return isSystemUid || isSystemAppId;
91     }
92 
93     @Override
isCallerSystemOrPhone()94     protected boolean isCallerSystemOrPhone() {
95         countSystemChecks++;
96         return isSystemUid || isSystemAppId;
97     }
98 
99     @Override
isCallerSystemOrSystemUi()100     protected boolean isCallerSystemOrSystemUi() {
101         countSystemChecks++;
102         return isSystemUid || isSystemAppId;
103     }
104 
105     @Override
getCompanionManager()106     protected ICompanionDeviceManager getCompanionManager() {
107         return null;
108     }
109 
110     @Override
reportUserInteraction(NotificationRecord r)111     protected void reportUserInteraction(NotificationRecord r) {
112         return;
113     }
114 
115     @Override
handleSavePolicyFile()116     protected void handleSavePolicyFile() {
117         return;
118     }
119 
120     @Override
logSmartSuggestionsVisible(NotificationRecord r, int notificationLocation)121     void logSmartSuggestionsVisible(NotificationRecord r, int notificationLocation) {
122         super.logSmartSuggestionsVisible(r, notificationLocation);
123         countLogSmartSuggestionsVisible++;
124     }
125 
126     @Override
setNotificationAssistantAccessGrantedForUserInternal( ComponentName assistant, int userId, boolean granted, boolean userSet)127     protected void setNotificationAssistantAccessGrantedForUserInternal(
128             ComponentName assistant, int userId, boolean granted, boolean userSet) {
129         if (mNotificationAssistantAccessGrantedCallback != null) {
130             mNotificationAssistantAccessGrantedCallback.onGranted(assistant, userId, granted,
131                     userSet);
132             return;
133         }
134         super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted,
135                 userSet);
136     }
137 
138     @Override
getStringArrayResource(int key)139     protected String[] getStringArrayResource(int key) {
140         return new String[] {stringArrayResourceValue};
141     }
142 
setStringArrayResourceValue(String value)143     protected void setStringArrayResourceValue(String value) {
144         stringArrayResourceValue = value;
145     }
146 
setNotificationAssistantAccessGrantedCallback( @ullable NotificationAssistantAccessGrantedCallback callback)147     void setNotificationAssistantAccessGrantedCallback(
148             @Nullable NotificationAssistantAccessGrantedCallback callback) {
149         this.mNotificationAssistantAccessGrantedCallback = callback;
150     }
151 
152     interface NotificationAssistantAccessGrantedCallback {
onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet)153         void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet);
154     }
155 
156     @Override
doChannelWarningToast(int uid, CharSequence toastText)157     protected void doChannelWarningToast(int uid, CharSequence toastText) {
158         mChannelToastsSent.add(uid);
159     }
160 
161     // Helper method for testing behavior when turning on/off the review permissions notification.
setShowReviewPermissionsNotification(boolean setting)162     protected void setShowReviewPermissionsNotification(boolean setting) {
163         mShowReviewPermissionsNotification = setting;
164     }
165 
setIsVisibleToListenerReturnValue(boolean value)166     protected void setIsVisibleToListenerReturnValue(boolean value) {
167         mIsVisibleToListenerReturnValue = value;
168     }
169 
170     @Override
isVisibleToListener(StatusBarNotification sbn, int notificationType, ManagedServiceInfo listener)171     boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
172             ManagedServiceInfo listener) {
173         if (mIsVisibleToListenerReturnValue != null) {
174             return mIsVisibleToListenerReturnValue;
175         }
176         return super.isVisibleToListener(sbn, notificationType, listener);
177     }
178 
179     @Override
checkComponentPermission(String permission, int uid, int owningUid, boolean exported)180     protected int checkComponentPermission(String permission, int uid, int owningUid,
181             boolean exported) {
182         return permissionChecker.check(permission, uid, owningUid, exported);
183     }
184 
185     @Override
logSensitiveAdjustmentReceived(boolean hasPosted, boolean hasSensitiveContent, int lifetimeMs)186     protected void logSensitiveAdjustmentReceived(boolean hasPosted, boolean hasSensitiveContent,
187             int lifetimeMs) {
188         lastSensitiveLog = new SensitiveLog();
189         lastSensitiveLog.hasPosted = hasPosted;
190         lastSensitiveLog.hasSensitiveContent = hasSensitiveContent;
191         lastSensitiveLog.lifetime = lifetimeMs;
192     }
193 
194     public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
195         private int mGetStrongAuthForUserReturnValue = 0;
StrongAuthTrackerFake(Context context)196         StrongAuthTrackerFake(Context context) {
197             super(context);
198         }
199 
setGetStrongAuthForUserReturnValue(int val)200         public void setGetStrongAuthForUserReturnValue(int val) {
201             mGetStrongAuthForUserReturnValue = val;
202         }
203 
204         @Override
getStrongAuthForUser(int userId)205         public int getStrongAuthForUser(int userId) {
206             return mGetStrongAuthForUserReturnValue;
207         }
208     }
209 
checkLastSensitiveLog(boolean hasPosted, boolean hasSensitive, int lifetime)210     public boolean checkLastSensitiveLog(boolean hasPosted, boolean hasSensitive, int lifetime) {
211         if (lastSensitiveLog == null) {
212             return false;
213         }
214         return hasPosted == lastSensitiveLog.hasPosted
215                 && hasSensitive == lastSensitiveLog.hasSensitiveContent
216                 && lifetime == lastSensitiveLog.lifetime;
217     }
218 
219     public interface ComponentPermissionChecker {
check(String permission, int uid, int owningUid, boolean exported)220         int check(String permission, int uid, int owningUid, boolean exported);
221     }
222 
223     @Override
logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting, int classification, int lifetimeMs)224     protected void logClassificationChannelAdjustmentReceived(boolean hasPosted, boolean isAlerting,
225                                                               int classification, int lifetimeMs) {
226         lastClassificationChannelLog = new ClassificationChannelLog();
227         lastClassificationChannelLog.hasPosted = hasPosted;
228         lastClassificationChannelLog.isAlerting = isAlerting;
229         lastClassificationChannelLog.classification = classification;
230         lastClassificationChannelLog.lifetime = lifetimeMs;
231     }
232 
233     /**
234      * Returns true if the last recorded classification channel log has all the values specified.
235      */
checkLastClassificationChannelLog(boolean hasPosted, boolean isAlerting, int classification, int lifetime)236     public boolean checkLastClassificationChannelLog(boolean hasPosted, boolean isAlerting,
237                                                      int classification, int lifetime) {
238         if (lastClassificationChannelLog == null) {
239             return false;
240         }
241 
242         return hasPosted == lastClassificationChannelLog.hasPosted
243                 && isAlerting == lastClassificationChannelLog.isAlerting
244                 && classification == lastClassificationChannelLog.classification
245                 && lifetime == lastClassificationChannelLog.lifetime;
246     }
247 }
248