blob: b561ac1664c09b72e0c512c9aec758afc2cc3113 [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
Jason Monkb7d50a72018-12-21 13:03:17 -050019import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
20
Yoshinori Hiranoadcbed72016-06-08 14:29:49 +090021import android.app.ActivityManager;
Jason Monk51e4dc02014-07-22 12:00:47 -040022import android.content.Context;
Jeremy Klein36c7aa02016-01-22 14:11:45 -080023import android.net.ConnectivityManager;
Jason Monk51e4dc02014-07-22 12:00:47 -040024import android.net.wifi.WifiManager;
Jason Monkb7d50a72018-12-21 13:03:17 -050025import android.os.Handler;
Yoshinori Hiranoadcbed72016-06-08 14:29:49 +090026import android.os.UserManager;
Jason Monk51e4dc02014-07-22 12:00:47 -040027import android.util.Log;
28
Jason Monkdd5bdc62015-07-20 12:18:38 -040029import java.io.FileDescriptor;
30import java.io.PrintWriter;
Jason Monk256a2262014-08-05 16:24:22 -040031import java.util.ArrayList;
32
Jason Monk196d6392018-12-20 13:25:34 -050033import javax.inject.Inject;
Jason Monkb7d50a72018-12-21 13:03:17 -050034import javax.inject.Named;
Jason Monk196d6392018-12-20 13:25:34 -050035import javax.inject.Singleton;
36
37/**
38 */
39@Singleton
Rohan Shahe4071122018-01-22 15:16:09 -080040public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
Jason Monk51e4dc02014-07-22 12:00:47 -040041
42 private static final String TAG = "HotspotController";
43 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Jason Monke69e5f82014-12-10 17:24:18 -050044
Rohan Shahe4071122018-01-22 15:16:09 -080045 private final ArrayList<Callback> mCallbacks = new ArrayList<>();
Jeremy Klein36c7aa02016-01-22 14:11:45 -080046 private final ConnectivityManager mConnectivityManager;
Rohan Shahe4071122018-01-22 15:16:09 -080047 private final WifiManager mWifiManager;
Jason Monkb7d50a72018-12-21 13:03:17 -050048 private final Handler mMainHandler;
Jason Monk51e4dc02014-07-22 12:00:47 -040049 private final Context mContext;
Jason Monkdd5bdc62015-07-20 12:18:38 -040050
51 private int mHotspotState;
Rohan Shahe4071122018-01-22 15:16:09 -080052 private int mNumConnectedDevices;
Amin Shaikh94a7f022018-09-19 11:25:59 -040053 private boolean mWaitingForTerminalState;
Jason Monk51e4dc02014-07-22 12:00:47 -040054
Jason Monk196d6392018-12-20 13:25:34 -050055 /**
56 */
57 @Inject
Jason Monkb7d50a72018-12-21 13:03:17 -050058 public HotspotControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
Jason Monk51e4dc02014-07-22 12:00:47 -040059 mContext = context;
Rohan Shahe4071122018-01-22 15:16:09 -080060 mConnectivityManager =
61 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
62 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Jason Monkb7d50a72018-12-21 13:03:17 -050063 mMainHandler = mainHandler;
Jason Monkdd5bdc62015-07-20 12:18:38 -040064 }
65
Jason Monk28680c62016-04-19 09:31:20 -040066 @Override
67 public boolean isHotspotSupported() {
68 return mConnectivityManager.isTetheringSupported()
Yoshinori Hiranoadcbed72016-06-08 14:29:49 +090069 && mConnectivityManager.getTetherableWifiRegexs().length != 0
70 && UserManager.get(mContext).isUserAdmin(ActivityManager.getCurrentUser());
Jason Monk28680c62016-04-19 09:31:20 -040071 }
72
Jason Monkdd5bdc62015-07-20 12:18:38 -040073 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
74 pw.println("HotspotController state:");
Amin Shaikh94a7f022018-09-19 11:25:59 -040075 pw.print(" mHotspotState="); pw.println(stateToString(mHotspotState));
76 pw.print(" mNumConnectedDevices="); pw.println(mNumConnectedDevices);
77 pw.print(" mWaitingForTerminalState="); pw.println(mWaitingForTerminalState);
Jason Monkdd5bdc62015-07-20 12:18:38 -040078 }
79
80 private static String stateToString(int hotspotState) {
81 switch (hotspotState) {
82 case WifiManager.WIFI_AP_STATE_DISABLED:
83 return "DISABLED";
84 case WifiManager.WIFI_AP_STATE_DISABLING:
85 return "DISABLING";
86 case WifiManager.WIFI_AP_STATE_ENABLED:
87 return "ENABLED";
88 case WifiManager.WIFI_AP_STATE_ENABLING:
89 return "ENABLING";
90 case WifiManager.WIFI_AP_STATE_FAILED:
91 return "FAILED";
92 }
93 return null;
Jason Monk51e4dc02014-07-22 12:00:47 -040094 }
95
Jeremy Klein36c7aa02016-01-22 14:11:45 -080096 @Override
Jason Monk51e4dc02014-07-22 12:00:47 -040097 public void addCallback(Callback callback) {
Jason Monkffc63f42016-03-16 15:22:15 -040098 synchronized (mCallbacks) {
99 if (callback == null || mCallbacks.contains(callback)) return;
100 if (DEBUG) Log.d(TAG, "addCallback " + callback);
101 mCallbacks.add(callback);
Rohan Shahe4071122018-01-22 15:16:09 -0800102
103 updateWifiStateListeners(!mCallbacks.isEmpty());
Jason Monkffc63f42016-03-16 15:22:15 -0400104 }
Jason Monk51e4dc02014-07-22 12:00:47 -0400105 }
106
Jeremy Klein36c7aa02016-01-22 14:11:45 -0800107 @Override
Jason Monk51e4dc02014-07-22 12:00:47 -0400108 public void removeCallback(Callback callback) {
109 if (callback == null) return;
110 if (DEBUG) Log.d(TAG, "removeCallback " + callback);
Jason Monkffc63f42016-03-16 15:22:15 -0400111 synchronized (mCallbacks) {
112 mCallbacks.remove(callback);
Rohan Shahe4071122018-01-22 15:16:09 -0800113 updateWifiStateListeners(!mCallbacks.isEmpty());
114 }
115 }
116
117 /**
118 * Updates the wifi state receiver to either start or stop listening to get updates to the
119 * hotspot status. Additionally starts listening to wifi manager state to track the number of
120 * connected devices.
121 *
122 * @param shouldListen whether we should start listening to various wifi statuses
123 */
124 private void updateWifiStateListeners(boolean shouldListen) {
Amin Shaikhbc38add2018-12-26 15:31:45 -0500125 if (mWifiManager == null) {
126 return;
127 }
Rohan Shahe4071122018-01-22 15:16:09 -0800128 if (shouldListen) {
129 mWifiManager.registerSoftApCallback(
130 this,
Jason Monkb7d50a72018-12-21 13:03:17 -0500131 mMainHandler);
Rohan Shahe4071122018-01-22 15:16:09 -0800132 } else {
133 mWifiManager.unregisterSoftApCallback(this);
Jason Monkffc63f42016-03-16 15:22:15 -0400134 }
Jason Monk51e4dc02014-07-22 12:00:47 -0400135 }
136
137 @Override
138 public boolean isHotspotEnabled() {
Jason Monkdd5bdc62015-07-20 12:18:38 -0400139 return mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED;
Jason Monk51e4dc02014-07-22 12:00:47 -0400140 }
141
Jason Monke645aee2017-03-31 13:19:26 -0400142 @Override
143 public boolean isHotspotTransient() {
Amin Shaikh94a7f022018-09-19 11:25:59 -0400144 return mWaitingForTerminalState || (mHotspotState == WifiManager.WIFI_AP_STATE_ENABLING);
Jeremy Klein36c7aa02016-01-22 14:11:45 -0800145 }
146
Jason Monk20ef4022014-10-07 14:22:59 -0400147 @Override
Jason Monk51e4dc02014-07-22 12:00:47 -0400148 public void setHotspotEnabled(boolean enabled) {
Amin Shaikh94a7f022018-09-19 11:25:59 -0400149 if (mWaitingForTerminalState) {
150 if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for terminal state.");
Amin Shaikhaa4735f2018-06-19 17:58:35 -0400151 return;
152 }
Jeremy Klein36c7aa02016-01-22 14:11:45 -0800153 if (enabled) {
Amin Shaikh94a7f022018-09-19 11:25:59 -0400154 mWaitingForTerminalState = true;
Jason Monke645aee2017-03-31 13:19:26 -0400155 if (DEBUG) Log.d(TAG, "Starting tethering");
Amin Shaikh94a7f022018-09-19 11:25:59 -0400156 mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false,
157 new ConnectivityManager.OnStartTetheringCallback() {
158 @Override
159 public void onTetheringFailed() {
160 if (DEBUG) Log.d(TAG, "onTetheringFailed");
161 maybeResetSoftApState();
162 fireHotspotChangedCallback();
163 }
164 });
Sanket Padawe63224c32014-12-01 18:14:40 -0800165 } else {
Jeremy Klein36c7aa02016-01-22 14:11:45 -0800166 mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
Jason Monk256a2262014-08-05 16:24:22 -0400167 }
Jason Monk51e4dc02014-07-22 12:00:47 -0400168 }
169
Rohan Shahe4071122018-01-22 15:16:09 -0800170 @Override
171 public int getNumConnectedDevices() {
172 return mNumConnectedDevices;
173 }
174
175 /**
Amin Shaikh94a7f022018-09-19 11:25:59 -0400176 * Sends a hotspot changed callback.
177 * Be careful when calling over multiple threads, especially if one of them is the main thread
178 * (as it can be blocked).
Rohan Shahe4071122018-01-22 15:16:09 -0800179 */
Amin Shaikh94a7f022018-09-19 11:25:59 -0400180 private void fireHotspotChangedCallback() {
Jason Monkffc63f42016-03-16 15:22:15 -0400181 synchronized (mCallbacks) {
182 for (Callback callback : mCallbacks) {
Amin Shaikh94a7f022018-09-19 11:25:59 -0400183 callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices);
Jason Monkffc63f42016-03-16 15:22:15 -0400184 }
Jason Monk51e4dc02014-07-22 12:00:47 -0400185 }
186 }
187
Rohan Shahe4071122018-01-22 15:16:09 -0800188 @Override
189 public void onStateChanged(int state, int failureReason) {
Amin Shaikh94a7f022018-09-19 11:25:59 -0400190 // Update internal hotspot state for tracking before using any enabled/callback methods.
191 mHotspotState = state;
192
193 maybeResetSoftApState();
194 if (!isHotspotEnabled()) {
195 // Reset num devices if the hotspot is no longer enabled so we don't get ghost
196 // counters.
197 mNumConnectedDevices = 0;
198 }
199
200 fireHotspotChangedCallback();
201 }
202
203 private void maybeResetSoftApState() {
204 if (!mWaitingForTerminalState) {
205 return; // Only reset soft AP state if enabled from this controller.
206 }
207 switch (mHotspotState) {
208 case WifiManager.WIFI_AP_STATE_FAILED:
209 // TODO(b/110697252): must be called to reset soft ap state after failure
210 mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
211 // Fall through
212 case WifiManager.WIFI_AP_STATE_ENABLED:
213 case WifiManager.WIFI_AP_STATE_DISABLED:
214 mWaitingForTerminalState = false;
215 break;
216 case WifiManager.WIFI_AP_STATE_ENABLING:
217 case WifiManager.WIFI_AP_STATE_DISABLING:
218 default:
219 break;
220 }
Rohan Shahe4071122018-01-22 15:16:09 -0800221 }
222
223 @Override
224 public void onNumClientsChanged(int numConnectedDevices) {
225 mNumConnectedDevices = numConnectedDevices;
Amin Shaikh94a7f022018-09-19 11:25:59 -0400226 fireHotspotChangedCallback();
Jason Monk51e4dc02014-07-22 12:00:47 -0400227 }
228}