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