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