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