• 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.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