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