1 /*
2  * Copyright (C) 2020 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 package com.android.settings.network.telephony;
17 
18 import android.app.settings.SettingsEnums;
19 import android.content.Context;
20 import android.os.PersistableBundle;
21 import android.telephony.CarrierConfigManager;
22 import android.telephony.SubscriptionInfo;
23 import android.telephony.SubscriptionManager;
24 import android.telephony.TelephonyManager;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 import androidx.preference.Preference;
29 import androidx.preference.PreferenceScreen;
30 
31 import com.android.settings.R;
32 import com.android.settings.flags.Flags;
33 import com.android.settings.network.CarrierConfigCache;
34 import com.android.settings.network.SubscriptionUtil;
35 import com.android.settings.overlay.FeatureFactory;
36 import com.android.settingslib.RestrictedSwitchPreference;
37 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
38 
39 /**
40  * Preference controller for "Enable 2G"
41  *
42  * <p>
43  * This preference controller is invoked per subscription id, which means toggling 2g is a per-sim
44  * operation. The requested 2g preference is delegated to
45  * {@link TelephonyManager#setAllowedNetworkTypesForReason(int reason, long allowedNetworkTypes)}
46  * with:
47  * <ul>
48  *     <li>{@code reason} {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}.</li>
49  *     <li>{@code allowedNetworkTypes} with set or cleared 2g-related bits, depending on the
50  *     requested preference state. </li>
51  * </ul>
52  */
53 public class Enable2gPreferenceController extends TelephonyTogglePreferenceController {
54 
55     private static final String LOG_TAG = "Enable2gPreferenceController";
56     private static final long BITMASK_2G = TelephonyManager.NETWORK_TYPE_BITMASK_GSM
57             | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
58             | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
59             | TelephonyManager.NETWORK_TYPE_BITMASK_CDMA
60             | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
61 
62     private final MetricsFeatureProvider mMetricsFeatureProvider;
63 
64     private CarrierConfigCache mCarrierConfigCache;
65     private SubscriptionManager mSubscriptionManager;
66     private TelephonyManager mTelephonyManager;
67     private RestrictedSwitchPreference mRestrictedPreference;
68 
69     /**
70      * Class constructor of "Enable 2G" toggle.
71      *
72      * @param context of settings
73      * @param key     assigned within UI entry of XML file
74      */
Enable2gPreferenceController(Context context, String key)75     public Enable2gPreferenceController(Context context, String key) {
76         super(context, key);
77         mCarrierConfigCache = CarrierConfigCache.getInstance(context);
78         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
79         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
80         mRestrictedPreference = null;
81     }
82 
83     /**
84      * Initialization based on a given subscription id.
85      *
86      * @param subId is the subscription id
87      * @return this instance after initialization
88      */
init(int subId)89     public Enable2gPreferenceController init(int subId) {
90         mSubId = subId;
91         mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
92                 .createForSubscriptionId(mSubId);
93         return this;
94     }
95 
96     @Override
displayPreference(PreferenceScreen screen)97     public void displayPreference(PreferenceScreen screen) {
98         super.displayPreference(screen);
99         mRestrictedPreference = screen.findPreference(getPreferenceKey());
100     }
101 
102     @Override
updateState(Preference preference)103     public void updateState(Preference preference) {
104         super.updateState(preference);
105 
106         // The device admin decision overrides any carrier preferences
107         if (isDisabledByAdmin()) {
108             return;
109         }
110 
111         if (preference == null || !SubscriptionManager.isUsableSubscriptionId(mSubId)) {
112             return;
113         }
114 
115         // TODO: b/303411083 remove all dynamic logic and rely on summary in resource file once flag
116         //  is no longer needed
117         if (Flags.removeKeyHideEnable2g()) {
118             preference.setSummary(mContext.getString(R.string.enable_2g_summary));
119         } else {
120             final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(mSubId);
121             boolean isDisabledByCarrier =
122                     carrierConfig != null
123                             && carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENABLE_2G);
124             preference.setEnabled(!isDisabledByCarrier);
125             String summary;
126             if (isDisabledByCarrier) {
127                 summary = mContext.getString(R.string.enable_2g_summary_disabled_carrier,
128                         getSimCardName());
129             } else {
130                 summary = mContext.getString(R.string.enable_2g_summary);
131             }
132             preference.setSummary(summary);
133         }
134     }
135 
getSimCardName()136     private String getSimCardName() {
137         SubscriptionInfo subInfo = SubscriptionUtil.getSubById(mSubscriptionManager, mSubId);
138         if (subInfo == null) {
139             return "";
140         }
141         // It is the sim card name, and it should be the same name as the sim page.
142         CharSequence simCardName = subInfo.getDisplayName();
143         return TextUtils.isEmpty(simCardName) ? "" : simCardName.toString();
144     }
145 
146     /**
147      * Get the {@link com.android.settings.core.BasePreferenceController.AvailabilityStatus} for
148      * this preference given a {@code subId}.
149      * <p>
150      * A return value of {@link #AVAILABLE} denotes that the 2g status can be updated for this
151      * particular subscription.
152      * We return {@link #AVAILABLE} if the following conditions are met and {@link
153      * #CONDITIONALLY_UNAVAILABLE} otherwise.
154      * <ul>
155      *     <li>The subscription is usable {@link SubscriptionManager#isUsableSubscriptionId}</li>
156      *     <li>The carrier has not opted to disable this preference
157      *     {@link CarrierConfigManager#KEY_HIDE_ENABLE_2G}</li>
158      *     <li>The device supports
159      *     <a href="https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/radio/1.6/IRadio.hal">Radio HAL version 1.6 or greater</a> </li>
160      * </ul>
161      */
162     @Override
getAvailabilityStatus(int subId)163     public int getAvailabilityStatus(int subId) {
164         if (mTelephonyManager == null) {
165             Log.w(LOG_TAG, "Telephony manager not yet initialized");
166             mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
167         }
168         boolean visible =
169                 SubscriptionManager.isUsableSubscriptionId(subId)
170                         && mTelephonyManager.isRadioInterfaceCapabilitySupported(
171                         mTelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK);
172         return visible ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
173     }
174 
175     /**
176      * Return {@code true} if 2g is currently enabled.
177      *
178      * <p><b>NOTE:</b> This method returns the active state of the preference controller and is not
179      * the parameter passed into {@link #setChecked(boolean)}, which is instead the requested future
180      * state.</p>
181      */
182     @Override
isChecked()183     public boolean isChecked() {
184         // If an enterprise admin has disabled 2g, we show the toggle as not checked to avoid
185         // user confusion of seeing a checked toggle, but having 2g actually disabled.
186         // The RestrictedSwitchPreference will take care of transparently informing the user that
187         // the setting was disabled by their admin
188         if (isDisabledByAdmin()) {
189             return false;
190         }
191 
192         long currentlyAllowedNetworkTypes = mTelephonyManager.getAllowedNetworkTypesForReason(
193                 mTelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
194         return (currentlyAllowedNetworkTypes & BITMASK_2G) != 0;
195     }
196 
197     /**
198      * Ensure that the modem's allowed network types are configured according to the user's
199      * preference.
200      * <p>
201      * See {@link com.android.settings.core.TogglePreferenceController#setChecked(boolean)} for
202      * details.
203      *
204      * @param isChecked The toggle value that we're being requested to enforce. A value of {@code
205      *                  false} denotes that 2g will be disabled by the modem after this function
206      *                  completes, if it is not already.
207      */
208     @Override
setChecked(boolean isChecked)209     public boolean setChecked(boolean isChecked) {
210         if (isDisabledByAdmin()) {
211             return false;
212         }
213 
214         if (!SubscriptionManager.isUsableSubscriptionId(mSubId)) {
215             return false;
216         }
217         long currentlyAllowedNetworkTypes = mTelephonyManager.getAllowedNetworkTypesForReason(
218                 mTelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
219         boolean enabled = (currentlyAllowedNetworkTypes & BITMASK_2G) != 0;
220         if (enabled == isChecked) {
221             return false;
222         }
223         long newAllowedNetworkTypes = currentlyAllowedNetworkTypes;
224         if (isChecked) {
225             newAllowedNetworkTypes = currentlyAllowedNetworkTypes | BITMASK_2G;
226             Log.i(LOG_TAG, "Enabling 2g. Allowed network types: " + newAllowedNetworkTypes);
227         } else {
228             newAllowedNetworkTypes = currentlyAllowedNetworkTypes & ~BITMASK_2G;
229             Log.i(LOG_TAG, "Disabling 2g. Allowed network types: " + newAllowedNetworkTypes);
230         }
231         mTelephonyManager.setAllowedNetworkTypesForReason(
232                 mTelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedNetworkTypes);
233         mMetricsFeatureProvider.action(
234                 mContext, SettingsEnums.ACTION_2G_ENABLED, isChecked);
235         return true;
236     }
237 
isDisabledByAdmin()238     private boolean isDisabledByAdmin() {
239         return (mRestrictedPreference != null && mRestrictedPreference.isDisabledByAdmin());
240     }
241 }
242