• 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 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