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