1 /*
2  * Copyright (C) 2008 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;
18 
19 import android.app.Activity;
20 import android.app.Dialog;
21 import android.app.ProgressDialog;
22 import android.app.settings.SettingsEnums;
23 import android.content.BroadcastReceiver;
24 import android.content.ContentResolver;
25 import android.content.ContentValues;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.database.Cursor;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.HandlerThread;
34 import android.os.Looper;
35 import android.os.Message;
36 import android.os.PersistableBundle;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.provider.Telephony;
40 import android.telephony.CarrierConfigManager;
41 import android.telephony.PhoneStateListener;
42 import android.telephony.PreciseDataConnectionState;
43 import android.telephony.SubscriptionInfo;
44 import android.telephony.SubscriptionManager;
45 import android.telephony.TelephonyManager;
46 import android.text.TextUtils;
47 import android.util.Log;
48 import android.view.Menu;
49 import android.view.MenuInflater;
50 import android.view.MenuItem;
51 import android.view.MotionEvent;
52 import android.widget.Toast;
53 
54 import androidx.preference.Preference;
55 import androidx.preference.PreferenceGroup;
56 
57 import com.android.settings.R;
58 import com.android.settings.RestrictedSettingsFragment;
59 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
60 
61 import java.util.ArrayList;
62 
63 public class ApnSettings extends RestrictedSettingsFragment
64         implements Preference.OnPreferenceChangeListener {
65     static final String TAG = "ApnSettings";
66 
67     public static final String EXTRA_POSITION = "position";
68     public static final String RESTORE_CARRIERS_URI =
69         "content://telephony/carriers/restore";
70     public static final String PREFERRED_APN_URI =
71         "content://telephony/carriers/preferapn";
72 
73     public static final String APN_ID = "apn_id";
74     public static final String SUB_ID = "sub_id";
75     public static final String MVNO_TYPE = "mvno_type";
76     public static final String MVNO_MATCH_DATA = "mvno_match_data";
77 
78     private static final String[] CARRIERS_PROJECTION = new String[] {
79             Telephony.Carriers._ID,
80             Telephony.Carriers.NAME,
81             Telephony.Carriers.APN,
82             Telephony.Carriers.TYPE,
83             Telephony.Carriers.MVNO_TYPE,
84             Telephony.Carriers.MVNO_MATCH_DATA,
85             Telephony.Carriers.EDITED_STATUS,
86     };
87 
88     private static final int ID_INDEX = 0;
89     private static final int NAME_INDEX = 1;
90     private static final int APN_INDEX = 2;
91     private static final int TYPES_INDEX = 3;
92     private static final int MVNO_TYPE_INDEX = 4;
93     private static final int MVNO_MATCH_DATA_INDEX = 5;
94     private static final int EDITED_INDEX = 6;
95 
96     private static final int MENU_NEW = Menu.FIRST;
97     private static final int MENU_RESTORE = Menu.FIRST + 1;
98 
99     private static final int EVENT_RESTORE_DEFAULTAPN_START = 1;
100     private static final int EVENT_RESTORE_DEFAULTAPN_COMPLETE = 2;
101 
102     private static final int DIALOG_RESTORE_DEFAULTAPN = 1001;
103 
104     private static final Uri DEFAULTAPN_URI = Uri.parse(RESTORE_CARRIERS_URI);
105     private static final Uri PREFERAPN_URI = Uri.parse(PREFERRED_APN_URI);
106 
107     private static boolean mRestoreDefaultApnMode;
108 
109     private UserManager mUserManager;
110     private TelephonyManager mTelephonyManager;
111     private RestoreApnUiHandler mRestoreApnUiHandler;
112     private RestoreApnProcessHandler mRestoreApnProcessHandler;
113     private HandlerThread mRestoreDefaultApnThread;
114     private SubscriptionInfo mSubscriptionInfo;
115     private int mSubId;
116     private int mPhoneId;
117     private String mMvnoType;
118     private String mMvnoMatchData;
119 
120     private String mSelectedKey;
121 
122     private IntentFilter mIntentFilter;
123 
124     private boolean mUnavailable;
125 
126     private boolean mHideImsApn;
127     private boolean mAllowAddingApns;
128     private boolean mHidePresetApnDetails;
129 
ApnSettings()130     public ApnSettings() {
131         super(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
132     }
133 
134     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
135         @Override
136         public void onPreciseDataConnectionStateChanged(
137                 PreciseDataConnectionState dataConnectionState) {
138             if (dataConnectionState.getState() == TelephonyManager.DATA_CONNECTED) {
139                 if (!mRestoreDefaultApnMode) {
140                     fillList();
141                 } else {
142                     showDialog(DIALOG_RESTORE_DEFAULTAPN);
143                 }
144             }
145         }
146     };
147 
148     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
149         @Override
150         public void onReceive(Context context, Intent intent) {
151             if (intent.getAction().equals(
152                     TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
153                 if (mRestoreDefaultApnMode) {
154                     return;
155                 }
156                 final int extraSubId = intent.getIntExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID,
157                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
158                 if (SubscriptionManager.isValidSubscriptionId(extraSubId)
159                         && mPhoneId == SubscriptionUtil.getPhoneId(context, extraSubId)
160                         && extraSubId != mSubId) {
161                     // subscription has changed
162                     mSubId = extraSubId;
163                     mSubscriptionInfo = getSubscriptionInfo(mSubId);
164                     restartPhoneStateListener(mSubId);
165                 }
166                 fillList();
167             }
168         }
169     };
170 
restartPhoneStateListener(int subId)171     private void restartPhoneStateListener(int subId) {
172         if (mRestoreDefaultApnMode) {
173             return;
174         }
175 
176         final TelephonyManager updatedTelephonyManager =
177                 mTelephonyManager.createForSubscriptionId(subId);
178 
179         // restart monitoring when subscription has been changed
180         mTelephonyManager.listen(mPhoneStateListener,
181                 PhoneStateListener.LISTEN_NONE);
182 
183         mTelephonyManager = updatedTelephonyManager;
184 
185         mTelephonyManager.listen(mPhoneStateListener,
186                 PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE);
187     }
188 
189     @Override
getMetricsCategory()190     public int getMetricsCategory() {
191         return SettingsEnums.APN;
192     }
193 
194     @Override
onCreate(Bundle icicle)195     public void onCreate(Bundle icicle) {
196         super.onCreate(icicle);
197         final Activity activity = getActivity();
198         mSubId = activity.getIntent().getIntExtra(SUB_ID,
199                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
200         mPhoneId = SubscriptionUtil.getPhoneId(activity, mSubId);
201         mIntentFilter = new IntentFilter(
202                 TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
203 
204         setIfOnlyAvailableForAdmins(true);
205 
206         mSubscriptionInfo = getSubscriptionInfo(mSubId);
207         mTelephonyManager = activity.getSystemService(TelephonyManager.class);
208 
209         final CarrierConfigManager configManager = (CarrierConfigManager)
210                 getSystemService(Context.CARRIER_CONFIG_SERVICE);
211         final PersistableBundle b = configManager.getConfigForSubId(mSubId);
212         mHideImsApn = b.getBoolean(CarrierConfigManager.KEY_HIDE_IMS_APN_BOOL);
213         mAllowAddingApns = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL);
214         if (mAllowAddingApns) {
215             final String[] readOnlyApnTypes = b.getStringArray(
216                     CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
217             // if no apn type can be edited, do not allow adding APNs
218             if (ApnEditor.hasAllApns(readOnlyApnTypes)) {
219                 Log.d(TAG, "not allowing adding APN because all APN types are read only");
220                 mAllowAddingApns = false;
221             }
222         }
223         mHidePresetApnDetails = b.getBoolean(CarrierConfigManager.KEY_HIDE_PRESET_APN_DETAILS_BOOL);
224         mUserManager = UserManager.get(activity);
225     }
226 
227     @Override
onActivityCreated(Bundle savedInstanceState)228     public void onActivityCreated(Bundle savedInstanceState) {
229         super.onActivityCreated(savedInstanceState);
230 
231         getEmptyTextView().setText(R.string.apn_settings_not_available);
232         mUnavailable = isUiRestricted();
233         setHasOptionsMenu(!mUnavailable);
234         if (mUnavailable) {
235             addPreferencesFromResource(R.xml.placeholder_prefs);
236             return;
237         }
238 
239         addPreferencesFromResource(R.xml.apn_settings);
240     }
241 
242     @Override
onResume()243     public void onResume() {
244         super.onResume();
245 
246         if (mUnavailable) {
247             return;
248         }
249 
250         getActivity().registerReceiver(mReceiver, mIntentFilter);
251 
252         restartPhoneStateListener(mSubId);
253 
254         if (!mRestoreDefaultApnMode) {
255             fillList();
256         }
257     }
258 
259     @Override
onPause()260     public void onPause() {
261         super.onPause();
262 
263         if (mUnavailable) {
264             return;
265         }
266 
267         getActivity().unregisterReceiver(mReceiver);
268 
269         mTelephonyManager.listen(mPhoneStateListener,
270                 PhoneStateListener.LISTEN_NONE);
271     }
272 
273     @Override
onDestroy()274     public void onDestroy() {
275         super.onDestroy();
276 
277         if (mRestoreDefaultApnThread != null) {
278             mRestoreDefaultApnThread.quit();
279         }
280     }
281 
282     @Override
getRestrictionEnforcedAdmin()283     public EnforcedAdmin getRestrictionEnforcedAdmin() {
284         final UserHandle user = UserHandle.of(mUserManager.getUserHandle());
285         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, user)
286                 && !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
287                         user)) {
288             return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
289         }
290         return null;
291     }
292 
getSubscriptionInfo(int subId)293     private SubscriptionInfo getSubscriptionInfo(int subId) {
294         return SubscriptionManager.from(getActivity()).getActiveSubscriptionInfo(subId);
295     }
296 
fillList()297     private void fillList() {
298         final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
299                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
300         final Uri simApnUri = Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
301                 String.valueOf(subId));
302         final StringBuilder where =
303                 new StringBuilder("NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND "
304                 + "user_visible!=0");
305 
306         if (mHideImsApn) {
307             where.append(" AND NOT (type='ims')");
308         }
309 
310         final Cursor cursor = getContentResolver().query(simApnUri,
311                 CARRIERS_PROJECTION, where.toString(), null,
312                 Telephony.Carriers.DEFAULT_SORT_ORDER);
313 
314         if (cursor != null) {
315             final PreferenceGroup apnPrefList = (PreferenceGroup) findPreference("apn_list");
316             apnPrefList.removeAll();
317 
318             final ArrayList<ApnPreference> apnList = new ArrayList<ApnPreference>();
319             final ArrayList<ApnPreference> mmsApnList = new ArrayList<ApnPreference>();
320 
321             mSelectedKey = getSelectedApnKey();
322             cursor.moveToFirst();
323             while (!cursor.isAfterLast()) {
324                 final String name = cursor.getString(NAME_INDEX);
325                 final String apn = cursor.getString(APN_INDEX);
326                 final String key = cursor.getString(ID_INDEX);
327                 final String type = cursor.getString(TYPES_INDEX);
328                 final int edited = cursor.getInt(EDITED_INDEX);
329                 mMvnoType = cursor.getString(MVNO_TYPE_INDEX);
330                 mMvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX);
331 
332                 final ApnPreference pref = new ApnPreference(getPrefContext());
333 
334                 pref.setKey(key);
335                 pref.setTitle(name);
336                 pref.setPersistent(false);
337                 pref.setOnPreferenceChangeListener(this);
338                 pref.setSubId(subId);
339                 if (mHidePresetApnDetails && edited == Telephony.Carriers.UNEDITED) {
340                     pref.setHideDetails();
341                 } else {
342                     pref.setSummary(apn);
343                 }
344 
345                 final boolean selectable = ((type == null) || !type.equals("mms"));
346                 pref.setSelectable(selectable);
347                 if (selectable) {
348                     if ((mSelectedKey != null) && mSelectedKey.equals(key)) {
349                         pref.setChecked();
350                     }
351                     apnList.add(pref);
352                 } else {
353                     mmsApnList.add(pref);
354                 }
355                 cursor.moveToNext();
356             }
357             cursor.close();
358 
359             for (Preference preference : apnList) {
360                 apnPrefList.addPreference(preference);
361             }
362             for (Preference preference : mmsApnList) {
363                 apnPrefList.addPreference(preference);
364             }
365         }
366     }
367 
368     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)369     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
370         if (!mUnavailable) {
371             if (mAllowAddingApns) {
372                 menu.add(0, MENU_NEW, 0,
373                         getResources().getString(R.string.menu_new))
374                         .setIcon(R.drawable.ic_add_24dp)
375                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
376             }
377             menu.add(0, MENU_RESTORE, 0,
378                     getResources().getString(R.string.menu_restore))
379                     .setIcon(android.R.drawable.ic_menu_upload);
380         }
381 
382         super.onCreateOptionsMenu(menu, inflater);
383     }
384 
385     @Override
onOptionsItemSelected(MenuItem item)386     public boolean onOptionsItemSelected(MenuItem item) {
387         switch (item.getItemId()) {
388         case MENU_NEW:
389             addNewApn();
390             return true;
391 
392         case MENU_RESTORE:
393             restoreDefaultApn();
394             return true;
395         }
396         return super.onOptionsItemSelected(item);
397     }
398 
addNewApn()399     private void addNewApn() {
400         final Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI);
401         final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
402                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
403         intent.putExtra(SUB_ID, subId);
404         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
405         if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) {
406             intent.putExtra(MVNO_TYPE, mMvnoType);
407             intent.putExtra(MVNO_MATCH_DATA, mMvnoMatchData);
408         }
409         startActivity(intent);
410     }
411 
412     @Override
onPreferenceChange(Preference preference, Object newValue)413     public boolean onPreferenceChange(Preference preference, Object newValue) {
414         Log.d(TAG, "onPreferenceChange(): Preference - " + preference
415                 + ", newValue - " + newValue + ", newValue type - "
416                 + newValue.getClass());
417         if (newValue instanceof String) {
418             setSelectedApnKey((String) newValue);
419         }
420 
421         return true;
422     }
423 
setSelectedApnKey(String key)424     private void setSelectedApnKey(String key) {
425         mSelectedKey = key;
426         final ContentResolver resolver = getContentResolver();
427 
428         final ContentValues values = new ContentValues();
429         values.put(APN_ID, mSelectedKey);
430         resolver.update(getUriForCurrSubId(PREFERAPN_URI), values, null, null);
431     }
432 
getSelectedApnKey()433     private String getSelectedApnKey() {
434         String key = null;
435 
436         final Cursor cursor = getContentResolver().query(getUriForCurrSubId(PREFERAPN_URI),
437                 new String[] {"_id"}, null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
438         if (cursor.getCount() > 0) {
439             cursor.moveToFirst();
440             key = cursor.getString(ID_INDEX);
441         }
442         cursor.close();
443         return key;
444     }
445 
restoreDefaultApn()446     private boolean restoreDefaultApn() {
447         showDialog(DIALOG_RESTORE_DEFAULTAPN);
448         mRestoreDefaultApnMode = true;
449 
450         if (mRestoreApnUiHandler == null) {
451             mRestoreApnUiHandler = new RestoreApnUiHandler();
452         }
453 
454         if (mRestoreApnProcessHandler == null ||
455             mRestoreDefaultApnThread == null) {
456             mRestoreDefaultApnThread = new HandlerThread(
457                     "Restore default APN Handler: Process Thread");
458             mRestoreDefaultApnThread.start();
459             mRestoreApnProcessHandler = new RestoreApnProcessHandler(
460                     mRestoreDefaultApnThread.getLooper(), mRestoreApnUiHandler);
461         }
462 
463         mRestoreApnProcessHandler
464                 .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START);
465         return true;
466     }
467 
468     // Append subId to the Uri
getUriForCurrSubId(Uri uri)469     private Uri getUriForCurrSubId(Uri uri) {
470         final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
471                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
472         if (SubscriptionManager.isValidSubscriptionId(subId)) {
473             return Uri.withAppendedPath(uri, "subId/" + String.valueOf(subId));
474         } else {
475             return uri;
476         }
477     }
478 
479     private class RestoreApnUiHandler extends Handler {
480         @Override
handleMessage(Message msg)481         public void handleMessage(Message msg) {
482             switch (msg.what) {
483                 case EVENT_RESTORE_DEFAULTAPN_COMPLETE:
484                     final Activity activity = getActivity();
485                     if (activity == null) {
486                         mRestoreDefaultApnMode = false;
487                         return;
488                     }
489                     fillList();
490                     getPreferenceScreen().setEnabled(true);
491                     mRestoreDefaultApnMode = false;
492                     removeDialog(DIALOG_RESTORE_DEFAULTAPN);
493                     Toast.makeText(
494                         activity,
495                         getResources().getString(
496                                 R.string.restore_default_apn_completed),
497                         Toast.LENGTH_LONG).show();
498                     break;
499             }
500         }
501     }
502 
503     private class RestoreApnProcessHandler extends Handler {
504         private Handler mRestoreApnUiHandler;
505 
RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler)506         public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
507             super(looper);
508             this.mRestoreApnUiHandler = restoreApnUiHandler;
509         }
510 
511         @Override
handleMessage(Message msg)512         public void handleMessage(Message msg) {
513             switch (msg.what) {
514                 case EVENT_RESTORE_DEFAULTAPN_START:
515                     final ContentResolver resolver = getContentResolver();
516                     resolver.delete(getUriForCurrSubId(DEFAULTAPN_URI), null, null);
517                     mRestoreApnUiHandler
518                         .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_COMPLETE);
519                     break;
520             }
521         }
522     }
523 
524     @Override
onCreateDialog(int id)525     public Dialog onCreateDialog(int id) {
526         if (id == DIALOG_RESTORE_DEFAULTAPN) {
527             final ProgressDialog dialog = new ProgressDialog(getActivity()) {
528                 public boolean onTouchEvent(MotionEvent event) {
529                     return true;
530                 }
531             };
532             dialog.setMessage(getResources().getString(R.string.restore_default_apn));
533             dialog.setCancelable(false);
534             return dialog;
535         }
536         return null;
537     }
538 
539     @Override
getDialogMetricsCategory(int dialogId)540     public int getDialogMetricsCategory(int dialogId) {
541         if (dialogId == DIALOG_RESTORE_DEFAULTAPN) {
542             return SettingsEnums.DIALOG_APN_RESTORE_DEFAULT;
543         }
544         return 0;
545     }
546 }
547