1 /*
2  * Copyright (C) 2019 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.car.settings.network;
18 
19 import android.car.drivingstate.CarUxRestrictions;
20 import android.content.Context;
21 import android.database.ContentObserver;
22 import android.net.Uri;
23 import android.os.Handler;
24 import android.os.Looper;
25 import android.provider.Settings;
26 import android.telephony.SubscriptionInfo;
27 import android.telephony.SubscriptionManager;
28 import android.telephony.TelephonyManager;
29 
30 import androidx.annotation.VisibleForTesting;
31 import androidx.preference.TwoStatePreference;
32 
33 import com.android.car.settings.R;
34 import com.android.car.settings.common.ConfirmationDialogFragment;
35 import com.android.car.settings.common.FragmentController;
36 import com.android.car.settings.common.PreferenceController;
37 
38 /**
39  * Business logic to control the toggle that enables/disables usage of mobile data. Does not have
40  * support for multi-sim.
41  */
42 public class MobileDataTogglePreferenceController extends
43         PreferenceController<TwoStatePreference> implements
44         MobileNetworkUpdateManager.MobileNetworkUpdateListener {
45 
46     @VisibleForTesting
47     static final String DISABLE_DIALOG_TAG = ConfirmationDialogFragment.TAG + "_DisableData";
48     @VisibleForTesting
49     static final String ENABLE_MULTISIM_DIALOG_TAG =
50             ConfirmationDialogFragment.TAG + "_EnableMultisim";
51 
52     private final SubscriptionManager mSubscriptionManager;
53     private TelephonyManager mTelephonyManager;
54     private int mSubId;
55 
56     private final ContentObserver mMobileDataChangeObserver = new ContentObserver(
57             new Handler(Looper.getMainLooper())) {
58         @Override
59         public void onChange(boolean selfChange) {
60             super.onChange(selfChange);
61             refreshUi();
62         }
63     };
64 
65     private final ConfirmationDialogFragment.ConfirmListener mConfirmDisableListener =
66             arguments -> setMobileDataEnabled(
67                     /* enabled= */ false, /* disableOtherSubscriptions= */ false);
68     private final ConfirmationDialogFragment.ConfirmListener mConfirmMultiSimListener =
69             arguments -> setMobileDataEnabled(
70                     /* enabled= */ true, /* disableOtherSubscriptions= */ true);
71     private final ConfirmationDialogFragment.RejectListener mRejectRefreshListener =
72             arguments -> refreshUi();
73 
MobileDataTogglePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)74     public MobileDataTogglePreferenceController(Context context, String preferenceKey,
75             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
76         super(context, preferenceKey, fragmentController, uxRestrictions);
77         mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class);
78     }
79 
80     @Override
getPreferenceType()81     protected Class<TwoStatePreference> getPreferenceType() {
82         return TwoStatePreference.class;
83     }
84 
85     /** Sets the subscription id to be controlled by this controller. */
setSubId(int subId)86     public void setSubId(int subId) {
87         mSubId = subId;
88         mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
89     }
90 
91     @Override
getAvailabilityStatus()92     protected int getAvailabilityStatus() {
93         return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID ? AVAILABLE
94                 : CONDITIONALLY_UNAVAILABLE;
95     }
96 
97     @Override
onCreateInternal()98     protected void onCreateInternal() {
99         ConfirmationDialogFragment.resetListeners(
100                 (ConfirmationDialogFragment) getFragmentController().findDialogByTag(
101                         DISABLE_DIALOG_TAG), mConfirmDisableListener, mRejectRefreshListener);
102 
103         ConfirmationDialogFragment.resetListeners(
104                 (ConfirmationDialogFragment) getFragmentController().findDialogByTag(
105                         ENABLE_MULTISIM_DIALOG_TAG), mConfirmMultiSimListener,
106                 mRejectRefreshListener);
107     }
108 
109     @Override
onStartInternal()110     protected void onStartInternal() {
111         if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
112             getContext().getContentResolver().registerContentObserver(getObservableUri(mSubId),
113                     /* notifyForDescendants= */ false, mMobileDataChangeObserver);
114         }
115     }
116 
117     @Override
onStopInternal()118     protected void onStopInternal() {
119         if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
120             getContext().getContentResolver().unregisterContentObserver(mMobileDataChangeObserver);
121         }
122     }
123 
124     @Override
updateState(TwoStatePreference preference)125     protected void updateState(TwoStatePreference preference) {
126         preference.setEnabled(!isOpportunistic());
127         preference.setChecked(
128                 mTelephonyManager != null ? mTelephonyManager.isDataEnabled() : false);
129     }
130 
131     @Override
handlePreferenceChanged(TwoStatePreference preference, Object newValue)132     protected boolean handlePreferenceChanged(TwoStatePreference preference, Object newValue) {
133         boolean newToggleValue = (boolean) newValue;
134         boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
135         int defaultSubId = mSubscriptionManager.getDefaultDataSubscriptionId();
136         boolean needToDisableOthers = mSubscriptionManager.isActiveSubscriptionId(defaultSubId)
137                 && mSubId != defaultSubId;
138 
139         if (!newToggleValue && !isMultiSim) {
140             getFragmentController().showDialog(getConfirmDataDisableDialog(), DISABLE_DIALOG_TAG);
141         } else if (newToggleValue && isMultiSim && needToDisableOthers) {
142             getFragmentController().showDialog(getConfirmMultisimEnableDialog(),
143                     ENABLE_MULTISIM_DIALOG_TAG);
144         } else {
145             setMobileDataEnabled(newToggleValue, /* disableOtherSubscriptions= */ false);
146         }
147         return false;
148     }
149 
150     @Override
onMobileNetworkUpdated(int subId)151     public void onMobileNetworkUpdated(int subId) {
152         setSubId(subId);
153         refreshUi();
154     }
155 
setMobileDataEnabled(boolean enabled, boolean disableOtherSubscriptions)156     private void setMobileDataEnabled(boolean enabled, boolean disableOtherSubscriptions) {
157         NetworkUtils.setMobileDataEnabled(getContext(), mSubId, enabled, disableOtherSubscriptions);
158         refreshUi();
159     }
160 
isOpportunistic()161     private boolean isOpportunistic() {
162         SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
163         return info != null && info.isOpportunistic();
164     }
165 
getObservableUri(int subId)166     private Uri getObservableUri(int subId) {
167         Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
168         if (TelephonyManager.from(getContext()).getSimCount() != 1) {
169             uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId);
170         }
171         return uri;
172     }
173 
getConfirmDataDisableDialog()174     private ConfirmationDialogFragment getConfirmDataDisableDialog() {
175         return new ConfirmationDialogFragment.Builder(getContext())
176                 .setMessage(R.string.confirm_mobile_data_disable)
177                 .setPositiveButton(android.R.string.ok, mConfirmDisableListener)
178                 .setNegativeButton(android.R.string.cancel, mRejectRefreshListener)
179                 .build();
180     }
181 
getConfirmMultisimEnableDialog()182     private ConfirmationDialogFragment getConfirmMultisimEnableDialog() {
183         SubscriptionInfo previousSubInfo =
184                 mSubscriptionManager.getDefaultDataSubscriptionInfo();
185         SubscriptionInfo newSubInfo =
186                 mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
187 
188         String previousName = (previousSubInfo == null)
189                 ? getContext().getResources().getString(R.string.sim_selection_required_pref)
190                 : previousSubInfo.getDisplayName().toString();
191 
192         String newName = (newSubInfo == null)
193                 ? getContext().getResources().getString(R.string.sim_selection_required_pref)
194                 : newSubInfo.getDisplayName().toString();
195 
196         return new ConfirmationDialogFragment.Builder(getContext())
197                 .setTitle(getContext().getString(R.string.sim_change_data_title, newName))
198                 .setMessage(getContext().getString(R.string.sim_change_data_message,
199                         newName, previousName))
200                 .setPositiveButton(getContext().getString(R.string.sim_change_data_ok, newName),
201                         mConfirmMultiSimListener)
202                 .setNegativeButton(android.R.string.cancel, mRejectRefreshListener)
203                 .build();
204     }
205 }
206