1 /*
2  * Copyright (C) 2023 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.network
18 
19 import android.app.settings.SettingsEnums
20 import android.content.Context
21 import android.os.Looper
22 import android.os.UserManager
23 import android.util.Log
24 import android.widget.Toast
25 import androidx.annotation.VisibleForTesting
26 import androidx.compose.material3.Text
27 import androidx.compose.runtime.Composable
28 import androidx.compose.runtime.rememberCoroutineScope
29 import androidx.compose.ui.res.stringResource
30 import com.android.settings.R
31 import com.android.settings.ResetNetworkRequest
32 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
33 import com.android.settings.spa.preference.ComposePreferenceController
34 import com.android.settingslib.spa.widget.dialog.AlertDialogButton
35 import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
36 import com.android.settingslib.spa.widget.preference.PreferenceModel
37 import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
38 import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
39 import kotlinx.coroutines.CoroutineScope
40 import kotlinx.coroutines.Dispatchers
41 import kotlinx.coroutines.launch
42 import kotlinx.coroutines.withContext
43 
44 /**
45  * This is to show a preference regarding resetting Bluetooth and Wi-Fi.
46  */
47 class BluetoothWiFiResetPreferenceController(context: Context, preferenceKey: String) :
48     ComposePreferenceController(context, preferenceKey) {
49 
50     private val restrictionChecker = NetworkResetRestrictionChecker(context)
51 
getAvailabilityStatusnull52     override fun getAvailabilityStatus() =
53         if (restrictionChecker.hasUserRestriction()) CONDITIONALLY_UNAVAILABLE else AVAILABLE
54 
55     @Composable
56     override fun Content() {
57         val coroutineScope = rememberCoroutineScope()
58         val dialogPresenter = rememberAlertDialogPresenter(
59             confirmButton = AlertDialogButton(
60                 text = stringResource(R.string.reset_bluetooth_wifi_button_text),
61             ) { reset(coroutineScope) },
62             dismissButton = AlertDialogButton(text = stringResource(R.string.cancel)),
63             title = stringResource(R.string.reset_bluetooth_wifi_title),
64         ) {
65             Text(stringResource(R.string.reset_bluetooth_wifi_desc))
66         }
67 
68         RestrictedPreference(
69             model = object : PreferenceModel {
70                 override val title = stringResource(R.string.reset_bluetooth_wifi_title)
71                 override val onClick = dialogPresenter::open
72             },
73             restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_NETWORK_RESET)),
74         )
75     }
76 
77     /**
78      * User pressed confirmation button, for starting reset operation.
79      */
resetnull80     private fun reset(coroutineScope: CoroutineScope) {
81         // User confirm the reset operation
82         featureFactory.metricsFeatureProvider
83             .action(mContext, SettingsEnums.RESET_BLUETOOTH_WIFI_CONFIRM, true)
84 
85         // Run reset in background thread
86         coroutineScope.launch {
87             try {
88                 withContext(Dispatchers.Default) {
89                     resetOperation().run()
90                 }
91             } catch (e: Exception) {
92                 Log.e(TAG, "Exception during reset", e)
93                 return@launch
94             }
95             Toast.makeText(
96                 mContext,
97                 R.string.reset_bluetooth_wifi_complete_toast,
98                 Toast.LENGTH_SHORT,
99             ).show()
100         }
101     }
102 
103     @VisibleForTesting
resetOperationnull104     fun resetOperation(): Runnable = if (SubscriptionUtil.isSimHardwareVisible(mContext)) {
105         ResetNetworkRequest(
106             ResetNetworkRequest.RESET_WIFI_MANAGER or
107                 ResetNetworkRequest.RESET_WIFI_P2P_MANAGER or
108                 ResetNetworkRequest.RESET_BLUETOOTH_MANAGER
109         )
110             .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
111     } else {  // For device without SIMs visible to the user
112         ResetNetworkRequest(
113             ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER or
114                 ResetNetworkRequest.RESET_VPN_MANAGER or
115                 ResetNetworkRequest.RESET_WIFI_MANAGER or
116                 ResetNetworkRequest.RESET_WIFI_P2P_MANAGER or
117                 ResetNetworkRequest.RESET_BLUETOOTH_MANAGER
118         )
119             .toResetNetworkOperationBuilder(mContext, Looper.getMainLooper())
120             .resetTelephonyAndNetworkPolicyManager(ResetNetworkRequest.ALL_SUBSCRIPTION_ID)
121     }.build()
122 
123     private companion object {
124         private const val TAG = "BluetoothWiFiResetPref"
125     }
126 }
127