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.car.settings.wifi; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.net.ConnectivityManager; 24 import android.net.TetheringManager; 25 import android.net.wifi.WifiManager; 26 27 import androidx.annotation.VisibleForTesting; 28 import androidx.lifecycle.Lifecycle; 29 import androidx.localbroadcastmanager.content.LocalBroadcastManager; 30 31 import com.android.internal.util.ConcurrentUtils; 32 33 /** 34 * Consolidates Wifi tethering logic into one handler so we can have consistent logic across various 35 * parts of the Settings app. 36 */ 37 public class WifiTetheringHandler { 38 39 private Context mContext; 40 private CarWifiManager mCarWifiManager; 41 private TetheringManager mTetheringManager; 42 private boolean mRestartBooked = false; 43 private WifiTetheringAvailabilityListener mWifiTetheringAvailabilityListener; 44 45 private WifiManager.SoftApCallback mSoftApCallback = new WifiManager.SoftApCallback() { 46 @Override 47 public void onStateChanged(int state, int failureReason) { 48 handleWifiApStateChanged(state); 49 } 50 }; 51 private final BroadcastReceiver mRestartReceiver = new BroadcastReceiver() { 52 @Override 53 public void onReceive(Context context, Intent intent) { 54 if (mCarWifiManager != null && mCarWifiManager.isWifiApEnabled()) { 55 restartTethering(); 56 } 57 } 58 }; 59 WifiTetheringHandler(Context context, Lifecycle lifecycle, WifiTetheringAvailabilityListener wifiTetherAvailabilityListener)60 public WifiTetheringHandler(Context context, Lifecycle lifecycle, 61 WifiTetheringAvailabilityListener wifiTetherAvailabilityListener) { 62 mContext = context; 63 mCarWifiManager = new CarWifiManager(mContext, lifecycle); 64 mTetheringManager = mContext.getSystemService(TetheringManager.class); 65 mWifiTetheringAvailabilityListener = wifiTetherAvailabilityListener; 66 } 67 68 /** 69 * Handles operations that should happen in host's onStartInternal(). 70 */ onStartInternal()71 public void onStartInternal() { 72 mCarWifiManager.registerSoftApCallback(mContext.getMainExecutor(), mSoftApCallback); 73 LocalBroadcastManager.getInstance(mContext).registerReceiver(mRestartReceiver, 74 new IntentFilter( 75 WifiTetherBasePreferenceController.ACTION_RESTART_WIFI_TETHERING)); 76 } 77 78 /** 79 * Handles operations that should happen in host's onStopInternal(). 80 */ onStopInternal()81 public void onStopInternal() { 82 mCarWifiManager.unregisterSoftApCallback(mSoftApCallback); 83 LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mRestartReceiver); 84 } 85 86 /** 87 * Returns whether wifi tethering is enabled 88 * @return whether wifi tethering is enabled 89 */ isWifiTetheringEnabled()90 public boolean isWifiTetheringEnabled() { 91 return mCarWifiManager.isWifiApEnabled(); 92 } 93 94 /** 95 * Changes the Wifi tethering state 96 * 97 * @param enable Whether to attempt to turn Wifi tethering on or off 98 */ updateWifiTetheringState(boolean enable)99 public void updateWifiTetheringState(boolean enable) { 100 if (enable) { 101 startTethering(); 102 } else { 103 stopTethering(); 104 } 105 } 106 107 @VisibleForTesting setCarWifiManager(CarWifiManager carWifiManager)108 void setCarWifiManager(CarWifiManager carWifiManager) { 109 mCarWifiManager = carWifiManager; 110 } 111 112 @VisibleForTesting setTetheringManager(TetheringManager tetheringManager)113 void setTetheringManager(TetheringManager tetheringManager) { 114 mTetheringManager = tetheringManager; 115 } 116 117 @VisibleForTesting handleWifiApStateChanged(int state)118 void handleWifiApStateChanged(int state) { 119 switch (state) { 120 case WifiManager.WIFI_AP_STATE_ENABLING: 121 mWifiTetheringAvailabilityListener.disablePreference(); 122 break; 123 case WifiManager.WIFI_AP_STATE_ENABLED: 124 mWifiTetheringAvailabilityListener.enablePreference(); 125 mWifiTetheringAvailabilityListener.onWifiTetheringAvailable(); 126 break; 127 case WifiManager.WIFI_AP_STATE_DISABLING: 128 mWifiTetheringAvailabilityListener.disablePreference(); 129 mWifiTetheringAvailabilityListener.onWifiTetheringUnavailable(); 130 break; 131 case WifiManager.WIFI_AP_STATE_DISABLED: 132 mWifiTetheringAvailabilityListener.onWifiTetheringUnavailable(); 133 mWifiTetheringAvailabilityListener.enablePreference(); 134 if (mRestartBooked) { 135 // Hotspot was disabled as part of a restart request - we can now re-enable it 136 mWifiTetheringAvailabilityListener.disablePreference(); 137 startTethering(); 138 mRestartBooked = false; 139 } 140 break; 141 default: 142 mWifiTetheringAvailabilityListener.onWifiTetheringUnavailable(); 143 mWifiTetheringAvailabilityListener.enablePreference(); 144 break; 145 } 146 } 147 startTethering()148 private void startTethering() { 149 mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI, 150 ConcurrentUtils.DIRECT_EXECUTOR, 151 new TetheringManager.StartTetheringCallback() { 152 @Override 153 public void onTetheringFailed(final int result) { 154 mWifiTetheringAvailabilityListener.onWifiTetheringUnavailable(); 155 mWifiTetheringAvailabilityListener.enablePreference(); 156 } 157 }); 158 } 159 stopTethering()160 private void stopTethering() { 161 mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI); 162 } 163 restartTethering()164 private void restartTethering() { 165 stopTethering(); 166 mRestartBooked = true; 167 } 168 169 /** 170 * Interface for receiving Wifi tethering status updates 171 */ 172 public interface WifiTetheringAvailabilityListener { 173 /** 174 * Callback for when Wifi tethering is available 175 */ onWifiTetheringAvailable()176 void onWifiTetheringAvailable(); 177 178 /** 179 * Callback for when Wifi tethering is unavailable 180 */ onWifiTetheringUnavailable()181 void onWifiTetheringUnavailable(); 182 183 /** 184 * Listener should allow further changes to Wifi tethering 185 */ enablePreference()186 void enablePreference(); 187 188 /** 189 * Listener should disallow further changes to Wifi tethering 190 */ disablePreference()191 void disablePreference(); 192 } 193 } 194