content: /*
   * Copyright (C) 2019 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.wifi;
  
  import android.app.AlertDialog;
  import android.app.Notification;
  import android.app.NotificationManager;
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.res.Resources;
  import android.net.wifi.WifiConfiguration;
  import android.os.Handler;
  import android.os.Process;
  import android.util.Log;
  
  import com.android.internal.R;
  
  /**
   * This class may be used to launch notifications when wifi connections fail.
   */
  public class ConnectionFailureNotifier {
      private static final String TAG = "ConnectionFailureNotifier";
      public static final int NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID = 123;
  
      private Context mContext;
      private WifiInjector mWifiInjector;
      private Resources mResources;
      private FrameworkFacade mFrameworkFacade;
      private WifiConfigManager mWifiConfigManager;
      private WifiConnectivityManager mWifiConnectivityManager;
      private NotificationManager mNotificationManager;
      private Handler mHandler;
      private ConnectionFailureNotificationBuilder mConnectionFailureNotificationBuilder;
  
      public ConnectionFailureNotifier(
              Context context, WifiInjector wifiInjector, FrameworkFacade framework,
              WifiConfigManager wifiConfigManager, WifiConnectivityManager wifiConnectivityManager,
              Handler handler) {
          mContext = context;
          mWifiInjector = wifiInjector;
          mResources = context.getResources();
          mFrameworkFacade = framework;
          mWifiConfigManager = wifiConfigManager;
          mWifiConnectivityManager = wifiConnectivityManager;
          mNotificationManager = mWifiInjector.getNotificationManager();
          mHandler = handler;
          mConnectionFailureNotificationBuilder =
                  mWifiInjector.getConnectionFailureNotificationBuilder();
  
          IntentFilter filter = new IntentFilter();
          filter.addAction(ConnectionFailureNotificationBuilder
                  .ACTION_SHOW_SET_RANDOMIZATION_DETAILS);
          mContext.registerReceiver(
                  new BroadcastReceiver() {
                      @Override
                      public void onReceive(Context context, Intent intent) {
                          String action = intent.getAction();
                          if (action.equals(ConnectionFailureNotificationBuilder
                                  .ACTION_SHOW_SET_RANDOMIZATION_DETAILS)) {
                              int networkId = intent.getIntExtra(
                                      ConnectionFailureNotificationBuilder
                                              .RANDOMIZATION_SETTINGS_NETWORK_ID,
                                      WifiConfiguration.INVALID_NETWORK_ID);
                              String ssidAndSecurityType = intent.getStringExtra(
                                      ConnectionFailureNotificationBuilder
                                              .RANDOMIZATION_SETTINGS_NETWORK_SSID);
                              showRandomizationSettingsDialog(networkId, ssidAndSecurityType);
                          }
                      }
                  }, filter);
      }
  
      /**
       * Shows a notification which will bring up a dialog which offers the user an option to disable
       * MAC randomization on |networkdId|.
       * @param networkId
       */
      public void showFailedToConnectDueToNoRandomizedMacSupportNotification(int networkId) {
          WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId);
          if (config == null) {
              return;
          }
          Notification notification = mConnectionFailureNotificationBuilder
                  .buildNoMacRandomizationSupportNotification(config);
          mNotificationManager.notify(NO_RANDOMIZED_MAC_SUPPORT_NOTIFICATION_ID, notification);
      }
  
      class DisableMacRandomizationListener implements DialogInterface.OnClickListener {
          private WifiConfiguration mConfig;
  
          DisableMacRandomizationListener(WifiConfiguration config) {
              mConfig = config;
          }
  
          @Override
          public void onClick(DialogInterface dialog, int which) {
              mHandler.post(() -> {
                  mConfig.macRandomizationSetting =
                          WifiConfiguration.RANDOMIZATION_NONE;
                  mWifiConfigManager.addOrUpdateNetwork(mConfig, Process.SYSTEM_UID);
                  WifiConfiguration updatedConfig =
                          mWifiConfigManager.getConfiguredNetwork(mConfig.networkId);
                  if (updatedConfig.macRandomizationSetting
                          == WifiConfiguration.RANDOMIZATION_NONE) {
                      String message = mResources.getString(
                              R.string.wifi_disable_mac_randomization_dialog_success);
                      mFrameworkFacade.showToast(mContext, message);
                      mWifiConfigManager.enableNetwork(updatedConfig.networkId, true,
                              Process.SYSTEM_UID);
                      mWifiConnectivityManager.forceConnectivityScan(
                              ClientModeImpl.WIFI_WORK_SOURCE);
                  } else {
                      // Shouldn't ever fail, but here for completeness
                      String message = mResources.getString(
                              R.string.wifi_disable_mac_randomization_dialog_failure);
                      mFrameworkFacade.showToast(mContext, message);
                      Log.e(TAG, "Failed to modify mac randomization setting");
                  }
              });
          }
      }
  
      /**
       * Class to show a AlertDialog which notifies the user of a network not being privacy
       * compliant and then suggests an action.
       */
      private void showRandomizationSettingsDialog(int networkId, String ssidAndSecurityType) {
          WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId);
          // Make sure the networkId is still pointing to the correct WifiConfiguration since
          // there might be a large time gap between when the notification shows and when
          // it's tapped.
          if (config == null || ssidAndSecurityType == null
                  || !ssidAndSecurityType.equals(config.getSsidAndSecurityTypeString())) {
              String message = mResources.getString(
                      R.string.wifi_disable_mac_randomization_dialog_network_not_found);
              mFrameworkFacade.showToast(mContext, message);
              return;
          }
  
          AlertDialog dialog = mConnectionFailureNotificationBuilder
                  .buildChangeMacRandomizationSettingDialog(config.SSID,
                          new DisableMacRandomizationListener(config));
          dialog.show();
      }
  }
  