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 17 package com.android.settings.network.telephony; 18 19 import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; 20 import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; 21 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.telecom.PhoneAccount; 25 import android.telecom.PhoneAccountHandle; 26 import android.telecom.TelecomManager; 27 import android.telephony.SubscriptionInfo; 28 import android.telephony.SubscriptionManager; 29 import android.view.View; 30 31 import androidx.lifecycle.LifecycleObserver; 32 import androidx.lifecycle.OnLifecycleEvent; 33 import androidx.preference.ListPreference; 34 import androidx.preference.Preference; 35 import androidx.preference.PreferenceScreen; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.settings.R; 39 import com.android.settings.network.SubscriptionUtil; 40 import com.android.settings.network.SubscriptionsChangeListener; 41 42 import java.util.ArrayList; 43 import java.util.List; 44 45 /** 46 * This implements common controller functionality for a Preference letting the user see/change 47 * what mobile network subscription is used by default for some service controlled by the 48 * SubscriptionManager. This can be used for services such as Calls or SMS. 49 */ 50 public abstract class DefaultSubscriptionController extends TelephonyBasePreferenceController 51 implements LifecycleObserver, Preference.OnPreferenceChangeListener, 52 SubscriptionsChangeListener.SubscriptionsChangeListenerClient { 53 private static final String TAG = "DefaultSubController"; 54 55 protected SubscriptionsChangeListener mChangeListener; 56 protected ListPreference mPreference; 57 protected SubscriptionManager mManager; 58 protected TelecomManager mTelecomManager; 59 60 private static final String EMERGENCY_ACCOUNT_HANDLE_ID = "E"; 61 private static final ComponentName PSTN_CONNECTION_SERVICE_COMPONENT = 62 new ComponentName("com.android.phone", 63 "com.android.services.telephony.TelephonyConnectionService"); 64 private boolean mIsRtlMode; 65 DefaultSubscriptionController(Context context, String preferenceKey)66 public DefaultSubscriptionController(Context context, String preferenceKey) { 67 super(context, preferenceKey); 68 mManager = context.getSystemService(SubscriptionManager.class); 69 mChangeListener = new SubscriptionsChangeListener(context, this); 70 mIsRtlMode = context.getResources().getConfiguration().getLayoutDirection() 71 == View.LAYOUT_DIRECTION_RTL; 72 } 73 74 /** @return SubscriptionInfo for the default subscription for the service, or null if there 75 * isn't one. */ getDefaultSubscriptionInfo()76 protected abstract SubscriptionInfo getDefaultSubscriptionInfo(); 77 78 /** @return the id of the default subscription for the service, or 79 * SubscriptionManager.INVALID_SUBSCRIPTION_ID if there isn't one. */ getDefaultSubscriptionId()80 protected abstract int getDefaultSubscriptionId(); 81 82 /** Called to change the default subscription for the service. */ setDefaultSubscription(int subscriptionId)83 protected abstract void setDefaultSubscription(int subscriptionId); 84 isAskEverytimeSupported()85 protected boolean isAskEverytimeSupported() { 86 return true; 87 } 88 89 @Override getAvailabilityStatus(int subId)90 public int getAvailabilityStatus(int subId) { 91 return AVAILABLE; 92 } 93 94 @OnLifecycleEvent(ON_RESUME) onResume()95 public void onResume() { 96 mChangeListener.start(); 97 updateEntries(); 98 } 99 100 @OnLifecycleEvent(ON_PAUSE) onPause()101 public void onPause() { 102 mChangeListener.stop(); 103 } 104 105 @Override displayPreference(PreferenceScreen screen)106 public void displayPreference(PreferenceScreen screen) { 107 super.displayPreference(screen); 108 mPreference = screen.findPreference(getPreferenceKey()); 109 updateEntries(); 110 } 111 112 @Override refreshSummary(Preference preference)113 protected void refreshSummary(Preference preference) { 114 // Currently, cannot use ListPreference.setSummary() when the summary contains user 115 // generated string, because ListPreference.getSummary() is using String.format() to format 116 // the summary when the summary is set by ListPreference.setSummary(). 117 if (preference != null) { 118 preference.setSummaryProvider(pref -> getSummary()); 119 } 120 } 121 122 @Override getSummary()123 public CharSequence getSummary() { 124 final PhoneAccountHandle handle = getDefaultCallingAccountHandle(); 125 if ((handle != null) && (!isCallingAccountBindToSubscription(handle))) { 126 // display VoIP account in summary when configured through settings within dialer 127 return getLabelFromCallingAccount(handle); 128 } 129 final SubscriptionInfo info = getDefaultSubscriptionInfo(); 130 if (info != null) { 131 // display subscription based account 132 return SubscriptionUtil.getUniqueSubscriptionDisplayName(info, mContext); 133 } else { 134 if (isAskEverytimeSupported()) { 135 return mContext.getString(R.string.calls_and_sms_ask_every_time); 136 } else { 137 return ""; 138 } 139 } 140 } 141 updateEntries()142 private void updateEntries() { 143 if (mPreference == null) { 144 return; 145 } 146 if (!isAvailable()) { 147 mPreference.setVisible(false); 148 return; 149 } 150 mPreference.setVisible(true); 151 152 // TODO(b/135142209) - for now we need to manually ensure we're registered as a change 153 // listener, because this might not have happened during displayPreference if 154 // getAvailabilityStatus returned CONDITIONALLY_UNAVAILABLE at the time. 155 mPreference.setOnPreferenceChangeListener(this); 156 157 final List<SubscriptionInfo> subs = SubscriptionUtil.getActiveSubscriptions(mManager); 158 159 // We'll have one entry for each available subscription, plus one for a "ask me every 160 // time" entry at the end. 161 final ArrayList<CharSequence> displayNames = new ArrayList<>(); 162 final ArrayList<CharSequence> subscriptionIds = new ArrayList<>(); 163 164 if (subs.size() == 1) { 165 mPreference.setEnabled(false); 166 mPreference.setSummaryProvider(pref -> 167 SubscriptionUtil.getUniqueSubscriptionDisplayName(subs.get(0), mContext)); 168 return; 169 } 170 171 final int serviceDefaultSubId = getDefaultSubscriptionId(); 172 boolean subIsAvailable = false; 173 174 for (SubscriptionInfo sub : subs) { 175 if (sub.isOpportunistic()) { 176 continue; 177 } 178 displayNames.add(SubscriptionUtil.getUniqueSubscriptionDisplayName(sub, mContext)); 179 final int subId = sub.getSubscriptionId(); 180 subscriptionIds.add(Integer.toString(subId)); 181 if (subId == serviceDefaultSubId) { 182 subIsAvailable = true; 183 } 184 } 185 186 if (isAskEverytimeSupported()) { 187 // Add the extra "Ask every time" value at the end. 188 displayNames.add(mContext.getString(R.string.calls_and_sms_ask_every_time)); 189 subscriptionIds.add(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID)); 190 } 191 192 mPreference.setEnabled(true); 193 mPreference.setEntries(displayNames.toArray(new CharSequence[0])); 194 mPreference.setEntryValues(subscriptionIds.toArray(new CharSequence[0])); 195 196 if (subIsAvailable) { 197 mPreference.setValue(Integer.toString(serviceDefaultSubId)); 198 } else { 199 mPreference.setValue(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID)); 200 } 201 } 202 203 /** 204 * Get default calling account 205 * 206 * @return current calling account {@link PhoneAccountHandle} 207 */ getDefaultCallingAccountHandle()208 public PhoneAccountHandle getDefaultCallingAccountHandle() { 209 final PhoneAccountHandle currentSelectPhoneAccount = 210 getTelecomManager().getUserSelectedOutgoingPhoneAccount(); 211 if (currentSelectPhoneAccount == null) { 212 return null; 213 } 214 final List<PhoneAccountHandle> accountHandles = 215 getTelecomManager().getCallCapablePhoneAccounts(false); 216 final PhoneAccountHandle emergencyAccountHandle = new PhoneAccountHandle( 217 PSTN_CONNECTION_SERVICE_COMPONENT, EMERGENCY_ACCOUNT_HANDLE_ID); 218 if (currentSelectPhoneAccount.equals(emergencyAccountHandle)) { 219 return null; 220 } 221 for (PhoneAccountHandle handle : accountHandles) { 222 if (currentSelectPhoneAccount.equals(handle)) { 223 return currentSelectPhoneAccount; 224 } 225 } 226 return null; 227 } 228 229 @VisibleForTesting getTelecomManager()230 TelecomManager getTelecomManager() { 231 if (mTelecomManager == null) { 232 mTelecomManager = mContext.getSystemService(TelecomManager.class); 233 } 234 return mTelecomManager; 235 } 236 237 @VisibleForTesting getPhoneAccount(PhoneAccountHandle handle)238 PhoneAccount getPhoneAccount(PhoneAccountHandle handle) { 239 return getTelecomManager().getPhoneAccount(handle); 240 } 241 242 /** 243 * Check if calling account bind to subscription 244 * 245 * @param handle {@link PhoneAccountHandle} for specific calling account 246 */ isCallingAccountBindToSubscription(PhoneAccountHandle handle)247 public boolean isCallingAccountBindToSubscription(PhoneAccountHandle handle) { 248 final PhoneAccount account = getPhoneAccount(handle); 249 if (account == null) { 250 return false; 251 } 252 return account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION); 253 } 254 255 /** 256 * Get label from calling account 257 * 258 * @param handle to get label from {@link PhoneAccountHandle} 259 * @return label of calling account 260 */ getLabelFromCallingAccount(PhoneAccountHandle handle)261 public CharSequence getLabelFromCallingAccount(PhoneAccountHandle handle) { 262 CharSequence label = null; 263 final PhoneAccount account = getPhoneAccount(handle); 264 if (account != null) { 265 label = account.getLabel(); 266 } 267 if (label != null) { 268 label = mContext.getPackageManager().getUserBadgedLabel(label, handle.getUserHandle()); 269 } 270 return (label != null) ? label : ""; 271 } 272 273 @Override onPreferenceChange(Preference preference, Object newValue)274 public boolean onPreferenceChange(Preference preference, Object newValue) { 275 final int subscriptionId = Integer.parseInt((String) newValue); 276 setDefaultSubscription(subscriptionId); 277 refreshSummary(mPreference); 278 return true; 279 } 280 281 @Override onAirplaneModeChanged(boolean airplaneModeEnabled)282 public void onAirplaneModeChanged(boolean airplaneModeEnabled) { 283 } 284 285 @Override onSubscriptionsChanged()286 public void onSubscriptionsChanged() { 287 if (mPreference != null) { 288 updateEntries(); 289 refreshSummary(mPreference); 290 } 291 } 292 isRtlMode()293 boolean isRtlMode() { 294 return mIsRtlMode; 295 } 296 } 297