1 /* 2 * Copyright (C) 2019 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.wifi; 18 19 import android.annotation.Nullable; 20 import android.app.Notification; 21 import android.app.PendingIntent; 22 import android.content.Intent; 23 import android.graphics.drawable.Icon; 24 import android.net.wifi.WifiConfiguration; 25 import android.net.wifi.WifiContext; 26 import android.net.wifi.WifiStringResourceWrapper; 27 import android.provider.Settings; 28 import android.service.notification.StatusBarNotification; 29 import android.telephony.TelephonyManager; 30 import android.text.TextUtils; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 34 import com.android.modules.utils.build.SdkLevel; 35 36 /** 37 * This class may be used to launch notifications when EAP failure occurs. 38 */ 39 public class EapFailureNotifier { 40 private static final String TAG = "EapFailureNotifier"; 41 @VisibleForTesting 42 static final String ERROR_MESSAGE_OVERLAY_PREFIX = "wifi_eap_error_message_code_"; 43 @VisibleForTesting 44 static final String ERROR_MESSAGE_OVERLAY_UNKNOWN_ERROR_CODE = 45 "wifi_eap_error_message_unknown_error_code"; 46 @VisibleForTesting 47 static final String CONFIG_EAP_FAILURE_DISABLE_THRESHOLD = 48 "config_wifiDisableReasonAuthenticationFailureCarrierSpecificThreshold"; 49 @VisibleForTesting 50 static final String CONFIG_EAP_FAILURE_DISABLE_DURATION = 51 "config_wifiDisableReasonAuthenticationFailureCarrierSpecificDurationMs"; 52 // Special error message that results in the EAP failure code to get ignored on devices pre 53 // Android 13. 54 public static final String ERROR_MESSAGE_IGNORE_ON_PRE_ANDROID_13 = "IgnorePreAndroid13"; 55 56 private static final long CANCEL_TIMEOUT_MILLISECONDS = 5 * 60 * 1000; 57 private final WifiContext mContext; 58 private final WifiNotificationManager mNotificationManager; 59 private final FrameworkFacade mFrameworkFacade; 60 private final WifiCarrierInfoManager mWifiCarrierInfoManager; 61 private final WifiGlobals mWifiGlobals; 62 63 // Unique ID associated with the notification. 64 public static final int NOTIFICATION_ID = SystemMessage.NOTE_WIFI_EAP_FAILURE; 65 private String mCurrentShownSsid; 66 EapFailureNotifier(WifiContext context, FrameworkFacade frameworkFacade, WifiCarrierInfoManager wifiCarrierInfoManager, WifiNotificationManager wifiNotificationManager, WifiGlobals wifiGlobals)67 public EapFailureNotifier(WifiContext context, FrameworkFacade frameworkFacade, 68 WifiCarrierInfoManager wifiCarrierInfoManager, 69 WifiNotificationManager wifiNotificationManager, 70 WifiGlobals wifiGlobals) { 71 mContext = context; 72 mFrameworkFacade = frameworkFacade; 73 mWifiCarrierInfoManager = wifiCarrierInfoManager; 74 mNotificationManager = wifiNotificationManager; 75 mWifiGlobals = wifiGlobals; 76 } 77 78 /** 79 * Invoked when EAP failure occurs. 80 * 81 * @param errorCode error code which delivers from supplicant 82 * @param showNotification whether to display the notification 83 * @return CarrierSpecificEapFailureConfig if the receiving error code is found in wifi resource 84 * null if not found. 85 */ onEapFailure( int errorCode, WifiConfiguration config, boolean showNotification)86 public @Nullable WifiBlocklistMonitor.CarrierSpecificEapFailureConfig onEapFailure( 87 int errorCode, WifiConfiguration config, boolean showNotification) { 88 if (errorCode < 0) { 89 // EapErrorCode is defined as an unsigned uint32_t in ISupplicantStaIfaceCallback, so 90 // only consider non-negative error codes for carrier-specific error messages. 91 return null; 92 } 93 int carrierId = config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID 94 ? mWifiCarrierInfoManager.getDefaultDataSimCarrierId() 95 : config.carrierId; 96 WifiStringResourceWrapper sr = mContext.getStringResourceWrapper( 97 mWifiCarrierInfoManager.getBestMatchSubscriptionId(config), 98 carrierId); 99 String errorMessage = sr.getString(ERROR_MESSAGE_OVERLAY_PREFIX + errorCode, config.SSID); 100 if (SdkLevel.isAtLeastT()) { 101 if (errorMessage == null) { 102 // Use the generic error message if the code does not match any known code. 103 errorMessage = sr.getString(ERROR_MESSAGE_OVERLAY_UNKNOWN_ERROR_CODE, config.SSID); 104 } 105 } else if (errorMessage != null 106 && errorMessage.contains(ERROR_MESSAGE_IGNORE_ON_PRE_ANDROID_13)) { 107 return null; 108 } 109 if (TextUtils.isEmpty(errorMessage)) return null; 110 WifiBlocklistMonitor.CarrierSpecificEapFailureConfig eapFailureConfig = 111 new WifiBlocklistMonitor.CarrierSpecificEapFailureConfig( 112 sr.getInt(CONFIG_EAP_FAILURE_DISABLE_THRESHOLD, 1), 113 sr.getInt(CONFIG_EAP_FAILURE_DISABLE_DURATION, -1), 114 true); 115 if (SdkLevel.isAtLeastU()) { 116 WifiBlocklistMonitor.CarrierSpecificEapFailureConfig carrierSpecificOverride = 117 mWifiGlobals.getCarrierSpecificEapFailureConfig(carrierId, errorCode); 118 if (carrierSpecificOverride != null) { 119 eapFailureConfig = carrierSpecificOverride; 120 } 121 } 122 123 StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications(); 124 for (StatusBarNotification activeNotification : activeNotifications) { 125 if ((activeNotification.getId() == NOTIFICATION_ID) 126 && TextUtils.equals(config.SSID, mCurrentShownSsid)) { 127 return eapFailureConfig; 128 } 129 } 130 131 if (showNotification && eapFailureConfig.displayNotification) { 132 showNotification(errorMessage, config.SSID); 133 } 134 return eapFailureConfig; 135 } 136 137 /** 138 * Dismiss notification 139 */ dismissEapFailureNotification(String ssid)140 public void dismissEapFailureNotification(String ssid) { 141 if (TextUtils.isEmpty(mCurrentShownSsid) || TextUtils.isEmpty(ssid) 142 || !TextUtils.equals(ssid, mCurrentShownSsid)) { 143 return; 144 } 145 StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications(); 146 for (StatusBarNotification activeNotification : activeNotifications) { 147 if ((activeNotification.getId() == NOTIFICATION_ID)) { 148 mNotificationManager.cancel(NOTIFICATION_ID); 149 return; 150 } 151 } 152 } 153 154 /** 155 * Display eap error notification which defined by carrier. 156 * 157 * @param ssid Error Message which defined by carrier 158 */ showNotification(String errorMessage, String ssid)159 private void showNotification(String errorMessage, String ssid) { 160 String settingsPackage = mFrameworkFacade.getSettingsPackageName(mContext); 161 if (settingsPackage == null) return; 162 Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS) 163 .setPackage(settingsPackage); 164 Notification.Builder builder = mFrameworkFacade.makeNotificationBuilder(mContext, 165 WifiService.NOTIFICATION_NETWORK_ALERTS) 166 .setAutoCancel(true) 167 .setTimeoutAfter(CANCEL_TIMEOUT_MILLISECONDS) 168 .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), 169 com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range)) 170 .setContentTitle(mContext.getString( 171 com.android.wifi.resources.R.string.wifi_available_title_failed_to_connect)) 172 .setContentText(errorMessage) 173 .setStyle(new Notification.BigTextStyle().bigText(errorMessage)) 174 .setContentIntent(mFrameworkFacade.getActivity( 175 mContext, 0, intent, 176 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) 177 .setColor(mContext.getResources().getColor( 178 android.R.color.system_notification_accent_color)); 179 mNotificationManager.notify(NOTIFICATION_ID, 180 builder.build()); 181 mCurrentShownSsid = ssid; 182 } 183 184 /** 185 * Allow tests to modify mCurrentShownSsid 186 */ 187 @VisibleForTesting setCurrentShownSsid(String currentShownSsid)188 void setCurrentShownSsid(String currentShownSsid) { 189 mCurrentShownSsid = currentShownSsid; 190 } 191 } 192