1 /*
2  * Copyright (C) 2018 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 android.app.slice.Slice.EXTRA_TOGGLE_STATE;
20 
21 import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
22 
23 import android.app.PendingIntent;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.content.res.Resources;
28 import android.net.Uri;
29 import android.os.PersistableBundle;
30 import android.telephony.CarrierConfigManager;
31 import android.telephony.SubscriptionManager;
32 import android.telephony.ims.ImsMmTelManager;
33 import android.util.Log;
34 
35 import androidx.annotation.VisibleForTesting;
36 import androidx.core.graphics.drawable.IconCompat;
37 import androidx.slice.Slice;
38 import androidx.slice.builders.ListBuilder;
39 import androidx.slice.builders.ListBuilder.RowBuilder;
40 import androidx.slice.builders.SliceAction;
41 
42 import com.android.settings.R;
43 import com.android.settings.Utils;
44 import com.android.settings.network.ims.VolteQueryImsState;
45 import com.android.settings.slices.CustomSliceRegistry;
46 import com.android.settings.slices.SliceBroadcastReceiver;
47 
48 /**
49  * Helper class to control slices for enhanced 4g LTE settings.
50  */
51 public class Enhanced4gLteSliceHelper {
52 
53     private static final String TAG = "Enhanced4gLteSlice";
54 
55     /**
56      * Action passed for changes to enhanced 4g LTE slice (toggle).
57      */
58     public static final String ACTION_ENHANCED_4G_LTE_CHANGED =
59             "com.android.settings.mobilenetwork.action.ENHANCED_4G_LTE_CHANGED";
60 
61     /**
62      * Action for mobile network settings activity which
63      * allows setting configuration for Enhanced 4G LTE
64      * related settings
65      */
66     public static final String ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY =
67             "android.settings.NETWORK_OPERATOR_SETTINGS";
68 
69     private final Context mContext;
70 
71     /**
72      * Phone package name
73      */
74     private static final String PACKAGE_PHONE = "com.android.phone";
75 
76     /**
77      * String resource type
78      */
79     private static final String RESOURCE_TYPE_STRING = "string";
80 
81     /**
82      * Enhanced 4g lte mode title variant resource name
83      */
84     private static final String RESOURCE_ENHANCED_4G_LTE_MODE_TITLE_VARIANT =
85             "enhanced_4g_lte_mode_title_variant";
86 
87     @VisibleForTesting
Enhanced4gLteSliceHelper(Context context)88     public Enhanced4gLteSliceHelper(Context context) {
89         mContext = context;
90     }
91 
92     /**
93      * Returns Slice object for enhanced_4g_lte settings.
94      *
95      * If enhanced 4g LTE is not supported for the current carrier, this method will return slice
96      * with not supported message.
97      *
98      * If enhanced 4g LTE is not editable for the current carrier, this method will return slice
99      * with not editable message.
100      *
101      * If enhanced 4g LTE setting can be changed, this method will return the slice to toggle
102      * enhanced 4g LTE option with ACTION_ENHANCED_4G_LTE_CHANGED as endItem.
103      */
createEnhanced4gLteSlice(Uri sliceUri)104     public Slice createEnhanced4gLteSlice(Uri sliceUri) {
105         final int subId = getDefaultVoiceSubId();
106 
107         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
108             Log.d(TAG, "Invalid subscription Id");
109             return null;
110         }
111 
112         if (isCarrierConfigManagerKeyEnabled(
113                 CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL, subId, false)
114                 || !isCarrierConfigManagerKeyEnabled(
115                 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, subId,
116                 true)) {
117             Log.d(TAG, "Setting is either hidden or not editable");
118             return null;
119         }
120 
121         final VolteQueryImsState queryState = queryImsState(subId);
122         if (!queryState.isVoLteProvisioned()) {
123             Log.d(TAG, "Setting is either not provisioned or not enabled by Platform");
124             return null;
125         }
126 
127         try {
128             return getEnhanced4gLteSlice(sliceUri,
129                     queryState.isEnabledByUser(), subId);
130         } catch (IllegalArgumentException e) {
131             Log.e(TAG, "Unable to read the current Enhanced 4g LTE status", e);
132             return null;
133         }
134     }
135 
136     /**
137      * Builds a toggle slice where the intent takes you to the Enhanced 4G LTE page and the toggle
138      * enables/disables Enhanced 4G LTE mode setting.
139      */
getEnhanced4gLteSlice(Uri sliceUri, boolean isEnhanced4gLteEnabled, int subId)140     private Slice getEnhanced4gLteSlice(Uri sliceUri, boolean isEnhanced4gLteEnabled, int subId) {
141         final IconCompat icon = IconCompat.createWithResource(mContext,
142                 R.drawable.ic_launcher_settings);
143 
144         return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
145                 .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
146                 .addRow(new RowBuilder()
147                         .setTitle(getEnhanced4glteModeTitle(subId))
148                         .addEndItem(
149                                 SliceAction.createToggle(
150                                         getBroadcastIntent(ACTION_ENHANCED_4G_LTE_CHANGED),
151                                         null /* actionTitle */, isEnhanced4gLteEnabled))
152                         .setPrimaryAction(
153                                 SliceAction.createDeeplink(
154                                         getActivityIntent(ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY),
155                                         icon,
156                                         ListBuilder.ICON_IMAGE,
157                                         getEnhanced4glteModeTitle(subId))))
158                 .build();
159     }
160 
161     /**
162      * Handles Enhanced 4G LTE mode setting change from Enhanced 4G LTE slice and posts
163      * notification. Should be called when intent action is ACTION_ENHANCED_4G_LTE_CHANGED
164      *
165      * @param intent action performed
166      */
handleEnhanced4gLteChanged(Intent intent)167     public void handleEnhanced4gLteChanged(Intent intent) {
168         // skip checking when no toggle state update contained within Intent
169         final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, false);
170         if (newValue != intent.getBooleanExtra(EXTRA_TOGGLE_STATE, true)) {
171             notifyEnhanced4gLteUpdate();
172             return;
173         }
174 
175         final int subId = getDefaultVoiceSubId();
176         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
177             notifyEnhanced4gLteUpdate();
178             return;
179         }
180 
181         final VolteQueryImsState queryState = queryImsState(subId);
182         final boolean currentValue = queryState.isEnabledByUser()
183                 && queryState.isAllowUserControl();
184         if (newValue == currentValue) {
185             notifyEnhanced4gLteUpdate();
186             return;
187         }
188 
189         // isVoLteProvisioned() is the last item to check since it might block the main thread
190         if (queryState.isVoLteProvisioned()) {
191             setEnhanced4gLteModeSetting(subId, newValue);
192         }
193         notifyEnhanced4gLteUpdate();
194     }
195 
notifyEnhanced4gLteUpdate()196     private void notifyEnhanced4gLteUpdate() {
197         // notify change in slice in any case to get re-queried. This would result in displaying
198         // appropriate message with the updated setting.
199         mContext.getContentResolver().notifyChange(CustomSliceRegistry.ENHANCED_4G_SLICE_URI, null);
200     }
201 
202     @VisibleForTesting
setEnhanced4gLteModeSetting(int subId, boolean isEnabled)203     void setEnhanced4gLteModeSetting(int subId, boolean isEnabled) {
204         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
205             return;
206         }
207         final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
208         if (imsMmTelManager == null) {
209             return;
210         }
211         try {
212             imsMmTelManager.setAdvancedCallingSettingEnabled(isEnabled);
213         } catch (IllegalArgumentException exception) {
214             Log.w(TAG, "Unable to change the Enhanced 4g LTE to " + isEnabled + ". subId=" + subId,
215                     exception);
216         }
217     }
218 
getEnhanced4glteModeTitle(int subId)219     private CharSequence getEnhanced4glteModeTitle(int subId) {
220         CharSequence ret = mContext.getText(R.string.enhanced_4g_lte_mode_title);
221         try {
222             if (isCarrierConfigManagerKeyEnabled(
223                     CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL,
224                     subId,
225                     false)) {
226                 final PackageManager manager = mContext.getPackageManager();
227                 final Resources resources = manager.getResourcesForApplication(
228                         PACKAGE_PHONE);
229                 final int resId = resources.getIdentifier(
230                         RESOURCE_ENHANCED_4G_LTE_MODE_TITLE_VARIANT,
231                         RESOURCE_TYPE_STRING, PACKAGE_PHONE);
232                 ret = resources.getText(resId);
233             }
234         } catch (PackageManager.NameNotFoundException e) {
235             Log.e(TAG, "package name not found");
236         }
237         return ret;
238     }
239 
240     /**
241      * Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise.
242      */
isCarrierConfigManagerKeyEnabled(String key, int subId, boolean defaultValue)243     private boolean isCarrierConfigManagerKeyEnabled(String key,
244             int subId, boolean defaultValue) {
245         final CarrierConfigManager configManager = getCarrierConfigManager();
246         boolean ret = defaultValue;
247         if (configManager != null) {
248             final PersistableBundle bundle = configManager.getConfigForSubId(subId);
249             if (bundle != null) {
250                 ret = bundle.getBoolean(key, defaultValue);
251             }
252         }
253         return ret;
254     }
255 
getCarrierConfigManager()256     protected CarrierConfigManager getCarrierConfigManager() {
257         return mContext.getSystemService(CarrierConfigManager.class);
258     }
259 
getBroadcastIntent(String action)260     private PendingIntent getBroadcastIntent(String action) {
261         final Intent intent = new Intent(action);
262         intent.setClass(mContext, SliceBroadcastReceiver.class);
263         return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
264                 PendingIntent.FLAG_CANCEL_CURRENT);
265     }
266 
267     /**
268      * Returns the current default voice subId obtained from SubscriptionManager
269      */
getDefaultVoiceSubId()270     protected int getDefaultVoiceSubId() {
271         return SubscriptionManager.getDefaultVoiceSubscriptionId();
272     }
273 
274     /**
275      * Returns PendingIntent to start activity specified by action
276      */
getActivityIntent(String action)277     private PendingIntent getActivityIntent(String action) {
278         final Intent intent = new Intent(action);
279         intent.setPackage(SETTINGS_PACKAGE_NAME);
280         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
281         return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
282     }
283 
284     @VisibleForTesting
queryImsState(int subId)285     VolteQueryImsState queryImsState(int subId) {
286         return new VolteQueryImsState(mContext, subId);
287     }
288 }
289 
290