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 android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.os.PersistableBundle; 22 import android.telephony.CarrierConfigManager; 23 import android.telephony.SubscriptionManager; 24 import android.telephony.TelephonyCallback; 25 import android.telephony.TelephonyManager; 26 import android.telephony.ims.ImsMmTelManager; 27 import android.util.Log; 28 29 import androidx.annotation.VisibleForTesting; 30 import androidx.preference.Preference; 31 import androidx.preference.PreferenceScreen; 32 import androidx.preference.TwoStatePreference; 33 34 import com.android.internal.telephony.flags.Flags; 35 import com.android.settings.network.CarrierConfigCache; 36 import com.android.settings.network.MobileDataEnabledListener; 37 import com.android.settings.network.ims.VolteQueryImsState; 38 import com.android.settings.network.ims.VtQueryImsState; 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 /** 44 * Preference controller for "Video Calling" 45 */ 46 public class VideoCallingPreferenceController extends TelephonyTogglePreferenceController implements 47 LifecycleObserver, OnStart, OnStop, 48 MobileDataEnabledListener.Client, 49 Enhanced4gBasePreferenceController.On4gLteUpdateListener { 50 51 private static final String TAG = "VideoCallingPreference"; 52 53 private Preference mPreference; 54 private PhoneTelephonyCallback mTelephonyCallback; 55 @VisibleForTesting 56 Integer mCallState; 57 private MobileDataEnabledListener mDataContentObserver; 58 private CallingPreferenceCategoryController mCallingPreferenceCategoryController; 59 VideoCallingPreferenceController(Context context, String key)60 public VideoCallingPreferenceController(Context context, String key) { 61 super(context, key); 62 mDataContentObserver = new MobileDataEnabledListener(context, this); 63 mTelephonyCallback = new PhoneTelephonyCallback(); 64 } 65 66 @Override getAvailabilityStatus(int subId)67 public int getAvailabilityStatus(int subId) { 68 return SubscriptionManager.isValidSubscriptionId(subId) 69 && isVideoCallEnabled(subId) 70 ? AVAILABLE 71 : CONDITIONALLY_UNAVAILABLE; 72 } 73 74 @Override displayPreference(PreferenceScreen screen)75 public void displayPreference(PreferenceScreen screen) { 76 super.displayPreference(screen); 77 mPreference = screen.findPreference(getPreferenceKey()); 78 } 79 80 @Override onStart()81 public void onStart() { 82 mTelephonyCallback.register(mContext, mSubId); 83 mDataContentObserver.start(mSubId); 84 } 85 86 @Override onStop()87 public void onStop() { 88 mTelephonyCallback.unregister(); 89 mDataContentObserver.stop(); 90 } 91 92 @Override updateState(Preference preference)93 public void updateState(Preference preference) { 94 super.updateState(preference); 95 if ((mCallState == null) || (preference == null)) { 96 Log.d(TAG, "Skip update under mCallState=" + mCallState); 97 return; 98 } 99 final TwoStatePreference switchPreference = (TwoStatePreference) preference; 100 final boolean videoCallEnabled = isVideoCallEnabled(mSubId); 101 switchPreference.setVisible(videoCallEnabled); 102 mCallingPreferenceCategoryController 103 .updateChildVisible(getPreferenceKey(), videoCallEnabled); 104 if (videoCallEnabled) { 105 final boolean videoCallEditable = queryVoLteState(mSubId).isEnabledByUser() 106 && queryImsState(mSubId).isAllowUserControl(); 107 preference.setEnabled(videoCallEditable 108 && mCallState == TelephonyManager.CALL_STATE_IDLE); 109 switchPreference.setChecked(videoCallEditable && isChecked()); 110 } 111 } 112 113 @Override setChecked(boolean isChecked)114 public boolean setChecked(boolean isChecked) { 115 if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { 116 return false; 117 } 118 final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId); 119 if (imsMmTelManager == null) { 120 return false; 121 } 122 try { 123 imsMmTelManager.setVtSettingEnabled(isChecked); 124 return true; 125 } catch (IllegalArgumentException exception) { 126 Log.w(TAG, "Unable to set VT status " + isChecked + ". subId=" + mSubId, 127 exception); 128 } 129 return false; 130 } 131 132 @Override isChecked()133 public boolean isChecked() { 134 return queryImsState(mSubId).isEnabledByUser(); 135 } 136 137 @VisibleForTesting isImsSupported()138 protected boolean isImsSupported() { 139 return mContext.getPackageManager().hasSystemFeature( 140 PackageManager.FEATURE_TELEPHONY_IMS); 141 } 142 143 /** 144 * Init instance of VideoCallingPreferenceController. 145 */ init( int subId, CallingPreferenceCategoryController callingPreferenceCategoryController)146 public VideoCallingPreferenceController init( 147 int subId, CallingPreferenceCategoryController callingPreferenceCategoryController) { 148 mSubId = subId; 149 mCallingPreferenceCategoryController = callingPreferenceCategoryController; 150 151 return this; 152 } 153 154 @VisibleForTesting isVideoCallEnabled(int subId)155 boolean isVideoCallEnabled(int subId) { 156 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 157 return false; 158 } 159 160 final PersistableBundle carrierConfig = 161 CarrierConfigCache.getInstance(mContext).getConfigForSubId(subId); 162 if (carrierConfig == null) { 163 return false; 164 } 165 166 if (!carrierConfig.getBoolean( 167 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS) 168 && (!mContext.getSystemService(TelephonyManager.class) 169 .createForSubscriptionId(subId).isDataEnabled())) { 170 return false; 171 } 172 173 return isImsSupported() && queryImsState(subId).isReadyToVideoCall(); 174 } 175 176 @Override on4gLteUpdated()177 public void on4gLteUpdated() { 178 updateState(mPreference); 179 } 180 181 private class PhoneTelephonyCallback extends TelephonyCallback implements 182 TelephonyCallback.CallStateListener { 183 184 private TelephonyManager mTelephonyManager; 185 186 @Override onCallStateChanged(int state)187 public void onCallStateChanged(int state) { 188 mCallState = state; 189 updateState(mPreference); 190 } 191 register(Context context, int subId)192 public void register(Context context, int subId) { 193 mTelephonyManager = context.getSystemService(TelephonyManager.class); 194 if (SubscriptionManager.isValidSubscriptionId(subId)) { 195 mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId); 196 } 197 // assign current call state so that it helps to show correct preference state even 198 // before first onCallStateChanged() by initial registration. 199 if (Flags.enforceTelephonyFeatureMappingForPublicApis()) { 200 try { 201 mCallState = mTelephonyManager.getCallState(subId); 202 } catch (UnsupportedOperationException e) { 203 // Device doesn't support FEATURE_TELEPHONY_CALLING 204 mCallState = TelephonyManager.CALL_STATE_IDLE; 205 } 206 } else { 207 mCallState = mTelephonyManager.getCallState(subId); 208 } 209 mTelephonyManager.registerTelephonyCallback(context.getMainExecutor(), this); 210 } 211 unregister()212 public void unregister() { 213 mCallState = null; 214 mTelephonyManager.unregisterTelephonyCallback(this); 215 } 216 } 217 218 /** 219 * Implementation of MobileDataEnabledListener.Client 220 */ onMobileDataEnabledChange()221 public void onMobileDataEnabledChange() { 222 updateState(mPreference); 223 } 224 225 @VisibleForTesting queryImsState(int subId)226 VtQueryImsState queryImsState(int subId) { 227 return new VtQueryImsState(mContext, subId); 228 } 229 230 @VisibleForTesting queryVoLteState(int subId)231 VolteQueryImsState queryVoLteState(int subId) { 232 return new VolteQueryImsState(mContext, subId); 233 } 234 } 235