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 30 import androidx.lifecycle.Lifecycle; 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 DefaultSubscriptionController(Context context, String preferenceKey)65 public DefaultSubscriptionController(Context context, String preferenceKey) { 66 super(context, preferenceKey); 67 mManager = context.getSystemService(SubscriptionManager.class); 68 mChangeListener = new SubscriptionsChangeListener(context, this); 69 } 70 init(Lifecycle lifecycle)71 public void init(Lifecycle lifecycle) { 72 lifecycle.addObserver(this); 73 } 74 75 /** @return SubscriptionInfo for the default subscription for the service, or null if there 76 * isn't one. */ getDefaultSubscriptionInfo()77 protected abstract SubscriptionInfo getDefaultSubscriptionInfo(); 78 79 /** @return the id of the default subscription for the service, or 80 * SubscriptionManager.INVALID_SUBSCRIPTION_ID if there isn't one. */ getDefaultSubscriptionId()81 protected abstract int getDefaultSubscriptionId(); 82 83 /** Called to change the default subscription for the service. */ setDefaultSubscription(int subscriptionId)84 protected abstract void setDefaultSubscription(int subscriptionId); 85 86 @Override getAvailabilityStatus(int subId)87 public int getAvailabilityStatus(int subId) { 88 final List<SubscriptionInfo> subs = SubscriptionUtil.getActiveSubscriptions(mManager); 89 if (subs.size() > 1) { 90 return AVAILABLE; 91 } else { 92 return CONDITIONALLY_UNAVAILABLE; 93 } 94 } 95 96 @OnLifecycleEvent(ON_RESUME) onResume()97 public void onResume() { 98 mChangeListener.start(); 99 updateEntries(); 100 } 101 102 @OnLifecycleEvent(ON_PAUSE) onPause()103 public void onPause() { 104 mChangeListener.stop(); 105 } 106 107 @Override displayPreference(PreferenceScreen screen)108 public void displayPreference(PreferenceScreen screen) { 109 super.displayPreference(screen); 110 mPreference = screen.findPreference(getPreferenceKey()); 111 updateEntries(); 112 } 113 114 @Override getSummary()115 public CharSequence getSummary() { 116 final PhoneAccountHandle handle = getDefaultCallingAccountHandle(); 117 if ((handle != null) && (!isCallingAccountBindToSubscription(handle))) { 118 // display VoIP account in summary when configured through settings within dialer 119 return getLabelFromCallingAccount(handle); 120 } 121 final SubscriptionInfo info = getDefaultSubscriptionInfo(); 122 if (info != null) { 123 // display subscription based account 124 return info.getDisplayName(); 125 } else { 126 return mContext.getString(R.string.calls_and_sms_ask_every_time); 127 } 128 } 129 updateEntries()130 private void updateEntries() { 131 if (mPreference == null) { 132 return; 133 } 134 if (!isAvailable()) { 135 mPreference.setVisible(false); 136 return; 137 } 138 mPreference.setVisible(true); 139 140 // TODO(b/135142209) - for now we need to manually ensure we're registered as a change 141 // listener, because this might not have happened during displayPreference if 142 // getAvailabilityStatus returned CONDITIONALLY_UNAVAILABLE at the time. 143 mPreference.setOnPreferenceChangeListener(this); 144 145 final List<SubscriptionInfo> subs = SubscriptionUtil.getActiveSubscriptions(mManager); 146 147 // We'll have one entry for each available subscription, plus one for a "ask me every 148 // time" entry at the end. 149 final ArrayList<CharSequence> displayNames = new ArrayList<>(); 150 final ArrayList<CharSequence> subscriptionIds = new ArrayList<>(); 151 152 final int serviceDefaultSubId = getDefaultSubscriptionId(); 153 boolean subIsAvailable = false; 154 155 for (SubscriptionInfo sub : subs) { 156 if (sub.isOpportunistic()) { 157 continue; 158 } 159 displayNames.add(sub.getDisplayName()); 160 final int subId = sub.getSubscriptionId(); 161 subscriptionIds.add(Integer.toString(subId)); 162 if (subId == serviceDefaultSubId) { 163 subIsAvailable = true; 164 } 165 } 166 // Add the extra "Ask every time" value at the end. 167 displayNames.add(mContext.getString(R.string.calls_and_sms_ask_every_time)); 168 subscriptionIds.add(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID)); 169 170 mPreference.setEntries(displayNames.toArray(new CharSequence[0])); 171 mPreference.setEntryValues(subscriptionIds.toArray(new CharSequence[0])); 172 173 if (subIsAvailable) { 174 mPreference.setValue(Integer.toString(serviceDefaultSubId)); 175 } else { 176 mPreference.setValue(Integer.toString(SubscriptionManager.INVALID_SUBSCRIPTION_ID)); 177 } 178 } 179 180 /** 181 * Get default calling account 182 * 183 * @return current calling account {@link PhoneAccountHandle} 184 */ getDefaultCallingAccountHandle()185 public PhoneAccountHandle getDefaultCallingAccountHandle() { 186 final PhoneAccountHandle currentSelectPhoneAccount = 187 getTelecomManager().getUserSelectedOutgoingPhoneAccount(); 188 if (currentSelectPhoneAccount == null) { 189 return null; 190 } 191 final List<PhoneAccountHandle> accountHandles = 192 getTelecomManager().getCallCapablePhoneAccounts(false); 193 final PhoneAccountHandle emergencyAccountHandle = new PhoneAccountHandle( 194 PSTN_CONNECTION_SERVICE_COMPONENT, EMERGENCY_ACCOUNT_HANDLE_ID); 195 if (currentSelectPhoneAccount.equals(emergencyAccountHandle)) { 196 return null; 197 } 198 for (PhoneAccountHandle handle : accountHandles) { 199 if (currentSelectPhoneAccount.equals(handle)) { 200 return currentSelectPhoneAccount; 201 } 202 } 203 return null; 204 } 205 206 @VisibleForTesting getTelecomManager()207 TelecomManager getTelecomManager() { 208 if (mTelecomManager == null) { 209 mTelecomManager = mContext.getSystemService(TelecomManager.class); 210 } 211 return mTelecomManager; 212 } 213 214 @VisibleForTesting getPhoneAccount(PhoneAccountHandle handle)215 PhoneAccount getPhoneAccount(PhoneAccountHandle handle) { 216 return getTelecomManager().getPhoneAccount(handle); 217 } 218 219 /** 220 * Check if calling account bind to subscription 221 * 222 * @param handle {@link PhoneAccountHandle} for specific calling account 223 */ isCallingAccountBindToSubscription(PhoneAccountHandle handle)224 public boolean isCallingAccountBindToSubscription(PhoneAccountHandle handle) { 225 final PhoneAccount account = getPhoneAccount(handle); 226 if (account == null) { 227 return false; 228 } 229 return account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION); 230 } 231 232 /** 233 * Get label from calling account 234 * 235 * @param handle to get label from {@link PhoneAccountHandle} 236 * @return label of calling account 237 */ getLabelFromCallingAccount(PhoneAccountHandle handle)238 public CharSequence getLabelFromCallingAccount(PhoneAccountHandle handle) { 239 CharSequence label = null; 240 final PhoneAccount account = getPhoneAccount(handle); 241 if (account != null) { 242 label = account.getLabel(); 243 } 244 if (label != null) { 245 label = mContext.getPackageManager().getUserBadgedLabel(label, handle.getUserHandle()); 246 } 247 return (label != null) ? label : ""; 248 } 249 250 @Override onPreferenceChange(Preference preference, Object newValue)251 public boolean onPreferenceChange(Preference preference, Object newValue) { 252 final int subscriptionId = Integer.parseInt((String) newValue); 253 setDefaultSubscription(subscriptionId); 254 refreshSummary(mPreference); 255 return true; 256 } 257 258 @Override onAirplaneModeChanged(boolean airplaneModeEnabled)259 public void onAirplaneModeChanged(boolean airplaneModeEnabled) { 260 } 261 262 @Override onSubscriptionsChanged()263 public void onSubscriptionsChanged() { 264 if (mPreference != null) { 265 updateEntries(); 266 refreshSummary(mPreference); 267 } 268 } 269 } 270