1 /*
2  * Copyright (C) 2020 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.settings.network;
17 
18 import static android.os.UserManager.DISALLOW_CONFIG_TETHERING;
19 
20 import static com.android.settings.network.TetherEnabler.TETHERING_BLUETOOTH_ON;
21 import static com.android.settings.network.TetherEnabler.TETHERING_ETHERNET_ON;
22 import static com.android.settings.network.TetherEnabler.TETHERING_OFF;
23 import static com.android.settings.network.TetherEnabler.TETHERING_USB_ON;
24 import static com.android.settings.network.TetherEnabler.TETHERING_WIFI_ON;
25 import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfRestrictionEnforced;
26 
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.BluetoothPan;
29 import android.bluetooth.BluetoothProfile;
30 import android.content.Context;
31 import android.os.UserHandle;
32 import android.util.FeatureFlagUtils;
33 import android.util.Log;
34 
35 import androidx.annotation.VisibleForTesting;
36 import androidx.lifecycle.Lifecycle;
37 import androidx.lifecycle.Lifecycle.Event;
38 import androidx.lifecycle.LifecycleObserver;
39 import androidx.lifecycle.OnLifecycleEvent;
40 import androidx.preference.PreferenceScreen;
41 
42 import com.android.settings.R;
43 import com.android.settings.core.BasePreferenceController;
44 import com.android.settings.core.FeatureFlags;
45 import com.android.settings.widget.MasterSwitchController;
46 import com.android.settings.widget.MasterSwitchPreference;
47 import com.android.settingslib.TetherUtil;
48 
49 import java.util.concurrent.atomic.AtomicReference;
50 
51 /**
52  * This controller helps to manage the switch state and visibility of "Hotspot & tethering" switch
53  * preference. It updates the preference summary text based on tethering state.
54  */
55 public class AllInOneTetherPreferenceController extends BasePreferenceController implements
56         LifecycleObserver, TetherEnabler.OnTetherStateUpdateListener {
57     private static final String TAG = "AllInOneTetherPreferenceController";
58 
59     private int mTetheringState;
60 
61     private final boolean mAdminDisallowedTetherConfig;
62     private final AtomicReference<BluetoothPan> mBluetoothPan;
63     private final BluetoothAdapter mBluetoothAdapter;
64     @VisibleForTesting
65     final BluetoothProfile.ServiceListener mBtProfileServiceListener =
66             new BluetoothProfile.ServiceListener() {
67                 @Override
68                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
69                     mBluetoothPan.set((BluetoothPan) proxy);
70                 }
71 
72                 @Override
73                 public void onServiceDisconnected(int profile) {
74                     mBluetoothPan.set(null);
75                 }
76             };
77 
78     private MasterSwitchPreference mPreference;
79     private TetherEnabler mTetherEnabler;
80 
81     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
AllInOneTetherPreferenceController()82     AllInOneTetherPreferenceController() {
83         super(null /*context*/, "test");
84         mAdminDisallowedTetherConfig = false;
85         mBluetoothPan = new AtomicReference<>();
86         mBluetoothAdapter = null;
87     }
88 
AllInOneTetherPreferenceController(Context context, String key)89     public AllInOneTetherPreferenceController(Context context, String key) {
90         super(context, key);
91         mBluetoothPan = new AtomicReference<>();
92         mAdminDisallowedTetherConfig = checkIfRestrictionEnforced(
93                 context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
94         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
95     }
96 
97     @Override
displayPreference(PreferenceScreen screen)98     public void displayPreference(PreferenceScreen screen) {
99         super.displayPreference(screen);
100         mPreference = screen.findPreference(mPreferenceKey);
101     }
102 
103     @Override
getAvailabilityStatus()104     public int getAvailabilityStatus() {
105         if (!TetherUtil.isTetherAvailable(mContext)
106                 || !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) {
107             return CONDITIONALLY_UNAVAILABLE;
108         } else {
109             return AVAILABLE;
110         }
111     }
112 
113     @Override
getSummary()114     public CharSequence getSummary() {
115         switch (mTetheringState) {
116             case TETHERING_OFF:
117                 return mContext.getString(R.string.tether_settings_summary_off);
118             case TETHERING_WIFI_ON:
119                 return mContext.getString(R.string.tether_settings_summary_hotspot_only);
120             case TETHERING_USB_ON:
121                 return mContext.getString(R.string.tether_settings_summary_usb_tethering_only);
122             case TETHERING_BLUETOOTH_ON:
123                 return mContext.getString(
124                         R.string.tether_settings_summary_bluetooth_tethering_only);
125             case TETHERING_ETHERNET_ON:
126                 return mContext.getString(R.string.tether_settings_summary_ethernet_tethering_only);
127             case TETHERING_WIFI_ON | TETHERING_USB_ON:
128                 return mContext.getString(R.string.tether_settings_summary_hotspot_and_usb);
129             case TETHERING_WIFI_ON | TETHERING_BLUETOOTH_ON:
130                 return mContext.getString(R.string.tether_settings_summary_hotspot_and_bluetooth);
131             case TETHERING_WIFI_ON | TETHERING_ETHERNET_ON:
132                 return mContext.getString(R.string.tether_settings_summary_hotspot_and_ethernet);
133             case TETHERING_USB_ON | TETHERING_BLUETOOTH_ON:
134                 return mContext.getString(R.string.tether_settings_summary_usb_and_bluetooth);
135             case TETHERING_USB_ON | TETHERING_ETHERNET_ON:
136                 return mContext.getString(R.string.tether_settings_summary_usb_and_ethernet);
137             case TETHERING_BLUETOOTH_ON | TETHERING_ETHERNET_ON:
138                 return mContext.getString(R.string.tether_settings_summary_bluetooth_and_ethernet);
139             case TETHERING_WIFI_ON | TETHERING_USB_ON | TETHERING_BLUETOOTH_ON:
140                 return mContext.getString(
141                         R.string.tether_settings_summary_hotspot_and_usb_and_bluetooth);
142             case TETHERING_WIFI_ON | TETHERING_USB_ON | TETHERING_ETHERNET_ON:
143                 return mContext.getString(
144                         R.string.tether_settings_summary_hotspot_and_usb_and_ethernet);
145             case TETHERING_WIFI_ON | TETHERING_BLUETOOTH_ON | TETHERING_ETHERNET_ON:
146                 return mContext.getString(
147                         R.string.tether_settings_summary_hotspot_and_bluetooth_and_ethernet);
148             case TETHERING_USB_ON | TETHERING_BLUETOOTH_ON | TETHERING_ETHERNET_ON:
149                 return mContext.getString(
150                         R.string.tether_settings_summary_usb_and_bluetooth_and_ethernet);
151             case TETHERING_WIFI_ON | TETHERING_USB_ON | TETHERING_BLUETOOTH_ON
152                     | TETHERING_ETHERNET_ON:
153                 return mContext.getString(R.string.tether_settings_summary_all);
154             default:
155                 Log.e(TAG, "Unknown tethering state");
156                 return mContext.getString(R.string.summary_placeholder);
157         }
158     }
159 
160     @OnLifecycleEvent(Event.ON_CREATE)
onCreate()161     public void onCreate() {
162         if (mBluetoothAdapter != null
163                 && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
164             mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener,
165                         BluetoothProfile.PAN);
166         }
167     }
168 
169     @OnLifecycleEvent(Event.ON_RESUME)
onResume()170     public void onResume() {
171         if (mTetherEnabler != null) {
172             mTetherEnabler.addListener(this);
173         }
174     }
175 
176     @OnLifecycleEvent(Event.ON_PAUSE)
onPause()177     public void onPause() {
178         if (mTetherEnabler != null) {
179             mTetherEnabler.removeListener(this);
180         }
181     }
182 
183     @OnLifecycleEvent(Event.ON_DESTROY)
onDestroy()184     public void onDestroy() {
185         final BluetoothProfile profile = mBluetoothPan.getAndSet(null);
186         if (profile != null && mBluetoothAdapter != null) {
187             mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile);
188         }
189     }
190 
initEnabler(Lifecycle lifecycle)191     void initEnabler(Lifecycle lifecycle) {
192         if (mPreference != null) {
193             mTetherEnabler = new TetherEnabler(
194                     mContext, new MasterSwitchController(mPreference), mBluetoothPan);
195             if (lifecycle != null) {
196                 lifecycle.addObserver(mTetherEnabler);
197             }
198         } else {
199             Log.e(TAG, "TetherEnabler is not initialized");
200         }
201     }
202 
203     @Override
onTetherStateUpdated(@etherEnabler.TetheringState int state)204     public void onTetherStateUpdated(@TetherEnabler.TetheringState int state) {
205         mTetheringState = state;
206         updateState(mPreference);
207     }
208 }
209