blob: c44b385f5057301704f757e40a03e36bf9d52c85 [file] [log] [blame]
Jason Monk51e4dc02014-07-22 12:00:47 -04001/*
2 * Copyright (C) 2014 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
17package com.android.systemui.statusbar.policy;
18
Yoshinori Hiranoadcbed72016-06-08 14:29:49 +090019import android.app.ActivityManager;
Jason Monk51e4dc02014-07-22 12:00:47 -040020import android.content.Context;
Jeremy Klein36c7aa02016-01-22 14:11:45 -080021import android.net.ConnectivityManager;
Jason Monk51e4dc02014-07-22 12:00:47 -040022import android.net.wifi.WifiManager;
Yoshinori Hiranoadcbed72016-06-08 14:29:49 +090023import android.os.UserManager;
Jason Monk51e4dc02014-07-22 12:00:47 -040024import android.util.Log;
25
Rohan Shahe4071122018-01-22 15:16:09 -080026import com.android.systemui.Dependency;
27
Jason Monkdd5bdc62015-07-20 12:18:38 -040028import java.io.FileDescriptor;
29import java.io.PrintWriter;
Jason Monk256a2262014-08-05 16:24:22 -040030import java.util.ArrayList;
31
Rohan Shahe4071122018-01-22 15:16:09 -080032public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
Jason Monk51e4dc02014-07-22 12:00:47 -040033
34 private static final String TAG = "HotspotController";
35 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jason Monke69e5f82014-12-10 17:24:18 -050036
Rohan Shahe4071122018-01-22 15:16:09 -080037 private final ArrayList<Callback> mCallbacks = new ArrayList<>();
Jeremy Klein36c7aa02016-01-22 14:11:45 -080038 private final ConnectivityManager mConnectivityManager;
Rohan Shahe4071122018-01-22 15:16:09 -080039 private final WifiManager mWifiManager;
Jason Monk51e4dc02014-07-22 12:00:47 -040040 private final Context mContext;
Jason Monkdd5bdc62015-07-20 12:18:38 -040041
42 private int mHotspotState;
Rohan Shahe4071122018-01-22 15:16:09 -080043 private int mNumConnectedDevices;
Amin Shaikhb5f01912018-09-19 11:25:59 -040044 private boolean mWaitingForTerminalState;
Jason Monk51e4dc02014-07-22 12:00:47 -040045
46 public HotspotControllerImpl(Context context) {
47 mContext = context;
Rohan Shahe4071122018-01-22 15:16:09 -080048 mConnectivityManager =
49 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
50 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Jason Monkdd5bdc62015-07-20 12:18:38 -040051 }
52
Jason Monk28680c62016-04-19 09:31:20 -040053 @Override
54 public boolean isHotspotSupported() {
55 return mConnectivityManager.isTetheringSupported()
Yoshinori Hiranoadcbed72016-06-08 14:29:49 +090056 && mConnectivityManager.getTetherableWifiRegexs().length != 0
57 && UserManager.get(mContext).isUserAdmin(ActivityManager.getCurrentUser());
Jason Monk28680c62016-04-19 09:31:20 -040058 }
59
Jason Monkdd5bdc62015-07-20 12:18:38 -040060 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
61 pw.println("HotspotController state:");
Amin Shaikhb5f01912018-09-19 11:25:59 -040062 pw.print(" mHotspotState="); pw.println(stateToString(mHotspotState));
63 pw.print(" mNumConnectedDevices="); pw.println(mNumConnectedDevices);
64 pw.print(" mWaitingForTerminalState="); pw.println(mWaitingForTerminalState);
Jason Monkdd5bdc62015-07-20 12:18:38 -040065 }
66
67 private static String stateToString(int hotspotState) {
68 switch (hotspotState) {
69 case WifiManager.WIFI_AP_STATE_DISABLED:
70 return "DISABLED";
71 case WifiManager.WIFI_AP_STATE_DISABLING:
72 return "DISABLING";
73 case WifiManager.WIFI_AP_STATE_ENABLED:
74 return "ENABLED";
75 case WifiManager.WIFI_AP_STATE_ENABLING:
76 return "ENABLING";
77 case WifiManager.WIFI_AP_STATE_FAILED:
78 return "FAILED";
79 }
80 return null;
Jason Monk51e4dc02014-07-22 12:00:47 -040081 }
82
Jeremy Klein36c7aa02016-01-22 14:11:45 -080083 @Override
Jason Monk51e4dc02014-07-22 12:00:47 -040084 public void addCallback(Callback callback) {
Jason Monkffc63f42016-03-16 15:22:15 -040085 synchronized (mCallbacks) {
86 if (callback == null || mCallbacks.contains(callback)) return;
87 if (DEBUG) Log.d(TAG, "addCallback " + callback);
88 mCallbacks.add(callback);
Rohan Shahe4071122018-01-22 15:16:09 -080089
90 updateWifiStateListeners(!mCallbacks.isEmpty());
Jason Monkffc63f42016-03-16 15:22:15 -040091 }
Jason Monk51e4dc02014-07-22 12:00:47 -040092 }
93
Jeremy Klein36c7aa02016-01-22 14:11:45 -080094 @Override
Jason Monk51e4dc02014-07-22 12:00:47 -040095 public void removeCallback(Callback callback) {
96 if (callback == null) return;
97 if (DEBUG) Log.d(TAG, "removeCallback " + callback);
Jason Monkffc63f42016-03-16 15:22:15 -040098 synchronized (mCallbacks) {
99 mCallbacks.remove(callback);
Rohan Shahe4071122018-01-22 15:16:09 -0800100 updateWifiStateListeners(!mCallbacks.isEmpty());
101 }
102 }
103
104 /**
105 * Updates the wifi state receiver to either start or stop listening to get updates to the
106 * hotspot status. Additionally starts listening to wifi manager state to track the number of
107 * connected devices.
108 *
109 * @param shouldListen whether we should start listening to various wifi statuses
110 */
111 private void updateWifiStateListeners(boolean shouldListen) {
Rohan Shahe4071122018-01-22 15:16:09 -0800112 if (shouldListen) {
113 mWifiManager.registerSoftApCallback(
114 this,
115 Dependency.get(Dependency.MAIN_HANDLER));
116 } else {
117 mWifiManager.unregisterSoftApCallback(this);
Jason Monkffc63f42016-03-16 15:22:15 -0400118 }
Jason Monk51e4dc02014-07-22 12:00:47 -0400119 }
120
121 @Override
122 public boolean isHotspotEnabled() {
Jason Monkdd5bdc62015-07-20 12:18:38 -0400123 return mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED;
Jason Monk51e4dc02014-07-22 12:00:47 -0400124 }
125
Jason Monke645aee2017-03-31 13:19:26 -0400126 @Override
127 public boolean isHotspotTransient() {
Amin Shaikhb5f01912018-09-19 11:25:59 -0400128 return mWaitingForTerminalState || (mHotspotState == WifiManager.WIFI_AP_STATE_ENABLING);
Jeremy Klein36c7aa02016-01-22 14:11:45 -0800129 }
130
Jason Monk20ef4022014-10-07 14:22:59 -0400131 @Override
Jason Monk51e4dc02014-07-22 12:00:47 -0400132 public void setHotspotEnabled(boolean enabled) {
Amin Shaikhb5f01912018-09-19 11:25:59 -0400133 if (mWaitingForTerminalState) {
134 if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for terminal state.");
Amin Shaikhd967f822018-06-19 17:58:35 -0400135 return;
136 }
Jeremy Klein36c7aa02016-01-22 14:11:45 -0800137 if (enabled) {
Amin Shaikhb5f01912018-09-19 11:25:59 -0400138 mWaitingForTerminalState = true;
Jason Monke645aee2017-03-31 13:19:26 -0400139 if (DEBUG) Log.d(TAG, "Starting tethering");
Amin Shaikhb5f01912018-09-19 11:25:59 -0400140 mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false,
141 new ConnectivityManager.OnStartTetheringCallback() {
142 @Override
143 public void onTetheringFailed() {
144 if (DEBUG) Log.d(TAG, "onTetheringFailed");
145 maybeResetSoftApState();
146 fireHotspotChangedCallback();
147 }
148 });
Sanket Padawe63224c32014-12-01 18:14:40 -0800149 } else {
Jeremy Klein36c7aa02016-01-22 14:11:45 -0800150 mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
Jason Monk256a2262014-08-05 16:24:22 -0400151 }
Jason Monk51e4dc02014-07-22 12:00:47 -0400152 }
153
Rohan Shahe4071122018-01-22 15:16:09 -0800154 @Override
155 public int getNumConnectedDevices() {
156 return mNumConnectedDevices;
157 }
158
159 /**
Amin Shaikhb5f01912018-09-19 11:25:59 -0400160 * Sends a hotspot changed callback.
161 * Be careful when calling over multiple threads, especially if one of them is the main thread
162 * (as it can be blocked).
Rohan Shahe4071122018-01-22 15:16:09 -0800163 */
Amin Shaikhb5f01912018-09-19 11:25:59 -0400164 private void fireHotspotChangedCallback() {
Jason Monkffc63f42016-03-16 15:22:15 -0400165 synchronized (mCallbacks) {
166 for (Callback callback : mCallbacks) {
Amin Shaikhb5f01912018-09-19 11:25:59 -0400167 callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices);
Jason Monkffc63f42016-03-16 15:22:15 -0400168 }
Jason Monk51e4dc02014-07-22 12:00:47 -0400169 }
170 }
171
Rohan Shahe4071122018-01-22 15:16:09 -0800172 @Override
173 public void onStateChanged(int state, int failureReason) {
Amin Shaikhb5f01912018-09-19 11:25:59 -0400174 // Update internal hotspot state for tracking before using any enabled/callback methods.
175 mHotspotState = state;
176
177 maybeResetSoftApState();
178 if (!isHotspotEnabled()) {
179 // Reset num devices if the hotspot is no longer enabled so we don't get ghost
180 // counters.
181 mNumConnectedDevices = 0;
182 }
183
184 fireHotspotChangedCallback();
185 }
186
187 private void maybeResetSoftApState() {
188 if (!mWaitingForTerminalState) {
189 return; // Only reset soft AP state if enabled from this controller.
190 }
191
192 switch (mHotspotState) {
193 case WifiManager.WIFI_AP_STATE_FAILED:
194 // TODO(b/110697252): must be called to reset soft ap state after failure
195 mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
196 // Fall through
197 case WifiManager.WIFI_AP_STATE_ENABLED:
198 case WifiManager.WIFI_AP_STATE_DISABLED:
199 mWaitingForTerminalState = false;
200 break;
201 case WifiManager.WIFI_AP_STATE_ENABLING:
202 case WifiManager.WIFI_AP_STATE_DISABLING:
203 default:
204 break;
205 }
Rohan Shahe4071122018-01-22 15:16:09 -0800206 }
207
208 @Override
209 public void onNumClientsChanged(int numConnectedDevices) {
210 mNumConnectedDevices = numConnectedDevices;
Amin Shaikhb5f01912018-09-19 11:25:59 -0400211 fireHotspotChangedCallback();
Jason Monk51e4dc02014-07-22 12:00:47 -0400212 }
213}