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