1 /* 2 * Copyright (C) 2019 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 package com.android.car.settings.wifi; 17 18 import android.content.BroadcastReceiver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.net.ConnectivityManager; 23 import android.net.TetheringManager; 24 import android.net.wifi.WifiManager; 25 import android.os.Bundle; 26 27 import androidx.annotation.XmlRes; 28 import androidx.localbroadcastmanager.content.LocalBroadcastManager; 29 30 import com.android.car.settings.R; 31 import com.android.car.settings.common.CarSettingActivities.WifiTetherActivity; 32 import com.android.car.settings.common.SettingsFragment; 33 import com.android.car.settings.network.NetworkUtils; 34 import com.android.car.settings.search.CarBaseSearchIndexProvider; 35 import com.android.car.ui.toolbar.MenuItem; 36 import com.android.internal.util.ConcurrentUtils; 37 import com.android.settingslib.search.SearchIndexable; 38 39 import java.util.Collections; 40 import java.util.List; 41 42 /** 43 * Fragment to host tethering-related preferences. 44 */ 45 @SearchIndexable 46 public class WifiTetherFragment extends SettingsFragment { 47 48 private CarWifiManager mCarWifiManager; 49 private TetheringManager mTetheringManager; 50 private MenuItem mTetherSwitch; 51 private boolean mRestartBooked = false; 52 53 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 54 @Override 55 public void onReceive(Context context, Intent intent) { 56 int state = intent.getIntExtra( 57 WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED); 58 handleWifiApStateChanged(state); 59 } 60 }; 61 private final BroadcastReceiver mRestartReceiver = new BroadcastReceiver() { 62 @Override 63 public void onReceive(Context context, Intent intent) { 64 if (mCarWifiManager != null && mCarWifiManager.isWifiApEnabled()) { 65 restartTethering(); 66 } 67 } 68 }; 69 70 @Override getToolbarMenuItems()71 public List<MenuItem> getToolbarMenuItems() { 72 return Collections.singletonList(mTetherSwitch); 73 } 74 75 @Override onCreate(Bundle savedInstanceState)76 public void onCreate(Bundle savedInstanceState) { 77 super.onCreate(savedInstanceState); 78 79 mTetherSwitch = new MenuItem.Builder(getContext()) 80 .setCheckable() 81 .setChecked(mCarWifiManager.isWifiApEnabled()) 82 .setOnClickListener(i -> { 83 if (!mTetherSwitch.isChecked()) { 84 stopTethering(); 85 } else { 86 startTethering(); 87 } 88 }) 89 .build(); 90 } 91 92 @Override 93 @XmlRes getPreferenceScreenResId()94 protected int getPreferenceScreenResId() { 95 return R.xml.wifi_tether_fragment; 96 } 97 98 @Override onAttach(Context context)99 public void onAttach(Context context) { 100 super.onAttach(context); 101 102 mCarWifiManager = new CarWifiManager(context); 103 mTetheringManager = getContext().getSystemService(TetheringManager.class); 104 } 105 106 @Override onStart()107 public void onStart() { 108 super.onStart(); 109 getContext().registerReceiver(mReceiver, 110 new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)); 111 LocalBroadcastManager.getInstance(getContext()).registerReceiver(mRestartReceiver, 112 new IntentFilter( 113 WifiTetherBasePreferenceController.ACTION_RESTART_WIFI_TETHERING)); 114 mCarWifiManager.start(); 115 } 116 117 @Override onStop()118 public void onStop() { 119 super.onStop(); 120 mCarWifiManager.stop(); 121 getContext().unregisterReceiver(mReceiver); 122 LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mRestartReceiver); 123 } 124 125 @Override onDestroy()126 public void onDestroy() { 127 super.onDestroy(); 128 mCarWifiManager.destroy(); 129 } 130 131 /** 132 * When the state of the hotspot changes, update the state of the tethering switch as well 133 */ handleWifiApStateChanged(int state)134 private void handleWifiApStateChanged(int state) { 135 switch (state) { 136 case WifiManager.WIFI_AP_STATE_ENABLING: 137 mTetherSwitch.setEnabled(false); 138 break; 139 case WifiManager.WIFI_AP_STATE_ENABLED: 140 mTetherSwitch.setEnabled(true); 141 if (!mTetherSwitch.isChecked()) { 142 mTetherSwitch.setChecked(true); 143 } 144 break; 145 case WifiManager.WIFI_AP_STATE_DISABLING: 146 mTetherSwitch.setEnabled(false); 147 if (mTetherSwitch.isChecked()) { 148 mTetherSwitch.setChecked(false); 149 } 150 break; 151 case WifiManager.WIFI_AP_STATE_DISABLED: 152 mTetherSwitch.setChecked(false); 153 mTetherSwitch.setEnabled(true); 154 if (mRestartBooked) { 155 // Hotspot was disabled as part of a restart request - we can now re-enable it 156 mTetherSwitch.setEnabled(false); 157 startTethering(); 158 mRestartBooked = false; 159 } 160 break; 161 default: 162 mTetherSwitch.setChecked(false); 163 mTetherSwitch.setEnabled(true); 164 break; 165 } 166 } 167 startTethering()168 private void startTethering() { 169 mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI, 170 ConcurrentUtils.DIRECT_EXECUTOR, 171 new TetheringManager.StartTetheringCallback() { 172 @Override 173 public void onTetheringFailed(final int result) { 174 mTetherSwitch.setChecked(false); 175 mTetherSwitch.setEnabled(true); 176 } 177 }); 178 } 179 stopTethering()180 private void stopTethering() { 181 mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI); 182 } 183 restartTethering()184 private void restartTethering() { 185 stopTethering(); 186 mRestartBooked = true; 187 } 188 189 /** 190 * Data provider for Settings Search. 191 */ 192 public static final CarBaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 193 new CarBaseSearchIndexProvider(R.xml.wifi_tether_fragment, 194 WifiTetherActivity.class) { 195 @Override 196 public boolean isPageSearchEnabled(Context context) { 197 return NetworkUtils.hasMobileNetwork( 198 context.getSystemService(ConnectivityManager.class)); 199 } 200 }; 201 } 202