1 /* 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 17 package com.android.systemui.statusbar.connectivity; 18 19 import android.content.Intent; 20 import android.os.UserHandle; 21 import android.os.UserManager; 22 import android.provider.Settings; 23 import android.util.IndentingPrintWriter; 24 import android.util.Log; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 import androidx.lifecycle.Lifecycle; 29 import androidx.lifecycle.LifecycleOwner; 30 import androidx.lifecycle.LifecycleRegistry; 31 32 import com.android.systemui.settings.UserTracker; 33 import com.android.wifitrackerlib.MergedCarrierEntry; 34 import com.android.wifitrackerlib.WifiEntry; 35 import com.android.wifitrackerlib.WifiPickerTracker; 36 37 import java.io.PrintWriter; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.List; 42 import java.util.concurrent.Executor; 43 44 /** */ 45 public class AccessPointControllerImpl implements AccessPointController, 46 WifiPickerTracker.WifiPickerTrackerCallback, 47 LifecycleOwner { 48 private static final String TAG = "AccessPointController"; 49 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 50 51 // This string extra specifies a network to open the connect dialog on, so the user can enter 52 // network credentials. This is used by quick settings for secured networks. 53 private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid"; 54 55 private static final int[] ICONS = WifiIcons.WIFI_FULL_ICONS; 56 57 private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>(); 58 private final UserManager mUserManager; 59 private final UserTracker mUserTracker; 60 private final Executor mMainExecutor; 61 62 private @Nullable WifiPickerTracker mWifiPickerTracker; 63 private WifiPickerTrackerFactory mWifiPickerTrackerFactory; 64 65 private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); 66 67 private int mCurrentUser; 68 AccessPointControllerImpl( UserManager userManager, UserTracker userTracker, Executor mainExecutor, WifiPickerTrackerFactory wifiPickerTrackerFactory )69 public AccessPointControllerImpl( 70 UserManager userManager, 71 UserTracker userTracker, 72 Executor mainExecutor, 73 WifiPickerTrackerFactory wifiPickerTrackerFactory 74 ) { 75 mUserManager = userManager; 76 mUserTracker = userTracker; 77 mCurrentUser = userTracker.getUserId(); 78 mMainExecutor = mainExecutor; 79 mWifiPickerTrackerFactory = wifiPickerTrackerFactory; 80 mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED)); 81 } 82 83 /** 84 * Initializes the controller. 85 * 86 * Will create a WifiPickerTracker associated to this controller. 87 */ init()88 public void init() { 89 if (mWifiPickerTracker == null) { 90 mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this, TAG); 91 } 92 } 93 94 @NonNull 95 @Override getLifecycle()96 public Lifecycle getLifecycle() { 97 return mLifecycle; 98 } 99 100 @Override finalize()101 protected void finalize() throws Throwable { 102 mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.DESTROYED)); 103 super.finalize(); 104 } 105 canConfigWifi()106 public boolean canConfigWifi() { 107 if (!mWifiPickerTrackerFactory.isSupported()) return false; 108 return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, 109 new UserHandle(mCurrentUser)); 110 } 111 canConfigMobileData()112 public boolean canConfigMobileData() { 113 return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, 114 UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin(); 115 } 116 onUserSwitched(int newUserId)117 void onUserSwitched(int newUserId) { 118 mCurrentUser = newUserId; 119 } 120 121 @Override addAccessPointCallback(AccessPointCallback callback)122 public void addAccessPointCallback(AccessPointCallback callback) { 123 if (callback == null || mCallbacks.contains(callback)) return; 124 if (DEBUG) Log.d(TAG, "addCallback " + callback); 125 mCallbacks.add(callback); 126 if (mCallbacks.size() == 1) { 127 mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED)); 128 } 129 } 130 131 @Override removeAccessPointCallback(AccessPointCallback callback)132 public void removeAccessPointCallback(AccessPointCallback callback) { 133 if (callback == null) return; 134 if (DEBUG) Log.d(TAG, "removeCallback " + callback); 135 mCallbacks.remove(callback); 136 if (mCallbacks.isEmpty()) { 137 mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED)); 138 } 139 } 140 141 @Override scanForAccessPoints()142 public void scanForAccessPoints() { 143 if (mWifiPickerTracker == null) { 144 fireAccessPointsCallback(Collections.emptyList()); 145 return; 146 } 147 List<WifiEntry> entries = mWifiPickerTracker.getWifiEntries(); 148 WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry(); 149 if (connectedEntry != null) { 150 entries.add(0, connectedEntry); 151 } 152 fireAccessPointsCallback(entries); 153 } 154 155 @Override getMergedCarrierEntry()156 public MergedCarrierEntry getMergedCarrierEntry() { 157 if (mWifiPickerTracker == null) { 158 fireAccessPointsCallback(Collections.emptyList()); 159 return null; 160 } 161 return mWifiPickerTracker.getMergedCarrierEntry(); 162 } 163 164 @Override getIcon(WifiEntry ap)165 public int getIcon(WifiEntry ap) { 166 int level = ap.getLevel(); 167 return ICONS[Math.max(0, level)]; 168 } 169 170 /** 171 * Connects to a {@link WifiEntry} if it's saved or does not require security. 172 * 173 * If the entry is not saved and requires security, will trigger 174 * {@link AccessPointCallback#onSettingsActivityTriggered}. 175 * @param ap 176 * @return {@code true} if {@link AccessPointCallback#onSettingsActivityTriggered} is triggered 177 */ connect(@ullable WifiEntry ap)178 public boolean connect(@Nullable WifiEntry ap) { 179 if (ap == null) return false; 180 if (DEBUG) { 181 if (ap.getWifiConfiguration() != null) { 182 Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId); 183 } else { 184 Log.d(TAG, "connect to unsaved network " + ap.getTitle()); 185 } 186 } 187 if (ap.isSaved()) { 188 ap.connect(mConnectCallback); 189 } else { 190 // Unknown network, need to add it. 191 if (ap.getSecurity() != WifiEntry.SECURITY_NONE) { 192 Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); 193 intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsid()); 194 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 195 fireSettingsIntentCallback(intent); 196 return true; 197 } else { 198 ap.connect(mConnectCallback); 199 } 200 } 201 return false; 202 } 203 fireSettingsIntentCallback(Intent intent)204 private void fireSettingsIntentCallback(Intent intent) { 205 for (AccessPointCallback callback : mCallbacks) { 206 callback.onSettingsActivityTriggered(intent); 207 } 208 } 209 fireAccessPointsCallback(List<WifiEntry> aps)210 private void fireAccessPointsCallback(List<WifiEntry> aps) { 211 for (AccessPointCallback callback : mCallbacks) { 212 callback.onAccessPointsChanged(aps); 213 } 214 } 215 fireWifiScanCallback(boolean isScan)216 private void fireWifiScanCallback(boolean isScan) { 217 for (AccessPointCallback callback : mCallbacks) { 218 callback.onWifiScan(isScan); 219 } 220 } 221 dump(PrintWriter pw)222 void dump(PrintWriter pw) { 223 IndentingPrintWriter ipw = new IndentingPrintWriter(pw); 224 ipw.println("AccessPointControllerImpl:"); 225 ipw.increaseIndent(); 226 ipw.println("Callbacks: " + Arrays.toString(mCallbacks.toArray())); 227 ipw.println("WifiPickerTracker: " + mWifiPickerTracker.toString()); 228 if (mWifiPickerTracker != null && !mCallbacks.isEmpty()) { 229 ipw.println("Connected: " + mWifiPickerTracker.getConnectedWifiEntry()); 230 ipw.println("Other wifi entries: " 231 + Arrays.toString(mWifiPickerTracker.getWifiEntries().toArray())); 232 } else if (mWifiPickerTracker != null) { 233 ipw.println("WifiPickerTracker not started, cannot get reliable entries"); 234 } 235 ipw.decreaseIndent(); 236 } 237 238 @Override onWifiStateChanged()239 public void onWifiStateChanged() { 240 scanForAccessPoints(); 241 } 242 243 @Override onWifiEntriesChanged()244 public void onWifiEntriesChanged() { 245 scanForAccessPoints(); 246 } 247 248 @Override onWifiEntriesChanged(@ifiPickerTracker.WifiEntriesChangedReason int reason)249 public void onWifiEntriesChanged(@WifiPickerTracker.WifiEntriesChangedReason int reason) { 250 onWifiEntriesChanged(); 251 if (reason == WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS) { 252 fireWifiScanCallback(false /* isScan */); 253 } 254 } 255 256 @Override onNumSavedNetworksChanged()257 public void onNumSavedNetworksChanged() { 258 // Do nothing 259 } 260 261 @Override onNumSavedSubscriptionsChanged()262 public void onNumSavedSubscriptionsChanged() { 263 // Do nothing 264 } 265 266 @Override onScanRequested()267 public void onScanRequested() { 268 fireWifiScanCallback(true /* isScan */); 269 } 270 271 private final WifiEntry.ConnectCallback mConnectCallback = new WifiEntry.ConnectCallback() { 272 @Override 273 public void onConnectResult(int status) { 274 if (status == CONNECT_STATUS_SUCCESS) { 275 if (DEBUG) Log.d(TAG, "connect success"); 276 } else { 277 if (DEBUG) Log.d(TAG, "connect failure reason=" + status); 278 } 279 } 280 }; 281 } 282