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.app.AlertDialog;
20 import android.app.Notification;
21 import android.app.NotificationManager;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.res.Resources;
28 import android.net.wifi.WifiConfiguration;
29 import android.os.Handler;
30 import android.os.Process;
31 import android.util.Log;
32 
33 import com.android.internal.R;
34 
35 /**
36  * This class may be used to launch notifications when wifi connections fail.
37  */
38 public class ConnectionFailureNotifier {
39     private static final String TAG = "ConnectionFailureNotifier";
40     public static final int NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID = 123;
41 
42     private Context mContext;
43     private WifiInjector mWifiInjector;
44     private Resources mResources;
45     private FrameworkFacade mFrameworkFacade;
46     private WifiConfigManager mWifiConfigManager;
47     private WifiConnectivityManager mWifiConnectivityManager;
48     private NotificationManager mNotificationManager;
49     private Handler mHandler;
50     private ConnectionFailureNotificationBuilder mConnectionFailureNotificationBuilder;
51 
ConnectionFailureNotifier( Context context, WifiInjector wifiInjector, FrameworkFacade framework, WifiConfigManager wifiConfigManager, WifiConnectivityManager wifiConnectivityManager, Handler handler)52     public ConnectionFailureNotifier(
53             Context context, WifiInjector wifiInjector, FrameworkFacade framework,
54             WifiConfigManager wifiConfigManager, WifiConnectivityManager wifiConnectivityManager,
55             Handler handler) {
56         mContext = context;
57         mWifiInjector = wifiInjector;
58         mResources = context.getResources();
59         mFrameworkFacade = framework;
60         mWifiConfigManager = wifiConfigManager;
61         mWifiConnectivityManager = wifiConnectivityManager;
62         mNotificationManager = mWifiInjector.getNotificationManager();
63         mHandler = handler;
64         mConnectionFailureNotificationBuilder =
65                 mWifiInjector.getConnectionFailureNotificationBuilder();
66 
67         IntentFilter filter = new IntentFilter();
68         filter.addAction(ConnectionFailureNotificationBuilder
69                 .ACTION_SHOW_SET_RANDOMIZATION_DETAILS);
70         mContext.registerReceiver(
71                 new BroadcastReceiver() {
72                     @Override
73                     public void onReceive(Context context, Intent intent) {
74                         String action = intent.getAction();
75                         if (action.equals(ConnectionFailureNotificationBuilder
76                                 .ACTION_SHOW_SET_RANDOMIZATION_DETAILS)) {
77                             int networkId = intent.getIntExtra(
78                                     ConnectionFailureNotificationBuilder
79                                             .RANDOMIZATION_SETTINGS_NETWORK_ID,
80                                     WifiConfiguration.INVALID_NETWORK_ID);
81                             String ssidAndSecurityType = intent.getStringExtra(
82                                     ConnectionFailureNotificationBuilder
83                                             .RANDOMIZATION_SETTINGS_NETWORK_SSID);
84                             showRandomizationSettingsDialog(networkId, ssidAndSecurityType);
85                         }
86                     }
87                 }, filter);
88     }
89 
90     /**
91      * Shows a notification which will bring up a dialog which offers the user an option to disable
92      * MAC randomization on |networkdId|.
93      * @param networkId
94      */
showFailedToConnectDueToNoRandomizedMacSupportNotification(int networkId)95     public void showFailedToConnectDueToNoRandomizedMacSupportNotification(int networkId) {
96         WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId);
97         if (config == null) {
98             return;
99         }
100         Notification notification = mConnectionFailureNotificationBuilder
101                 .buildNoMacRandomizationSupportNotification(config);
102         mNotificationManager.notify(NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID, notification);
103     }
104 
105     class DisableMacRandomizationListener implements DialogInterface.OnClickListener {
106         private WifiConfiguration mConfig;
107 
DisableMacRandomizationListener(WifiConfiguration config)108         DisableMacRandomizationListener(WifiConfiguration config) {
109             mConfig = config;
110         }
111 
112         @Override
onClick(DialogInterface dialog, int which)113         public void onClick(DialogInterface dialog, int which) {
114             mHandler.post(() -> {
115                 mConfig.macRandomizationSetting =
116                         WifiConfiguration.RANDOMIZATION_NONE;
117                 mWifiConfigManager.addOrUpdateNetwork(mConfig, Process.SYSTEM_UID);
118                 WifiConfiguration updatedConfig =
119                         mWifiConfigManager.getConfiguredNetwork(mConfig.networkId);
120                 if (updatedConfig.macRandomizationSetting
121                         == WifiConfiguration.RANDOMIZATION_NONE) {
122                     String message = mResources.getString(
123                             R.string.wifi_disable_mac_randomization_dialog_success);
124                     mFrameworkFacade.showToast(mContext, message);
125                     mWifiConfigManager.enableNetwork(updatedConfig.networkId, true,
126                             Process.SYSTEM_UID);
127                     mWifiConnectivityManager.forceConnectivityScan(
128                             ClientModeImpl.WIFI_WORK_SOURCE);
129                 } else {
130                     // Shouldn't ever fail, but here for completeness
131                     String message = mResources.getString(
132                             R.string.wifi_disable_mac_randomization_dialog_failure);
133                     mFrameworkFacade.showToast(mContext, message);
134                     Log.e(TAG, "Failed to modify mac randomization setting");
135                 }
136             });
137         }
138     }
139 
140     /**
141      * Class to show a AlertDialog which notifies the user of a network not being privacy
142      * compliant and then suggests an action.
143      */
showRandomizationSettingsDialog(int networkId, String ssidAndSecurityType)144     private void showRandomizationSettingsDialog(int networkId, String ssidAndSecurityType) {
145         WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId);
146         // Make sure the networkId is still pointing to the correct WifiConfiguration since
147         // there might be a large time gap between when the notification shows and when
148         // it's tapped.
149         if (config == null || ssidAndSecurityType == null
150                 || !ssidAndSecurityType.equals(config.getSsidAndSecurityTypeString())) {
151             String message = mResources.getString(
152                     R.string.wifi_disable_mac_randomization_dialog_network_not_found);
153             mFrameworkFacade.showToast(mContext, message);
154             return;
155         }
156 
157         AlertDialog dialog = mConnectionFailureNotificationBuilder
158                 .buildChangeMacRandomizationSettingDialog(config.SSID,
159                         new DisableMacRandomizationListener(config));
160         dialog.show();
161     }
162 }
163