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