• 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 android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.os.PersistableBundle;
24 import android.telephony.CarrierConfigManager;
25 import android.telephony.PhoneStateListener;
26 import android.telephony.SubscriptionManager;
27 import android.telephony.TelephonyCallback;
28 import android.telephony.TelephonyManager;
29 import android.telephony.ims.ImsMmTelManager;
30 import android.util.Log;
31 
32 import androidx.annotation.VisibleForTesting;
33 import androidx.preference.Preference;
34 import androidx.preference.PreferenceScreen;
35 import androidx.preference.SwitchPreference;
36 
37 import com.android.internal.telephony.util.ArrayUtils;
38 import com.android.settings.R;
39 import com.android.settings.network.ims.VolteQueryImsState;
40 import com.android.settingslib.core.lifecycle.LifecycleObserver;
41 import com.android.settingslib.core.lifecycle.events.OnStart;
42 import com.android.settingslib.core.lifecycle.events.OnStop;
43 
44 import java.util.ArrayList;
45 import java.util.List;
46 
47 /**
48  * Preference controller for "Enhanced 4G LTE"
49  */
50 public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenceController
51         implements LifecycleObserver, OnStart, OnStop {
52 
53     private static final String TAG = "Enhanced4g";
54 
55     @VisibleForTesting
56     Preference mPreference;
57     private PhoneCallStateTelephonyCallback mTelephonyCallback;
58     private boolean mShow5gLimitedDialog;
59     boolean mIsNrEnabledFromCarrierConfig;
60     private boolean mHas5gCapability;
61     private Integer mCallState;
62     private final List<On4gLteUpdateListener> m4gLteListeners;
63 
64     protected static final int MODE_NONE = -1;
65     protected static final int MODE_VOLTE = 0;
66     protected static final int MODE_ADVANCED_CALL = 1;
67     protected static final int MODE_4G_CALLING = 2;
68     private int m4gCurrentMode = MODE_NONE;
69 
Enhanced4gBasePreferenceController(Context context, String key)70     public Enhanced4gBasePreferenceController(Context context, String key) {
71         super(context, key);
72         m4gLteListeners = new ArrayList<>();
73     }
74 
init(int subId)75     public Enhanced4gBasePreferenceController init(int subId) {
76         if (mTelephonyCallback == null) {
77             mTelephonyCallback = new PhoneCallStateTelephonyCallback();
78         }
79 
80         if (mSubId == subId) {
81             return this;
82         }
83         mSubId = subId;
84         final PersistableBundle carrierConfig = getCarrierConfigForSubId(subId);
85         if (carrierConfig == null) {
86             return this;
87         }
88 
89         final boolean show4GForLTE = carrierConfig.getBoolean(
90                 CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
91         m4gCurrentMode = carrierConfig.getInt(
92                 CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT);
93         if (m4gCurrentMode != MODE_ADVANCED_CALL) {
94             m4gCurrentMode = show4GForLTE ? MODE_4G_CALLING : MODE_VOLTE;
95         }
96 
97         mShow5gLimitedDialog = carrierConfig.getBoolean(
98                 CarrierConfigManager.KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL);
99 
100         int[] nrAvailabilities = carrierConfig.getIntArray(
101                 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
102         mIsNrEnabledFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities);
103         return this;
104     }
105 
106     @Override
getAvailabilityStatus(int subId)107     public int getAvailabilityStatus(int subId) {
108         init(subId);
109         if (!isModeMatched()) {
110             return CONDITIONALLY_UNAVAILABLE;
111         }
112         final VolteQueryImsState queryState = queryImsState(subId);
113         // Show VoLTE settings if VoIMS opt-in has been enabled irrespective of other VoLTE settings
114         if (queryState.isVoImsOptInEnabled()) {
115             return AVAILABLE;
116         }
117 
118         final PersistableBundle carrierConfig = getCarrierConfigForSubId(subId);
119         if ((carrierConfig == null)
120                 || carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL)) {
121             return CONDITIONALLY_UNAVAILABLE;
122         }
123 
124         if (!queryState.isReadyToVoLte()) {
125             return CONDITIONALLY_UNAVAILABLE;
126         }
127         return (isUserControlAllowed(carrierConfig) && queryState.isAllowUserControl())
128                 ? AVAILABLE : AVAILABLE_UNSEARCHABLE;
129     }
130 
131     @Override
displayPreference(PreferenceScreen screen)132     public void displayPreference(PreferenceScreen screen) {
133         super.displayPreference(screen);
134         mPreference = screen.findPreference(getPreferenceKey());
135     }
136 
137     @Override
onStart()138     public void onStart() {
139         if (!isModeMatched() || (mTelephonyCallback == null)) {
140             return;
141         }
142         mTelephonyCallback.register(mContext, mSubId);
143     }
144 
145     @Override
onStop()146     public void onStop() {
147         if (mTelephonyCallback == null) {
148             return;
149         }
150         mTelephonyCallback.unregister();
151     }
152 
153     @Override
updateState(Preference preference)154     public void updateState(Preference preference) {
155         super.updateState(preference);
156         if (preference == null) {
157             return;
158         }
159         final SwitchPreference switchPreference = (SwitchPreference) preference;
160 
161         final VolteQueryImsState queryState = queryImsState(mSubId);
162         switchPreference.setEnabled(isUserControlAllowed(getCarrierConfigForSubId(mSubId))
163                 && queryState.isAllowUserControl());
164         switchPreference.setChecked(queryState.isEnabledByUser()
165                 && queryState.isAllowUserControl());
166     }
167 
168     @Override
setChecked(boolean isChecked)169     public boolean setChecked(boolean isChecked) {
170         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
171             return false;
172         }
173         final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
174         if (imsMmTelManager == null) {
175             return false;
176         }
177 
178         if (isDialogNeeded() && !isChecked) {
179             show5gLimitedDialog(imsMmTelManager);
180         } else {
181             return setAdvancedCallingSettingEnabled(imsMmTelManager, isChecked);
182         }
183         return false;
184     }
185 
186     @Override
isChecked()187     public boolean isChecked() {
188         final VolteQueryImsState queryState = queryImsState(mSubId);
189         return queryState.isEnabledByUser();
190     }
191 
addListener(On4gLteUpdateListener lsn)192     public Enhanced4gBasePreferenceController addListener(On4gLteUpdateListener lsn) {
193         m4gLteListeners.add(lsn);
194         return this;
195     }
196 
197     @VisibleForTesting
getMode()198     protected int getMode() {
199         return MODE_NONE;
200     }
201 
isModeMatched()202     private boolean isModeMatched() {
203         return m4gCurrentMode == getMode();
204     }
205 
206     @VisibleForTesting
queryImsState(int subId)207     protected VolteQueryImsState queryImsState(int subId) {
208         return new VolteQueryImsState(mContext, subId);
209     }
210 
211     @VisibleForTesting
isCallStateIdle()212     protected boolean isCallStateIdle() {
213         return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE);
214     }
215 
isUserControlAllowed(final PersistableBundle carrierConfig)216     private boolean isUserControlAllowed(final PersistableBundle carrierConfig) {
217         return isCallStateIdle()
218                 && (carrierConfig != null)
219                 && carrierConfig.getBoolean(
220                 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL);
221     }
222 
223     private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
224             TelephonyCallback.CallStateListener {
225 
226         private TelephonyManager mTelephonyManager;
227 
228         @Override
onCallStateChanged(int state)229         public void onCallStateChanged(int state) {
230             mCallState = state;
231             updateState(mPreference);
232         }
233 
register(Context context, int subId)234         public void register(Context context, int subId) {
235             mTelephonyManager = context.getSystemService(TelephonyManager.class);
236             if (SubscriptionManager.isValidSubscriptionId(subId)) {
237                 mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
238             }
239             // assign current call state so that it helps to show correct preference state even
240             // before first onCallStateChanged() by initial registration.
241             mCallState = mTelephonyManager.getCallState(subId);
242             mTelephonyManager.registerTelephonyCallback(
243                     mContext.getMainExecutor(), mTelephonyCallback);
244 
245             final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily();
246             mHas5gCapability =
247                     (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0;
248         }
249 
unregister()250         public void unregister() {
251             mCallState = null;
252             if (mTelephonyManager != null) {
253                 mTelephonyManager.unregisterTelephonyCallback(this);
254             }
255         }
256     }
257 
258     /**
259      * Update other preferences when 4gLte state is changed
260      */
261     public interface On4gLteUpdateListener {
on4gLteUpdated()262         void on4gLteUpdated();
263     }
264 
isDialogNeeded()265     private boolean isDialogNeeded() {
266         Log.d(TAG, "Has5gCapability:" + mHas5gCapability);
267         return mShow5gLimitedDialog && mHas5gCapability && mIsNrEnabledFromCarrierConfig;
268     }
269 
show5gLimitedDialog(ImsMmTelManager imsMmTelManager)270     private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) {
271         Log.d(TAG, "show5gLimitedDialog");
272         AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
273         DialogInterface.OnClickListener networkSettingsClickListener =
274                 new Dialog.OnClickListener() {
275                     @Override
276                     public void onClick(DialogInterface dialog, int which) {
277                         Log.d(TAG, "onClick,isChecked:false");
278                         setAdvancedCallingSettingEnabled(imsMmTelManager, false);
279                         updateState(mPreference);
280                     }
281                 };
282         builder.setTitle(R.string.volte_5G_limited_title)
283                 .setMessage(R.string.volte_5G_limited_text)
284                 .setNeutralButton(mContext.getResources().getString(
285                         R.string.cancel), null)
286                 .setPositiveButton(mContext.getResources().getString(
287                         R.string.condition_turn_off),
288                         networkSettingsClickListener)
289                 .create()
290                 .show();
291     }
292 
setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager, boolean isChecked)293     private boolean setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager,
294             boolean isChecked) {
295         try {
296             imsMmTelManager.setAdvancedCallingSettingEnabled(isChecked);
297         } catch (IllegalArgumentException exception) {
298             Log.w(TAG, "fail to set VoLTE=" + isChecked + ". subId=" + mSubId, exception);
299             return false;
300         }
301         for (final On4gLteUpdateListener lsn : m4gLteListeners) {
302             lsn.on4gLteUpdated();
303         }
304         return true;
305     }
306 }
307