1 /* 2 * Copyright (C) 2024 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.calling 18 19 import android.Manifest.permission.MODIFY_PHONE_STATE 20 import android.Manifest.permission.READ_PRECISE_PHONE_STATE 21 import android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE 22 import android.app.settings.SettingsEnums.ACTION_WIFI_CALLING 23 import android.content.Context 24 import android.telephony.SubscriptionManager 25 import android.telephony.TelephonyManager 26 import android.telephony.ims.ImsMmTelManager 27 import android.util.Log 28 import com.android.settings.R 29 import com.android.settings.contract.KEY_WIFI_CALLING 30 import com.android.settings.metrics.PreferenceActionMetricsProvider 31 import com.android.settings.network.ims.WifiCallingQueryImsState 32 import com.android.settings.network.telephony.wificalling.WifiCallingRepository 33 import com.android.settings.widget.SettingsMainSwitchPreference 34 import com.android.settings.wifi.calling.WifiCallingSettingsForSub.getCarrierActivityIntent 35 import com.android.settingslib.datastore.KeyValueStore 36 import com.android.settingslib.datastore.NoOpKeyedObservable 37 import com.android.settingslib.datastore.Permissions 38 import com.android.settingslib.datastore.and 39 import com.android.settingslib.metadata.BooleanValuePreference 40 import com.android.settingslib.metadata.PreferenceAvailabilityProvider 41 import com.android.settingslib.metadata.ReadWritePermit 42 import com.android.settingslib.metadata.SensitivityLevel 43 import com.android.settingslib.preference.BooleanValuePreferenceBinding 44 import kotlinx.coroutines.flow.first 45 import kotlinx.coroutines.runBlocking 46 47 /** 48 * Metadata of the "Use Wi-Fi calling" preference. 49 * 50 * TODO(b/372732219): apply metadata to UI 51 */ 52 class WifiCallingMainSwitchPreference(private val subId: Int) : 53 BooleanValuePreference, 54 BooleanValuePreferenceBinding, 55 PreferenceActionMetricsProvider, 56 PreferenceAvailabilityProvider { 57 58 override val key: String 59 get() = KEY 60 61 override val title: Int 62 get() = R.string.wifi_calling_main_switch_title 63 64 override val preferenceActionMetrics: Int 65 get() = ACTION_WIFI_CALLING 66 tagsnull67 override fun tags(context: Context) = arrayOf(KEY_WIFI_CALLING) 68 69 override fun isEnabled(context: Context) = 70 context.isCallStateIdle(subId) && 71 WifiCallingQueryImsState(context, subId).isAllowUserControl 72 73 override fun isAvailable(context: Context) = 74 SubscriptionManager.isValidSubscriptionId(subId) && 75 runBlocking { WifiCallingRepository(context, subId).wifiCallingReadyFlow().first() } 76 createWidgetnull77 override fun createWidget(context: Context) = SettingsMainSwitchPreference(context) 78 79 override fun getReadPermissions(context: Context) = 80 Permissions.anyOf(READ_PRIVILEGED_PHONE_STATE, READ_PRECISE_PHONE_STATE) 81 82 override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = 83 ReadWritePermit.ALLOW 84 85 override fun getWritePermissions(context: Context) = 86 Permissions.anyOf(READ_PRIVILEGED_PHONE_STATE, READ_PRECISE_PHONE_STATE) and 87 MODIFY_PHONE_STATE 88 89 override fun getWritePermit( 90 context: Context, 91 value: Boolean?, 92 callingPid: Int, 93 callingUid: Int, 94 ) = 95 when { 96 value == true && 97 (DisclaimerItemFactory.create(context, subId).isNotEmpty() || 98 getCarrierActivityIntent(context, subId) != null) -> 99 ReadWritePermit.REQUIRE_USER_AGREEMENT 100 else -> ReadWritePermit.ALLOW 101 } 102 103 override val sensitivityLevel 104 get() = SensitivityLevel.NO_SENSITIVITY 105 storagenull106 override fun storage(context: Context): KeyValueStore = WifiCallingStore(context, subId) 107 108 @Suppress("UNCHECKED_CAST") 109 private class WifiCallingStore(context: Context, private val subId: Int) : 110 NoOpKeyedObservable<String>(), KeyValueStore { 111 private val queryIms = WifiCallingQueryImsState(context, subId) 112 113 override fun contains(key: String) = key == KEY 114 115 override fun <T : Any> getValue(key: String, valueType: Class<T>) = 116 (queryIms.isEnabledByUser && queryIms.isAllowUserControl) as T 117 118 override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) { 119 if (value is Boolean) { 120 try { 121 ImsMmTelManager.createForSubscriptionId(subId).isVoWiFiSettingEnabled = value 122 } catch (e: Exception) { 123 Log.w(TAG, "fail to enable wifi calling", e) 124 } 125 } 126 } 127 } 128 129 companion object { 130 // TODO(b/372732219): The key is different from XML to avoid applying metadata to UI. 131 const val KEY = "wifi_calling_switch" 132 const val TAG = KEY 133 Contextnull134 private fun Context.isCallStateIdle(subId: Int) = 135 getSystemService(TelephonyManager::class.java)?.getCallState(subId) == 136 TelephonyManager.CALL_STATE_IDLE 137 } 138 } 139