• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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