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.Looper;
24 import android.os.PersistableBundle;
25 import android.telephony.CarrierConfigManager;
26 import android.telephony.PhoneStateListener;
27 import android.telephony.SubscriptionManager;
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.settings.R;
38 import com.android.settings.network.ims.VolteQueryImsState;
39 import com.android.settingslib.core.lifecycle.LifecycleObserver;
40 import com.android.settingslib.core.lifecycle.events.OnStart;
41 import com.android.settingslib.core.lifecycle.events.OnStop;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 
46 /**
47  * Preference controller for "Enhanced 4G LTE"
48  */
49 public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenceController
50         implements LifecycleObserver, OnStart, OnStop {
51 
52     private static final String TAG = "Enhanced4g";
53 
54     @VisibleForTesting
55     Preference mPreference;
56     private PhoneCallStateListener mPhoneStateListener;
57     private boolean mShow5gLimitedDialog;
58     boolean mIsNrEnabledFromCarrierConfig;
59     private boolean mHas5gCapability;
60     @VisibleForTesting
61     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 (mPhoneStateListener == null) {
77             mPhoneStateListener = new PhoneCallStateListener();
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         mIsNrEnabledFromCarrierConfig = carrierConfig.getBoolean(
100                 CarrierConfigManager.KEY_NR_ENABLED_BOOL);
101         return this;
102     }
103 
104     @Override
getAvailabilityStatus(int subId)105     public int getAvailabilityStatus(int subId) {
106         init(subId);
107         if (!isModeMatched()) {
108             return CONDITIONALLY_UNAVAILABLE;
109         }
110         final PersistableBundle carrierConfig = getCarrierConfigForSubId(subId);
111         if ((carrierConfig == null)
112                 || carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL)) {
113             return CONDITIONALLY_UNAVAILABLE;
114         }
115         final VolteQueryImsState queryState = queryImsState(subId);
116         if (!queryState.isReadyToVoLte()) {
117             return CONDITIONALLY_UNAVAILABLE;
118         }
119         return (isUserControlAllowed(carrierConfig) && queryState.isAllowUserControl())
120                 ? AVAILABLE : AVAILABLE_UNSEARCHABLE;
121     }
122 
123     @Override
displayPreference(PreferenceScreen screen)124     public void displayPreference(PreferenceScreen screen) {
125         super.displayPreference(screen);
126         mPreference = screen.findPreference(getPreferenceKey());
127     }
128 
129     @Override
onStart()130     public void onStart() {
131         if (mPhoneStateListener == null) {
132             return;
133         }
134         mPhoneStateListener.register(mContext, mSubId);
135     }
136 
137     @Override
onStop()138     public void onStop() {
139         if (mPhoneStateListener == null) {
140             return;
141         }
142         mPhoneStateListener.unregister();
143     }
144 
145     @Override
updateState(Preference preference)146     public void updateState(Preference preference) {
147         super.updateState(preference);
148         if (preference == null) {
149             return;
150         }
151         final SwitchPreference switchPreference = (SwitchPreference) preference;
152 
153         final VolteQueryImsState queryState = queryImsState(mSubId);
154         switchPreference.setEnabled(isUserControlAllowed(getCarrierConfigForSubId(mSubId))
155                 && queryState.isAllowUserControl());
156         switchPreference.setChecked(queryState.isEnabledByUser()
157                 && queryState.isAllowUserControl());
158     }
159 
160     @Override
setChecked(boolean isChecked)161     public boolean setChecked(boolean isChecked) {
162         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
163             return false;
164         }
165         final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
166         if (imsMmTelManager == null) {
167             return false;
168         }
169 
170         if (isDialogNeeded() && !isChecked) {
171             show5gLimitedDialog(imsMmTelManager);
172         } else {
173             return setAdvancedCallingSettingEnabled(imsMmTelManager, isChecked);
174         }
175         return false;
176     }
177 
178     @Override
isChecked()179     public boolean isChecked() {
180         final VolteQueryImsState queryState = queryImsState(mSubId);
181         return queryState.isEnabledByUser();
182     }
183 
addListener(On4gLteUpdateListener lsn)184     public Enhanced4gBasePreferenceController addListener(On4gLteUpdateListener lsn) {
185         m4gLteListeners.add(lsn);
186         return this;
187     }
188 
getMode()189     protected int getMode() {
190         return MODE_NONE;
191     }
192 
isModeMatched()193     private boolean isModeMatched() {
194         return m4gCurrentMode == getMode();
195     }
196 
197     @VisibleForTesting
queryImsState(int subId)198     VolteQueryImsState queryImsState(int subId) {
199         return new VolteQueryImsState(mContext, subId);
200     }
201 
isUserControlAllowed(final PersistableBundle carrierConfig)202     private boolean isUserControlAllowed(final PersistableBundle carrierConfig) {
203         return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE)
204                 && (carrierConfig != null)
205                 && carrierConfig.getBoolean(
206                 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL);
207     }
208 
209     private class PhoneCallStateListener extends PhoneStateListener {
210 
PhoneCallStateListener()211         PhoneCallStateListener() {
212             super(Looper.getMainLooper());
213         }
214 
215         private TelephonyManager mTelephonyManager;
216 
217         @Override
onCallStateChanged(int state, String incomingNumber)218         public void onCallStateChanged(int state, String incomingNumber) {
219             mCallState = state;
220             updateState(mPreference);
221         }
222 
register(Context context, int subId)223         public void register(Context context, int subId) {
224             mTelephonyManager = context.getSystemService(TelephonyManager.class);
225             if (SubscriptionManager.isValidSubscriptionId(subId)) {
226                 mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
227             }
228             mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
229 
230             final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily();
231             mHas5gCapability =
232                     (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0;
233         }
234 
unregister()235         public void unregister() {
236             mCallState = null;
237             mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
238         }
239     }
240 
241     /**
242      * Update other preferences when 4gLte state is changed
243      */
244     public interface On4gLteUpdateListener {
on4gLteUpdated()245         void on4gLteUpdated();
246     }
247 
isDialogNeeded()248     private boolean isDialogNeeded() {
249         Log.d(TAG, "Has5gCapability:" + mHas5gCapability);
250         return mShow5gLimitedDialog && mHas5gCapability && mIsNrEnabledFromCarrierConfig;
251     }
252 
show5gLimitedDialog(ImsMmTelManager imsMmTelManager)253     private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) {
254         Log.d(TAG, "show5gLimitedDialog");
255         AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
256         DialogInterface.OnClickListener networkSettingsClickListener =
257                 new Dialog.OnClickListener() {
258                     @Override
259                     public void onClick(DialogInterface dialog, int which) {
260                         Log.d(TAG, "onClick,isChecked:false");
261                         setAdvancedCallingSettingEnabled(imsMmTelManager, false);
262                         updateState(mPreference);
263                     }
264                 };
265         builder.setTitle(R.string.volte_5G_limited_title)
266                 .setMessage(R.string.volte_5G_limited_text)
267                 .setNeutralButton(mContext.getResources().getString(
268                         R.string.cancel), null)
269                 .setPositiveButton(mContext.getResources().getString(
270                         R.string.condition_turn_off),
271                         networkSettingsClickListener)
272                 .create()
273                 .show();
274     }
275 
setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager, boolean isChecked)276     private boolean setAdvancedCallingSettingEnabled(ImsMmTelManager imsMmTelManager,
277             boolean isChecked) {
278         try {
279             imsMmTelManager.setAdvancedCallingSettingEnabled(isChecked);
280         } catch (IllegalArgumentException exception) {
281             Log.w(TAG, "fail to set VoLTE=" + isChecked + ". subId=" + mSubId, exception);
282             return false;
283         }
284         for (final On4gLteUpdateListener lsn : m4gLteListeners) {
285             lsn.on4gLteUpdated();
286         }
287         return true;
288     }
289 }
290