1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.settings.datausage;
16 
17 import android.app.settings.SettingsEnums;
18 import android.content.Context;
19 import android.content.DialogInterface;
20 import android.net.NetworkTemplate;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.telephony.SubscriptionInfo;
24 import android.telephony.SubscriptionManager;
25 import android.telephony.TelephonyManager;
26 import android.util.AttributeSet;
27 import android.util.Log;
28 import android.view.View;
29 import android.widget.CompoundButton;
30 
31 import androidx.annotation.VisibleForTesting;
32 import androidx.appcompat.app.AlertDialog.Builder;
33 import androidx.preference.PreferenceViewHolder;
34 
35 import com.android.settings.R;
36 import com.android.settings.network.MobileDataEnabledListener;
37 import com.android.settings.network.ProxySubscriptionManager;
38 import com.android.settings.network.SubscriptionUtil;
39 import com.android.settings.overlay.FeatureFactory;
40 import com.android.settingslib.CustomDialogPreferenceCompat;
41 
42 /**
43  * Preference of cellular data control within Data Usage
44  */
45 public class CellDataPreference extends CustomDialogPreferenceCompat
46         implements TemplatePreference, MobileDataEnabledListener.Client {
47 
48     private static final String TAG = "CellDataPreference";
49 
50     public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
51     public boolean mChecked;
52     public boolean mMultiSimDialog;
53     private final MobileDataEnabledListener mDataStateListener;
54 
CellDataPreference(Context context, AttributeSet attrs)55     public CellDataPreference(Context context, AttributeSet attrs) {
56         super(context, attrs, androidx.preference.R.attr.switchPreferenceCompatStyle);
57         mDataStateListener = new MobileDataEnabledListener(context, this);
58     }
59 
60     @Override
onRestoreInstanceState(Parcelable s)61     protected void onRestoreInstanceState(Parcelable s) {
62         final CellDataState state = (CellDataState) s;
63         super.onRestoreInstanceState(state.getSuperState());
64         mChecked = state.mChecked;
65         mMultiSimDialog = state.mMultiSimDialog;
66         if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
67             mSubId = state.mSubId;
68             setKey(getKey() + mSubId);
69         }
70         notifyChanged();
71     }
72 
73     @Override
onSaveInstanceState()74     protected Parcelable onSaveInstanceState() {
75         final CellDataState state = new CellDataState(super.onSaveInstanceState());
76         state.mChecked = mChecked;
77         state.mMultiSimDialog = mMultiSimDialog;
78         state.mSubId = mSubId;
79         return state;
80     }
81 
82     @Override
onAttached()83     public void onAttached() {
84         super.onAttached();
85         mDataStateListener.start(mSubId);
86         getProxySubscriptionManager()
87                 .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener);
88     }
89 
90     @Override
onDetached()91     public void onDetached() {
92         mDataStateListener.stop();
93         getProxySubscriptionManager()
94                 .removeActiveSubscriptionsListener(mOnSubscriptionsChangeListener);
95         super.onDetached();
96     }
97 
98     @Override
setTemplate(NetworkTemplate template, int subId)99     public void setTemplate(NetworkTemplate template, int subId) {
100         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
101             throw new IllegalArgumentException("CellDataPreference needs a SubscriptionInfo");
102         }
103 
104         getProxySubscriptionManager()
105                 .addActiveSubscriptionsListener(mOnSubscriptionsChangeListener);
106 
107         if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
108             mSubId = subId;
109             setKey(getKey() + subId);
110         }
111         updateEnabled();
112         updateChecked();
113     }
114 
115     @VisibleForTesting
getProxySubscriptionManager()116     ProxySubscriptionManager getProxySubscriptionManager() {
117         return ProxySubscriptionManager.getInstance(getContext());
118     }
119 
120     @VisibleForTesting
getActiveSubscriptionInfo(int subId)121     SubscriptionInfo getActiveSubscriptionInfo(int subId) {
122         return getProxySubscriptionManager().getActiveSubscriptionInfo(subId);
123     }
124 
updateChecked()125     private void updateChecked() {
126         setChecked(getContext().getSystemService(TelephonyManager.class).getDataEnabled(mSubId));
127     }
128 
updateEnabled()129     private void updateEnabled() {
130         // If this subscription is not active, for example, SIM card is taken out, we disable
131         // the button.
132         setEnabled(getActiveSubscriptionInfo(mSubId) != null);
133     }
134 
135     @Override
performClick(View view)136     protected void performClick(View view) {
137         final Context context = getContext();
138         FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
139                 .action(context, SettingsEnums.ACTION_CELL_DATA_TOGGLE, !mChecked);
140         final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId);
141         final SubscriptionInfo nextSir = getActiveSubscriptionInfo(
142                 SubscriptionManager.getDefaultDataSubscriptionId());
143         if (mChecked) {
144             setMobileDataEnabled(false);
145             if (nextSir != null && currentSir != null
146                     && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
147                 disableDataForOtherSubscriptions(mSubId);
148             }
149         } else {
150             setMobileDataEnabled(true);
151         }
152     }
153 
setMobileDataEnabled(boolean enabled)154     private void setMobileDataEnabled(boolean enabled) {
155         Log.d(TAG, "setDataEnabledForReason (" + enabled + "," + mSubId + ")");
156         getContext().getSystemService(TelephonyManager.class).setDataEnabled(mSubId, enabled);
157         setChecked(enabled);
158     }
159 
setChecked(boolean checked)160     private void setChecked(boolean checked) {
161         if (mChecked == checked) return;
162         mChecked = checked;
163         notifyChanged();
164     }
165 
166     @Override
onBindViewHolder(PreferenceViewHolder holder)167     public void onBindViewHolder(PreferenceViewHolder holder) {
168         super.onBindViewHolder(holder);
169         final CompoundButton switchView =
170                 (CompoundButton) holder.findViewById(androidx.preference.R.id.switchWidget);
171         switchView.setClickable(false);
172         switchView.setChecked(mChecked);
173     }
174 
175     @Override
onPrepareDialogBuilder(Builder builder, DialogInterface.OnClickListener listener)176     protected void onPrepareDialogBuilder(Builder builder,
177             DialogInterface.OnClickListener listener) {
178         if (mMultiSimDialog) {
179             showMultiSimDialog(builder, listener);
180         } else {
181             showDisableDialog(builder, listener);
182         }
183     }
184 
showDisableDialog(Builder builder, DialogInterface.OnClickListener listener)185     private void showDisableDialog(Builder builder,
186             DialogInterface.OnClickListener listener) {
187         builder.setTitle(null)
188                 .setMessage(R.string.data_usage_disable_mobile)
189                 .setPositiveButton(android.R.string.ok, listener)
190                 .setNegativeButton(android.R.string.cancel, null);
191     }
192 
showMultiSimDialog(Builder builder, DialogInterface.OnClickListener listener)193     private void showMultiSimDialog(Builder builder,
194             DialogInterface.OnClickListener listener) {
195         final SubscriptionInfo currentSir = getActiveSubscriptionInfo(mSubId);
196         final SubscriptionInfo nextSir = getActiveSubscriptionInfo(
197                 SubscriptionManager.getDefaultDataSubscriptionId());
198 
199         final String previousName = (nextSir == null)
200             ? getContext().getResources().getString(R.string.sim_selection_required_pref)
201                 : SubscriptionUtil.getUniqueSubscriptionDisplayName(
202                         nextSir, getContext()).toString();
203 
204         builder.setTitle(R.string.sim_change_data_title);
205         builder.setMessage(getContext().getString(R.string.sim_change_data_message,
206                 String.valueOf(currentSir != null
207                     ? SubscriptionUtil.getUniqueSubscriptionDisplayName(currentSir, getContext())
208                     : null), previousName));
209 
210         builder.setPositiveButton(R.string.okay, listener);
211         builder.setNegativeButton(R.string.cancel, null);
212     }
213 
disableDataForOtherSubscriptions(int subId)214     private void disableDataForOtherSubscriptions(int subId) {
215         final SubscriptionInfo subInfo = getActiveSubscriptionInfo(subId);
216         if (subInfo != null) {
217             getContext().getSystemService(TelephonyManager.class).setDataEnabled(subId, false);
218         }
219     }
220 
221     @Override
onClick(DialogInterface dialog, int which)222     protected void onClick(DialogInterface dialog, int which) {
223         if (which != DialogInterface.BUTTON_POSITIVE) {
224             return;
225         }
226         if (mMultiSimDialog) {
227             getProxySubscriptionManager().get().setDefaultDataSubId(mSubId);
228             setMobileDataEnabled(true);
229             disableDataForOtherSubscriptions(mSubId);
230         } else {
231             // TODO: extend to modify policy enabled flag.
232             setMobileDataEnabled(false);
233         }
234     }
235 
236     @VisibleForTesting
237     final ProxySubscriptionManager.OnActiveSubscriptionChangedListener
238             mOnSubscriptionsChangeListener =
239             new ProxySubscriptionManager.OnActiveSubscriptionChangedListener() {
240                 public void onChanged() {
241                     if (DataUsageSummary.LOGD) {
242                         Log.d(TAG, "onSubscriptionsChanged");
243                     }
244                     updateEnabled();
245                 }
246             };
247 
248     /**
249      * Implementation of {@code MobileDataEnabledListener.Client}
250     */
251     @VisibleForTesting
onMobileDataEnabledChange()252     public void onMobileDataEnabledChange() {
253         updateChecked();
254     }
255 
256     public static class CellDataState extends BaseSavedState {
257         public int mSubId;
258         public boolean mChecked;
259         public boolean mMultiSimDialog;
260 
CellDataState(Parcelable base)261         public CellDataState(Parcelable base) {
262             super(base);
263         }
264 
CellDataState(Parcel source)265         public CellDataState(Parcel source) {
266             super(source);
267             mChecked = source.readByte() != 0;
268             mMultiSimDialog = source.readByte() != 0;
269             mSubId = source.readInt();
270         }
271 
272         @Override
writeToParcel(Parcel dest, int flags)273         public void writeToParcel(Parcel dest, int flags) {
274             super.writeToParcel(dest, flags);
275             dest.writeByte((byte) (mChecked ? 1 : 0));
276             dest.writeByte((byte) (mMultiSimDialog ? 1 : 0));
277             dest.writeInt(mSubId);
278         }
279 
280         public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
281             @Override
282             public CellDataState createFromParcel(Parcel source) {
283                 return new CellDataState(source);
284             }
285 
286             @Override
287             public CellDataState[] newArray(int size) {
288                 return new CellDataState[size];
289             }
290         };
291     }
292 }
293