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