1 /* 2 * Copyright (C) 2017 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.settings.wifi.tether; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.net.TetheringManager; 22 import android.net.wifi.SoftApConfiguration; 23 import android.net.wifi.WifiClient; 24 import android.net.wifi.WifiManager; 25 import android.text.BidiFormatter; 26 27 import androidx.annotation.VisibleForTesting; 28 import androidx.preference.Preference; 29 import androidx.preference.PreferenceScreen; 30 31 import com.android.settings.R; 32 import com.android.settings.Utils; 33 import com.android.settings.core.PreferenceControllerMixin; 34 import com.android.settingslib.core.AbstractPreferenceController; 35 import com.android.settingslib.core.lifecycle.Lifecycle; 36 import com.android.settingslib.core.lifecycle.LifecycleObserver; 37 import com.android.settingslib.core.lifecycle.events.OnStart; 38 import com.android.settingslib.core.lifecycle.events.OnStop; 39 40 import java.util.List; 41 42 public class WifiTetherPreferenceController extends AbstractPreferenceController 43 implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop { 44 45 private static final String WIFI_TETHER_SETTINGS = "wifi_tether"; 46 47 private final TetheringManager mTetheringManager; 48 private final String[] mWifiRegexs; 49 private final WifiManager mWifiManager; 50 private final Lifecycle mLifecycle; 51 private int mSoftApState; 52 @VisibleForTesting 53 Preference mPreference; 54 @VisibleForTesting 55 WifiTetherSoftApManager mWifiTetherSoftApManager; 56 WifiTetherPreferenceController(Context context, Lifecycle lifecycle)57 public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) { 58 this(context, lifecycle, true /* initSoftApManager */); 59 } 60 61 @VisibleForTesting WifiTetherPreferenceController(Context context, Lifecycle lifecycle, boolean initSoftApManager)62 WifiTetherPreferenceController(Context context, Lifecycle lifecycle, 63 boolean initSoftApManager) { 64 super(context); 65 mTetheringManager = context.getSystemService(TetheringManager.class); 66 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 67 mWifiRegexs = mTetheringManager.getTetherableWifiRegexs(); 68 mLifecycle = lifecycle; 69 if (lifecycle != null) { 70 lifecycle.addObserver(this); 71 } 72 if (initSoftApManager) { 73 initWifiTetherSoftApManager(); 74 } 75 } 76 77 @Override isAvailable()78 public boolean isAvailable() { 79 return mWifiRegexs != null 80 && mWifiRegexs.length != 0 81 && !Utils.isMonkeyRunning(); 82 } 83 84 @Override displayPreference(PreferenceScreen screen)85 public void displayPreference(PreferenceScreen screen) { 86 super.displayPreference(screen); 87 mPreference = screen.findPreference(WIFI_TETHER_SETTINGS); 88 if (mPreference == null) { 89 // unavailable 90 return; 91 } 92 } 93 94 @Override getPreferenceKey()95 public String getPreferenceKey() { 96 return WIFI_TETHER_SETTINGS; 97 } 98 99 @Override onStart()100 public void onStart() { 101 if (mPreference != null) { 102 if (mWifiTetherSoftApManager != null) { 103 mWifiTetherSoftApManager.registerSoftApCallback(); 104 } 105 } 106 } 107 108 @Override onStop()109 public void onStop() { 110 if (mPreference != null) { 111 if (mWifiTetherSoftApManager != null) { 112 mWifiTetherSoftApManager.unRegisterSoftApCallback(); 113 } 114 } 115 } 116 117 @VisibleForTesting initWifiTetherSoftApManager()118 void initWifiTetherSoftApManager() { 119 // This manager only handles the number of connected devices, other parts are handled by 120 // normal BroadcastReceiver in this controller 121 mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager, 122 new WifiTetherSoftApManager.WifiTetherSoftApCallback() { 123 @Override 124 public void onStateChanged(int state, int failureReason) { 125 mSoftApState = state; 126 handleWifiApStateChanged(state, failureReason); 127 } 128 129 @Override 130 public void onConnectedClientsChanged(List<WifiClient> clients) { 131 if (mPreference != null 132 && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) { 133 // Only show the number of clients when state is on 134 mPreference.setSummary(mContext.getResources().getQuantityString( 135 R.plurals.wifi_tether_connected_summary, clients.size(), 136 clients.size())); 137 } 138 } 139 }); 140 } 141 142 @VisibleForTesting handleWifiApStateChanged(int state, int reason)143 void handleWifiApStateChanged(int state, int reason) { 144 switch (state) { 145 case WifiManager.WIFI_AP_STATE_ENABLING: 146 mPreference.setSummary(R.string.wifi_tether_starting); 147 break; 148 case WifiManager.WIFI_AP_STATE_ENABLED: 149 final SoftApConfiguration softApConfig = mWifiManager.getSoftApConfiguration(); 150 updateConfigSummary(softApConfig); 151 break; 152 case WifiManager.WIFI_AP_STATE_DISABLING: 153 mPreference.setSummary(R.string.wifi_tether_stopping); 154 break; 155 case WifiManager.WIFI_AP_STATE_DISABLED: 156 mPreference.setSummary(R.string.wifi_hotspot_off_subtext); 157 break; 158 default: 159 if (reason == WifiManager.SAP_START_FAILURE_NO_CHANNEL) { 160 mPreference.setSummary(R.string.wifi_sap_no_channel_error); 161 } else { 162 mPreference.setSummary(R.string.wifi_error); 163 } 164 } 165 } 166 updateConfigSummary(@onNull SoftApConfiguration softApConfig)167 private void updateConfigSummary(@NonNull SoftApConfiguration softApConfig) { 168 if (softApConfig == null) { 169 // Should never happen. 170 return; 171 } 172 mPreference.setSummary(mContext.getString(R.string.wifi_tether_enabled_subtext, 173 BidiFormatter.getInstance().unicodeWrap(softApConfig.getSsid()))); 174 } 175 } 176