blob: e1ceab086f9050b2e033e1cc457934d13c6e22b1 [file] [log] [blame]
Malcolm Chen9ef63c62017-06-20 16:53:01 -07001/*
2 * Copyright (C) 2017 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
17package com.android.phone;
18
19import android.app.AlertDialog;
20import android.content.Context;
21import android.content.DialogInterface;
22import android.database.ContentObserver;
23import android.net.Uri;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Parcel;
27import android.os.Parcelable;
28import android.preference.DialogPreference;
29import android.preference.PreferenceScreen;
30import android.provider.Settings.Global;
31import android.telephony.SubscriptionInfo;
32import android.telephony.SubscriptionManager;
33import android.telephony.TelephonyManager;
34import android.util.AttributeSet;
35import android.util.Log;
36import android.view.View;
37import android.widget.Checkable;
38
Malcolm Chen66c618e2017-07-24 16:44:32 -070039import com.android.internal.logging.MetricsLogger;
40import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
41
Malcolm Chen9ef63c62017-06-20 16:53:01 -070042import java.util.List;
43
44/**
45 * Customized Preference to enable / disable mobile data.
46 * Basically copy of with com.android.settings.CellDataPreference.
47 */
48public class MobileDataPreference extends DialogPreference {
49
50 private static final boolean DBG = false;
51 private static final String TAG = "MobileDataPreference";
52
53 public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
54 public boolean mChecked;
Malcolm Chena077b5d2018-08-14 18:17:05 -070055 // Whether to show the dialog to ask switching default data subscription.
56 // Should be true only when a multi-sim phone only supports data connection on a single phone,
57 // and user is enabling data on the non-default phone.
Malcolm Chen9ef63c62017-06-20 16:53:01 -070058 public boolean mMultiSimDialog;
59 private TelephonyManager mTelephonyManager;
60 private SubscriptionManager mSubscriptionManager;
61
62 public MobileDataPreference(Context context, AttributeSet attrs) {
63 super(context, attrs, com.android.internal.R.attr.switchPreferenceStyle);
64 }
65
Malcolm Chen457bb172018-06-06 20:20:53 -070066 // Must be called to avoid binder leakage.
67 void dispose() {
68 mListener.setListener(false, mSubId, getContext());
69 }
70
Malcolm Chen9ef63c62017-06-20 16:53:01 -070071 @Override
72 protected void onRestoreInstanceState(Parcelable s) {
73 CellDataState state = (CellDataState) s;
74 super.onRestoreInstanceState(state.getSuperState());
75 mTelephonyManager = TelephonyManager.from(getContext());
76 mChecked = state.mChecked;
77 mMultiSimDialog = state.mMultiSimDialog;
78 if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
79 mSubId = state.mSubId;
80 setKey(getKey() + mSubId);
81 }
82 notifyChanged();
83 }
84
85 @Override
86 protected Parcelable onSaveInstanceState() {
87 CellDataState state = new CellDataState(super.onSaveInstanceState());
88 state.mChecked = mChecked;
89 state.mMultiSimDialog = mMultiSimDialog;
90 state.mSubId = mSubId;
91 return state;
92 }
93
94 @Override
95 protected void onAttachedToActivity() {
96 super.onAttachedToActivity();
97 mListener.setListener(true, mSubId, getContext());
98 }
99
100 @Override
101 protected void onPrepareForRemoval() {
102 mListener.setListener(false, mSubId, getContext());
103 super.onPrepareForRemoval();
104 }
105
106 /**
107 * Initialize this preference with subId.
108 */
109 public void initialize(int subId) {
110 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
111 throw new IllegalArgumentException("MobileDataPreference needs a SubscriptionInfo");
112 }
113 mSubscriptionManager = SubscriptionManager.from(getContext());
114 mTelephonyManager = TelephonyManager.from(getContext());
115 if (mSubId != subId) {
116 mSubId = subId;
117 setKey(getKey() + subId);
118 }
119 updateChecked();
120 }
121
122 private void updateChecked() {
123 setChecked(mTelephonyManager.getDataEnabled(mSubId));
124 }
125
126 @Override
127 public void performClick(PreferenceScreen preferenceScreen) {
Sanket Padawe4efc9242017-08-13 23:33:07 -0700128 if (!isEnabled() || !SubscriptionManager.isValidSubscriptionId(mSubId)) {
129 return;
130 }
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700131 final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(
132 mSubId);
133 final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
Malcolm Chena077b5d2018-08-14 18:17:05 -0700134 final boolean isMultiSim = (mTelephonyManager.getSimCount() > 1);
135 final boolean isMultipleDataOnCapable =
136 (mTelephonyManager.getNumberOfModemsWithSimultaneousDataConnections() > 1);
137 final boolean isDefaultDataSubscription = (nextSir != null && currentSir != null
138 && currentSir.getSubscriptionId() == nextSir.getSubscriptionId());
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700139 if (mChecked) {
Malcolm Chena077b5d2018-08-14 18:17:05 -0700140 if (!isMultiSim) {
141 // disabling data; show confirmation dialog which eventually
142 // calls setMobileDataEnabled() once user confirms.
143 mMultiSimDialog = false;
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700144 super.performClick(preferenceScreen);
145 } else {
Malcolm Chena077b5d2018-08-14 18:17:05 -0700146 // Don't show any dialog.
147 setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
148 }
149 } else {
150 if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) {
151 // enabling data and setting to default; show confirmation dialog which eventually
152 // calls setMobileDataEnabled() once user confirms.
153 mMultiSimDialog = true;
154 super.performClick(preferenceScreen);
155 } else {
156 // Don't show any dialog.
157 setMobileDataEnabled(true /* enabled */, false /* disableOtherSubscriptions */);
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700158 }
159 }
160 }
161
Malcolm Chena077b5d2018-08-14 18:17:05 -0700162 private void setMobileDataEnabled(boolean enabled, boolean disableOtherSubscriptions) {
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700163 if (DBG) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")");
Malcolm Chen66c618e2017-07-24 16:44:32 -0700164
165 MetricsLogger.action(getContext(), MetricsEvent.ACTION_MOBILE_NETWORK_MOBILE_DATA_TOGGLE,
166 enabled);
167
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700168 mTelephonyManager.setDataEnabled(mSubId, enabled);
Malcolm Chena077b5d2018-08-14 18:17:05 -0700169
170 if (disableOtherSubscriptions) {
171 disableDataForOtherSubscriptions(mSubId);
172 }
173
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700174 setChecked(enabled);
175 }
176
177 private void setChecked(boolean checked) {
178 if (mChecked == checked) return;
179 mChecked = checked;
180 notifyChanged();
181 }
182
183 @Override
184 protected void onBindView(View view) {
185 super.onBindView(view);
186 View checkableView = view.findViewById(com.android.internal.R.id.switch_widget);
187 checkableView.setClickable(false);
188 ((Checkable) checkableView).setChecked(mChecked);
189 }
190
191 @Override
192 protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
193 if (mMultiSimDialog) {
194 showMultiSimDialog(builder);
195 } else {
196 showDisableDialog(builder);
197 }
198 }
199
200 private void showDisableDialog(AlertDialog.Builder builder) {
201 builder.setTitle(null)
202 .setMessage(R.string.data_usage_disable_mobile)
203 .setPositiveButton(android.R.string.ok, this)
204 .setNegativeButton(android.R.string.cancel, null);
205 }
206
207 private void showMultiSimDialog(AlertDialog.Builder builder) {
208 final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
209 final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
210
211 final String previousName = (nextSir == null)
212 ? getContext().getResources().getString(R.string.sim_selection_required_pref)
213 : nextSir.getDisplayName().toString();
214
215 builder.setTitle(R.string.sim_change_data_title);
216 builder.setMessage(getContext().getString(R.string.sim_change_data_message,
217 String.valueOf(currentSir != null ? currentSir.getDisplayName() : null),
218 previousName));
219
220 builder.setPositiveButton(R.string.ok, this);
221 builder.setNegativeButton(R.string.cancel, null);
222 }
223
224 private void disableDataForOtherSubscriptions(int subId) {
225 List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
226 if (subInfoList != null) {
227 for (SubscriptionInfo subInfo : subInfoList) {
228 if (subInfo.getSubscriptionId() != subId) {
229 mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false);
230 }
231 }
232 }
233 }
234
235 @Override
236 public void onClick(DialogInterface dialog, int which) {
237 if (which != DialogInterface.BUTTON_POSITIVE) {
238 return;
239 }
240 if (mMultiSimDialog) {
241 mSubscriptionManager.setDefaultDataSubId(mSubId);
Malcolm Chena077b5d2018-08-14 18:17:05 -0700242 setMobileDataEnabled(true /* enabled */, true /* disableOtherSubscriptions */);
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700243 } else {
244 // TODO: extend to modify policy enabled flag.
Malcolm Chena077b5d2018-08-14 18:17:05 -0700245 setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */);
Malcolm Chen9ef63c62017-06-20 16:53:01 -0700246 }
247 }
248
249 private final DataStateListener mListener = new DataStateListener() {
250 @Override
251 public void onChange(boolean selfChange) {
252 updateChecked();
253 }
254 };
255
256 /**
257 * Listener that listens mobile data state change.
258 */
259 public abstract static class DataStateListener extends ContentObserver {
260 public DataStateListener() {
261 super(new Handler(Looper.getMainLooper()));
262 }
263
264 /**
265 * Set / Unset data state listening, specifying subId.
266 */
267 public void setListener(boolean listening, int subId, Context context) {
268 if (listening) {
269 Uri uri = Global.getUriFor(Global.MOBILE_DATA);
270 if (TelephonyManager.getDefault().getSimCount() != 1) {
271 uri = Global.getUriFor(Global.MOBILE_DATA + subId);
272 }
273 context.getContentResolver().registerContentObserver(uri, false, this);
274 } else {
275 context.getContentResolver().unregisterContentObserver(this);
276 }
277 }
278 }
279
280 /**
281 * Class that represents state of mobile data state.
282 * Used by onSaveInstanceState and onRestoreInstanceState.
283 */
284 public static class CellDataState extends BaseSavedState {
285 public int mSubId;
286 public boolean mChecked;
287 public boolean mMultiSimDialog;
288
289 public CellDataState(Parcelable base) {
290 super(base);
291 }
292
293 public CellDataState(Parcel source) {
294 super(source);
295 mChecked = source.readByte() != 0;
296 mMultiSimDialog = source.readByte() != 0;
297 mSubId = source.readInt();
298 }
299
300 @Override
301 public void writeToParcel(Parcel dest, int flags) {
302 super.writeToParcel(dest, flags);
303 dest.writeByte((byte) (mChecked ? 1 : 0));
304 dest.writeByte((byte) (mMultiSimDialog ? 1 : 0));
305 dest.writeInt(mSubId);
306 }
307
308 public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
309 @Override
310 public CellDataState createFromParcel(Parcel source) {
311 return new CellDataState(source);
312 }
313
314 @Override
315 public CellDataState[] newArray(int size) {
316 return new CellDataState[size];
317 }
318 };
319 }
320}