1 /*
2  * Copyright (C) 2021 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.content.Context;
20 import android.os.PersistableBundle;
21 import android.telephony.CarrierConfigManager;
22 import android.telephony.SubscriptionManager;
23 import android.telephony.TelephonyCallback;
24 import android.telephony.TelephonyManager;
25 import android.util.Log;
26 
27 import androidx.annotation.VisibleForTesting;
28 import androidx.preference.Preference;
29 import androidx.preference.PreferenceScreen;
30 import androidx.preference.SwitchPreference;
31 
32 import com.android.internal.telephony.util.ArrayUtils;
33 import com.android.settingslib.core.lifecycle.LifecycleObserver;
34 import com.android.settingslib.core.lifecycle.events.OnStart;
35 import com.android.settingslib.core.lifecycle.events.OnStop;
36 
37 /**
38  * Preference controller for "Enhanced 4G LTE"
39  */
40 public class NrAdvancedCallingPreferenceController extends TelephonyTogglePreferenceController
41         implements LifecycleObserver, OnStart, OnStop {
42 
43     private static final String TAG = "VoNrSettings";
44 
45     @VisibleForTesting
46     Preference mPreference;
47     private TelephonyManager mTelephonyManager;
48     private PhoneCallStateTelephonyCallback mTelephonyCallback;
49     private boolean mIsVonrEnabledFromCarrierConfig = false;
50     private boolean mIsVonrVisibleFromCarrierConfig = false;
51     private boolean mIsNrEnableFromCarrierConfig = false;
52     private boolean mHas5gCapability = false;
53     private Integer mCallState;
54 
NrAdvancedCallingPreferenceController(Context context, String key)55     public NrAdvancedCallingPreferenceController(Context context, String key) {
56         super(context, key);
57         mTelephonyManager = context.getSystemService(TelephonyManager.class);
58     }
59 
60     /**
61      * Initial this PreferenceController.
62      * @param subId The subscription Id.
63      * @return This PreferenceController.
64      */
init(int subId)65     public NrAdvancedCallingPreferenceController init(int subId) {
66         Log.d(TAG, "init: ");
67         if (mTelephonyCallback == null) {
68             mTelephonyCallback = new PhoneCallStateTelephonyCallback();
69         }
70 
71         mSubId = subId;
72 
73         if (mTelephonyManager == null) {
74             mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
75         }
76         if (SubscriptionManager.isValidSubscriptionId(subId)) {
77             mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
78         }
79         long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily();
80         mHas5gCapability =
81                 (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0;
82 
83         PersistableBundle carrierConfig = getCarrierConfigForSubId(subId);
84         if (carrierConfig == null) {
85             return this;
86         }
87         mIsVonrEnabledFromCarrierConfig = carrierConfig.getBoolean(
88             CarrierConfigManager.KEY_VONR_ENABLED_BOOL);
89 
90         mIsVonrVisibleFromCarrierConfig = carrierConfig.getBoolean(
91                 CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL);
92 
93         int[] nrAvailabilities = carrierConfig.getIntArray(
94                 CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
95         mIsNrEnableFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities);
96 
97         Log.d(TAG, "mHas5gCapability: " + mHas5gCapability
98                 + ",mIsNrEnabledFromCarrierConfig: " + mIsNrEnableFromCarrierConfig
99                 + ",mIsVonrEnabledFromCarrierConfig: " + mIsVonrEnabledFromCarrierConfig
100                 + ",mIsVonrVisibleFromCarrierConfig: " + mIsVonrVisibleFromCarrierConfig);
101         return this;
102     }
103 
104     @Override
getAvailabilityStatus(int subId)105     public int getAvailabilityStatus(int subId) {
106         init(subId);
107 
108         if (mHas5gCapability
109                 && mIsNrEnableFromCarrierConfig
110                 && mIsVonrEnabledFromCarrierConfig
111                 && mIsVonrVisibleFromCarrierConfig) {
112             return AVAILABLE;
113         }
114         return CONDITIONALLY_UNAVAILABLE;
115     }
116 
117     @Override
displayPreference(PreferenceScreen screen)118     public void displayPreference(PreferenceScreen screen) {
119         super.displayPreference(screen);
120         mPreference = screen.findPreference(getPreferenceKey());
121     }
122 
123     @Override
onStart()124     public void onStart() {
125         if (mTelephonyCallback == null) {
126             return;
127         }
128         mTelephonyCallback.register(mTelephonyManager);
129     }
130 
131     @Override
onStop()132     public void onStop() {
133         if (mTelephonyCallback == null) {
134             return;
135         }
136         mTelephonyCallback.unregister();
137     }
138 
139     @Override
updateState(Preference preference)140     public void updateState(Preference preference) {
141         super.updateState(preference);
142         if (preference == null) {
143             return;
144         }
145         final SwitchPreference switchPreference = (SwitchPreference) preference;
146         switchPreference.setEnabled(isUserControlAllowed());
147     }
148 
149     @Override
setChecked(boolean isChecked)150     public boolean setChecked(boolean isChecked) {
151         if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
152             return false;
153         }
154         Log.d(TAG, "setChecked: " + isChecked);
155         int result = mTelephonyManager.setVoNrEnabled(isChecked);
156         if (result == TelephonyManager.ENABLE_VONR_SUCCESS) {
157             return true;
158         }
159         Log.d(TAG, "Fail to set VoNR result= " + result + ". subId=" + mSubId);
160         return false;
161     }
162 
163     @Override
isChecked()164     public boolean isChecked() {
165         return mTelephonyManager.isVoNrEnabled();
166     }
167 
168     @VisibleForTesting
isCallStateIdle()169     protected boolean isCallStateIdle() {
170         return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE);
171     }
172 
isUserControlAllowed()173     private boolean isUserControlAllowed() {
174         return isCallStateIdle();
175     }
176 
177     private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
178             TelephonyCallback.CallStateListener {
179 
180         private TelephonyManager mLocalTelephonyManager;
181 
182         @Override
onCallStateChanged(int state)183         public void onCallStateChanged(int state) {
184             mCallState = state;
185             updateState(mPreference);
186         }
187 
register(TelephonyManager telephonyManager)188         public void register(TelephonyManager telephonyManager) {
189             mLocalTelephonyManager = telephonyManager;
190 
191             // assign current call state so that it helps to show correct preference state even
192             // before first onCallStateChanged() by initial registration.
193             mCallState = mLocalTelephonyManager.getCallState();
194             mLocalTelephonyManager.registerTelephonyCallback(
195                     mContext.getMainExecutor(), mTelephonyCallback);
196         }
197 
unregister()198         public void unregister() {
199             mCallState = null;
200             if (mLocalTelephonyManager != null) {
201                 mLocalTelephonyManager.unregisterTelephonyCallback(this);
202             }
203         }
204     }
205 }
206