1 /* 2 * Copyright (C) 2018 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.telephony; 18 19 import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; 20 21 import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; 22 23 import android.app.PendingIntent; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.net.Uri; 27 import android.os.PersistableBundle; 28 import android.telephony.CarrierConfigManager; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.ims.ImsMmTelManager; 31 import android.util.Log; 32 33 import androidx.annotation.VisibleForTesting; 34 import androidx.core.graphics.drawable.IconCompat; 35 import androidx.slice.Slice; 36 import androidx.slice.builders.ListBuilder; 37 import androidx.slice.builders.ListBuilder.RowBuilder; 38 import androidx.slice.builders.SliceAction; 39 40 import com.android.settings.R; 41 import com.android.settings.Utils; 42 import com.android.settings.network.ims.VolteQueryImsState; 43 import com.android.settings.slices.CustomSliceRegistry; 44 import com.android.settings.slices.SliceBroadcastReceiver; 45 46 /** 47 * Helper class to control slices for enhanced 4g LTE settings. 48 */ 49 public class Enhanced4gLteSliceHelper { 50 51 private static final String TAG = "Enhanced4gLteSlice"; 52 53 /** 54 * Action passed for changes to enhanced 4g LTE slice (toggle). 55 */ 56 public static final String ACTION_ENHANCED_4G_LTE_CHANGED = 57 "com.android.settings.mobilenetwork.action.ENHANCED_4G_LTE_CHANGED"; 58 59 /** 60 * Action for mobile network settings activity which 61 * allows setting configuration for Enhanced 4G LTE 62 * related settings 63 */ 64 public static final String ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY = 65 "android.settings.NETWORK_OPERATOR_SETTINGS"; 66 67 private final Context mContext; 68 69 /** 70 * Phone package name 71 */ 72 private static final String PACKAGE_PHONE = "com.android.phone"; 73 74 /** 75 * String resource type 76 */ 77 private static final String RESOURCE_TYPE_STRING = "string"; 78 79 /** 80 * Enhanced 4g lte mode title variant resource name 81 */ 82 private static final String RESOURCE_ENHANCED_4G_LTE_MODE_TITLE_VARIANT = 83 "enhanced_4g_lte_mode_title_variant"; 84 85 private static final int MODE_VOLTE = 0; 86 private static final int MODE_ADVANCED_CALL = 1; 87 private static final int MODE_4G_CALLING = 2; 88 89 @VisibleForTesting Enhanced4gLteSliceHelper(Context context)90 public Enhanced4gLteSliceHelper(Context context) { 91 mContext = context; 92 } 93 94 /** 95 * Returns Slice object for enhanced_4g_lte settings. 96 * 97 * If enhanced 4g LTE is not supported for the current carrier, this method will return slice 98 * with not supported message. 99 * 100 * If enhanced 4g LTE is not editable for the current carrier, this method will return slice 101 * with not editable message. 102 * 103 * If enhanced 4g LTE setting can be changed, this method will return the slice to toggle 104 * enhanced 4g LTE option with ACTION_ENHANCED_4G_LTE_CHANGED as endItem. 105 */ createEnhanced4gLteSlice(Uri sliceUri)106 public Slice createEnhanced4gLteSlice(Uri sliceUri) { 107 final int subId = getDefaultVoiceSubId(); 108 109 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 110 Log.d(TAG, "Invalid subscription Id"); 111 return null; 112 } 113 114 if (isCarrierConfigManagerKeyEnabled( 115 CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL, subId, false) 116 || !isCarrierConfigManagerKeyEnabled( 117 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, subId, 118 true)) { 119 Log.d(TAG, "Setting is either hidden or not editable"); 120 return null; 121 } 122 123 final VolteQueryImsState queryState = queryImsState(subId); 124 if (!queryState.isVoLteProvisioned()) { 125 Log.d(TAG, "Setting is either not provisioned or not enabled by Platform"); 126 return null; 127 } 128 129 try { 130 return getEnhanced4gLteSlice(sliceUri, 131 queryState.isEnabledByUser(), subId); 132 } catch (IllegalArgumentException e) { 133 Log.e(TAG, "Unable to read the current Enhanced 4g LTE status", e); 134 return null; 135 } 136 } 137 138 /** 139 * Builds a toggle slice where the intent takes you to the Enhanced 4G LTE page and the toggle 140 * enables/disables Enhanced 4G LTE mode setting. 141 */ getEnhanced4gLteSlice(Uri sliceUri, boolean isEnhanced4gLteEnabled, int subId)142 private Slice getEnhanced4gLteSlice(Uri sliceUri, boolean isEnhanced4gLteEnabled, int subId) { 143 final IconCompat icon = IconCompat.createWithResource(mContext, 144 R.drawable.ic_launcher_settings); 145 146 return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY) 147 .setAccentColor(Utils.getColorAccentDefaultColor(mContext)) 148 .addRow(new RowBuilder() 149 .setTitle(getEnhanced4glteModeTitle(subId)) 150 .addEndItem( 151 SliceAction.createToggle( 152 getBroadcastIntent(ACTION_ENHANCED_4G_LTE_CHANGED), 153 null /* actionTitle */, isEnhanced4gLteEnabled)) 154 .setPrimaryAction( 155 SliceAction.createDeeplink( 156 getActivityIntent(ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY), 157 icon, 158 ListBuilder.ICON_IMAGE, 159 getEnhanced4glteModeTitle(subId)))) 160 .build(); 161 } 162 163 /** 164 * Handles Enhanced 4G LTE mode setting change from Enhanced 4G LTE slice and posts 165 * notification. Should be called when intent action is ACTION_ENHANCED_4G_LTE_CHANGED 166 * 167 * @param intent action performed 168 */ handleEnhanced4gLteChanged(Intent intent)169 public void handleEnhanced4gLteChanged(Intent intent) { 170 // skip checking when no toggle state update contained within Intent 171 final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, false); 172 if (newValue != intent.getBooleanExtra(EXTRA_TOGGLE_STATE, true)) { 173 notifyEnhanced4gLteUpdate(); 174 return; 175 } 176 177 final int subId = getDefaultVoiceSubId(); 178 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 179 notifyEnhanced4gLteUpdate(); 180 return; 181 } 182 183 final VolteQueryImsState queryState = queryImsState(subId); 184 final boolean currentValue = queryState.isEnabledByUser() 185 && queryState.isAllowUserControl(); 186 if (newValue == currentValue) { 187 notifyEnhanced4gLteUpdate(); 188 return; 189 } 190 191 // isVoLteProvisioned() is the last item to check since it might block the main thread 192 if (queryState.isVoLteProvisioned()) { 193 setEnhanced4gLteModeSetting(subId, newValue); 194 } 195 notifyEnhanced4gLteUpdate(); 196 } 197 notifyEnhanced4gLteUpdate()198 private void notifyEnhanced4gLteUpdate() { 199 // notify change in slice in any case to get re-queried. This would result in displaying 200 // appropriate message with the updated setting. 201 mContext.getContentResolver().notifyChange(CustomSliceRegistry.ENHANCED_4G_SLICE_URI, null); 202 } 203 204 @VisibleForTesting setEnhanced4gLteModeSetting(int subId, boolean isEnabled)205 void setEnhanced4gLteModeSetting(int subId, boolean isEnabled) { 206 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 207 return; 208 } 209 final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId); 210 if (imsMmTelManager == null) { 211 return; 212 } 213 try { 214 imsMmTelManager.setAdvancedCallingSettingEnabled(isEnabled); 215 } catch (IllegalArgumentException exception) { 216 Log.w(TAG, "Unable to change the Enhanced 4g LTE to " + isEnabled + ". subId=" + subId, 217 exception); 218 } 219 } 220 getEnhanced4glteModeTitle(int subId)221 private CharSequence getEnhanced4glteModeTitle(int subId) { 222 final int variant4gLteTitleIndex = getCarrierConfigManagerKeyValue( 223 CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, subId, 0); 224 final boolean show4GForLTE = isCarrierConfigManagerKeyEnabled( 225 CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, subId, false); 226 final CharSequence[] variantTitles = mContext.getResources() 227 .getTextArray(R.array.enhanced_4g_lte_mode_title_variant); 228 int index = MODE_ADVANCED_CALL; 229 if (variant4gLteTitleIndex != MODE_ADVANCED_CALL) { 230 index = show4GForLTE ? MODE_4G_CALLING : MODE_VOLTE; 231 } 232 return variantTitles[index]; 233 } 234 235 /** 236 * Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise. 237 */ isCarrierConfigManagerKeyEnabled(String key, int subId, boolean defaultValue)238 private boolean isCarrierConfigManagerKeyEnabled(String key, int subId, boolean defaultValue) { 239 boolean result = defaultValue; 240 final PersistableBundle carrierConfig = getCarrierConfig(subId); 241 if (carrierConfig != null) { 242 result = carrierConfig.getBoolean(key, defaultValue); 243 } 244 return result; 245 } 246 getCarrierConfigManagerKeyValue(String key, int subId, int defaultValue)247 private int getCarrierConfigManagerKeyValue(String key, int subId, int defaultValue) { 248 int result = defaultValue; 249 final PersistableBundle carrierConfig = getCarrierConfig(subId); 250 if (carrierConfig != null) { 251 result = carrierConfig.getInt(key, defaultValue); 252 } 253 return result; 254 } 255 getCarrierConfig(int subId)256 private PersistableBundle getCarrierConfig(int subId) { 257 final CarrierConfigManager configManager = getCarrierConfigManager(); 258 PersistableBundle bundle = null; 259 if (configManager != null) { 260 bundle = configManager.getConfigForSubId(subId); 261 } 262 return bundle; 263 } 264 getCarrierConfigManager()265 protected CarrierConfigManager getCarrierConfigManager() { 266 return mContext.getSystemService(CarrierConfigManager.class); 267 } 268 getBroadcastIntent(String action)269 private PendingIntent getBroadcastIntent(String action) { 270 final Intent intent = new Intent(action); 271 intent.setClass(mContext, SliceBroadcastReceiver.class); 272 return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, 273 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); 274 } 275 276 /** 277 * Returns the current default voice subId obtained from SubscriptionManager 278 */ getDefaultVoiceSubId()279 protected int getDefaultVoiceSubId() { 280 return SubscriptionManager.getDefaultVoiceSubscriptionId(); 281 } 282 283 /** 284 * Returns PendingIntent to start activity specified by action 285 */ getActivityIntent(String action)286 private PendingIntent getActivityIntent(String action) { 287 final Intent intent = new Intent(action); 288 intent.setPackage(SETTINGS_PACKAGE_NAME); 289 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 290 return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 291 PendingIntent.FLAG_IMMUTABLE); 292 } 293 294 @VisibleForTesting queryImsState(int subId)295 VolteQueryImsState queryImsState(int subId) { 296 return new VolteQueryImsState(mContext, subId); 297 } 298 } 299 300