1 /* 2 * Copyright (C) 2017 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.app.Notification; 20 import android.app.PendingIntent; 21 import android.content.Intent; 22 import android.graphics.drawable.Icon; 23 import android.net.wifi.WifiConfiguration; 24 import android.net.wifi.WifiContext; 25 import android.net.wifi.WifiSsid; 26 import android.provider.Settings; 27 28 import androidx.annotation.NonNull; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 32 33 /** 34 * Responsible for notifying user for wrong password errors. 35 */ 36 public class WrongPasswordNotifier { 37 private static final String TAG = "WrongPasswordNotifier"; 38 // Number of milliseconds to wait before automatically dismiss the notification. 39 private static final long CANCEL_TIMEOUT_MILLISECONDS = 5 * 60 * 1000; 40 41 // Unique ID associated with the notification. 42 @VisibleForTesting 43 public static final int NOTIFICATION_ID = SystemMessage.NOTE_WIFI_WRONG_PASSWORD; 44 45 // Flag indicating if a wrong password error is detected for the current connection. 46 private boolean mWrongPasswordDetected; 47 48 private final WifiContext mContext; 49 private final WifiNotificationManager mNotificationManager; 50 private final FrameworkFacade mFrameworkFacade; 51 WrongPasswordNotifier(WifiContext context, FrameworkFacade frameworkFacade, WifiNotificationManager wifiNotificationManager)52 public WrongPasswordNotifier(WifiContext context, FrameworkFacade frameworkFacade, 53 WifiNotificationManager wifiNotificationManager) { 54 mContext = context; 55 mFrameworkFacade = frameworkFacade; 56 mNotificationManager = wifiNotificationManager; 57 } 58 59 /** 60 * Invoked when a wrong password error for a Wi-Fi network is detected. 61 * 62 * @param wifiConfiguration the network 63 */ onWrongPasswordError(@onNull WifiConfiguration wifiConfiguration)64 public void onWrongPasswordError(@NonNull WifiConfiguration wifiConfiguration) { 65 showNotification(wifiConfiguration); 66 mWrongPasswordDetected = true; 67 } 68 69 /** 70 * Invoked when attempting a new Wi-Fi network connection. 71 */ onNewConnectionAttempt()72 public void onNewConnectionAttempt() { 73 if (mWrongPasswordDetected) { 74 dismissNotification(); 75 mWrongPasswordDetected = false; 76 } 77 } 78 79 /** 80 * Display wrong password notification for a given Wi-Fi network (specified by its SSID). 81 * 82 * @param wifiConfiguration the network 83 */ showNotification(@onNull WifiConfiguration wifiConfiguration)84 private void showNotification(@NonNull WifiConfiguration wifiConfiguration) { 85 CharSequence utf8Ssid = WifiSsid.fromString(wifiConfiguration.SSID).getUtf8Text(); 86 if (utf8Ssid == null) { 87 // TODO(qal): Non-utf-8 SSIDs are currently not supported in Settings, and the intent 88 // action will fail to open the password dialog for the correct network. In 89 // addition, it is unclear which charset is appropriate for the non-utf-8 90 // SSID. We may need to re-evaluate if we should support displaying non-utf-8 91 // SSIDs from the framework or not. For now, fallback to the raw 92 // WifiConfiguration.SSID so the user still gets a notification. 93 utf8Ssid = wifiConfiguration.SSID; 94 } 95 String settingsPackage = mFrameworkFacade.getSettingsPackageName(mContext); 96 if (settingsPackage == null) return; 97 Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS) 98 .setPackage(settingsPackage) 99 .putExtra("wifi_start_connect_ssid", utf8Ssid.toString()); 100 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 101 CharSequence title = mContext.getString( 102 com.android.wifi.resources.R.string.wifi_available_title_failed_to_connect); 103 Notification.Builder builder = mFrameworkFacade.makeNotificationBuilder(mContext, 104 WifiService.NOTIFICATION_NETWORK_ALERTS) 105 .setAutoCancel(true) 106 .setTimeoutAfter(CANCEL_TIMEOUT_MILLISECONDS) 107 // TODO(zqiu): consider creating a new icon. 108 .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(), 109 com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range)) 110 .setContentTitle(title) 111 .setContentText(utf8Ssid) 112 .setContentIntent(mFrameworkFacade.getActivity( 113 mContext, 0, intent, 114 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) 115 .setColor(mContext.getResources().getColor( 116 android.R.color.system_notification_accent_color)); 117 mNotificationManager.notify(NOTIFICATION_ID, builder.build()); 118 } 119 120 /** 121 * Dismiss the notification that was generated by {@link #showNotification}. The notification 122 * might have already been dismissed, either by user or timeout. We'll attempt to dismiss it 123 * regardless if it is been dismissed or not, to reduce code complexity. 124 */ dismissNotification()125 private void dismissNotification() { 126 // Notification might have already been dismissed, either by user or timeout. It is 127 // still okay to cancel it if already dismissed. 128 mNotificationManager.cancel(NOTIFICATION_ID); 129 } 130 } 131