• 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                 Context.RECEIVER_EXPORTED_UNAUDITED);
272 
273         restartPhoneStateListener(mSubId);
274 
275         if (!mRestoreDefaultApnMode) {
276             fillList();
277         }
278     }
279 
280     @Override
onPause()281     public void onPause() {
282         super.onPause();
283 
284         if (mUnavailable) {
285             return;
286         }
287 
288         getActivity().unregisterReceiver(mReceiver);
289 
290         mTelephonyManager.listen(mPhoneStateListener,
291                 PhoneStateListener.LISTEN_NONE);
292     }
293 
294     @Override
onDestroy()295     public void onDestroy() {
296         super.onDestroy();
297 
298         if (mRestoreDefaultApnThread != null) {
299             mRestoreDefaultApnThread.quit();
300         }
301     }
302 
303     @Override
getRestrictionEnforcedAdmin()304     public EnforcedAdmin getRestrictionEnforcedAdmin() {
305         final UserHandle user = UserHandle.of(mUserManager.getProcessUserId());
306         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, user)
307                 && !mUserManager.hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
308                         user)) {
309             return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
310         }
311         return null;
312     }
313 
getSubscriptionInfo(int subId)314     private SubscriptionInfo getSubscriptionInfo(int subId) {
315         return SubscriptionManager.from(getActivity()).getActiveSubscriptionInfo(subId);
316     }
317 
fillList()318     private void fillList() {
319         final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
320                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
321         final Uri simApnUri = Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
322                 String.valueOf(subId));
323         final StringBuilder where =
324                 new StringBuilder("NOT (type='ia' AND (apn=\"\" OR apn IS NULL)) AND "
325                 + "user_visible!=0");
326         // Remove Emergency type, users should not mess with that
327         where.append(" AND NOT (type='emergency')");
328 
329         if (mHideImsApn) {
330             where.append(" AND NOT (type='ims')");
331         }
332 
333         final Cursor cursor = getContentResolver().query(simApnUri,
334                 CARRIERS_PROJECTION, where.toString(), null,
335                 Telephony.Carriers.DEFAULT_SORT_ORDER);
336 
337         if (cursor != null) {
338             final PreferenceGroup apnPrefList = (PreferenceGroup) findPreference("apn_list");
339             apnPrefList.removeAll();
340 
341             final ArrayList<ApnPreference> apnList = new ArrayList<ApnPreference>();
342             final ArrayList<ApnPreference> mmsApnList = new ArrayList<ApnPreference>();
343 
344             mSelectedKey = getSelectedApnKey();
345             cursor.moveToFirst();
346             while (!cursor.isAfterLast()) {
347                 final String name = cursor.getString(NAME_INDEX);
348                 final String apn = cursor.getString(APN_INDEX);
349                 final String key = cursor.getString(ID_INDEX);
350                 final String type = cursor.getString(TYPES_INDEX);
351                 final int edited = cursor.getInt(EDITED_INDEX);
352                 mMvnoType = cursor.getString(MVNO_TYPE_INDEX);
353                 mMvnoMatchData = cursor.getString(MVNO_MATCH_DATA_INDEX);
354 
355                 final ApnPreference pref = new ApnPreference(getPrefContext());
356 
357                 pref.setKey(key);
358                 pref.setTitle(name);
359                 pref.setPersistent(false);
360                 pref.setOnPreferenceChangeListener(this);
361                 pref.setSubId(subId);
362                 if (mHidePresetApnDetails && edited == Telephony.Carriers.UNEDITED) {
363                     pref.setHideDetails();
364                 } else {
365                     pref.setSummary(apn);
366                 }
367 
368                 final boolean selectable =
369                         ((type == null) || type.contains(ApnSetting.TYPE_DEFAULT_STRING));
370                 pref.setSelectable(selectable);
371                 if (selectable) {
372                     if ((mSelectedKey != null) && mSelectedKey.equals(key)) {
373                         pref.setChecked();
374                     }
375                     apnList.add(pref);
376                 } else {
377                     mmsApnList.add(pref);
378                 }
379                 cursor.moveToNext();
380             }
381             cursor.close();
382 
383             for (Preference preference : apnList) {
384                 apnPrefList.addPreference(preference);
385             }
386             for (Preference preference : mmsApnList) {
387                 apnPrefList.addPreference(preference);
388             }
389         }
390     }
391 
392     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)393     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
394         if (!mUnavailable) {
395             if (mAllowAddingApns) {
396                 menu.add(0, MENU_NEW, 0,
397                         getResources().getString(R.string.menu_new))
398                         .setIcon(R.drawable.ic_add_24dp)
399                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
400             }
401             menu.add(0, MENU_RESTORE, 0,
402                     getResources().getString(R.string.menu_restore))
403                     .setIcon(android.R.drawable.ic_menu_upload);
404         }
405 
406         super.onCreateOptionsMenu(menu, inflater);
407     }
408 
409     @Override
onOptionsItemSelected(MenuItem item)410     public boolean onOptionsItemSelected(MenuItem item) {
411         switch (item.getItemId()) {
412             case MENU_NEW:
413                 addNewApn();
414                 return true;
415             case MENU_RESTORE:
416                 restoreDefaultApn();
417                 return true;
418         }
419         return super.onOptionsItemSelected(item);
420     }
421 
addNewApn()422     private void addNewApn() {
423         final Intent intent = new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI);
424         final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
425                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
426         intent.putExtra(SUB_ID, subId);
427         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
428         if (!TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData)) {
429             intent.putExtra(MVNO_TYPE, mMvnoType);
430             intent.putExtra(MVNO_MATCH_DATA, mMvnoMatchData);
431         }
432         startActivity(intent);
433     }
434 
435     @Override
onPreferenceChange(Preference preference, Object newValue)436     public boolean onPreferenceChange(Preference preference, Object newValue) {
437         Log.d(TAG, "onPreferenceChange(): Preference - " + preference
438                 + ", newValue - " + newValue + ", newValue type - "
439                 + newValue.getClass());
440         if (newValue instanceof String) {
441             setSelectedApnKey((String) newValue);
442         }
443 
444         return true;
445     }
446 
setSelectedApnKey(String key)447     private void setSelectedApnKey(String key) {
448         mSelectedKey = key;
449         final ContentResolver resolver = getContentResolver();
450 
451         final ContentValues values = new ContentValues();
452         values.put(APN_ID, mSelectedKey);
453         resolver.update(getUriForCurrSubId(PREFERAPN_URI), values, null, null);
454     }
455 
getSelectedApnKey()456     private String getSelectedApnKey() {
457         String key = null;
458 
459         final Cursor cursor = getContentResolver().query(getUriForCurrSubId(PREFERAPN_URI),
460                 new String[] {"_id"}, null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
461         if (cursor.getCount() > 0) {
462             cursor.moveToFirst();
463             key = cursor.getString(ID_INDEX);
464         }
465         cursor.close();
466         return key;
467     }
468 
restoreDefaultApn()469     private boolean restoreDefaultApn() {
470         // Callback of data connection change could be some noise during the stage of restore.
471         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
472 
473         showDialog(DIALOG_RESTORE_DEFAULTAPN);
474         mRestoreDefaultApnMode = true;
475 
476         if (mRestoreApnUiHandler == null) {
477             mRestoreApnUiHandler = new RestoreApnUiHandler();
478         }
479 
480         if (mRestoreApnProcessHandler == null || mRestoreDefaultApnThread == null) {
481             mRestoreDefaultApnThread = new HandlerThread(
482                     "Restore default APN Handler: Process Thread");
483             mRestoreDefaultApnThread.start();
484             mRestoreApnProcessHandler = new RestoreApnProcessHandler(
485                     mRestoreDefaultApnThread.getLooper(), mRestoreApnUiHandler);
486         }
487 
488         mRestoreApnProcessHandler
489                 .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START);
490         return true;
491     }
492 
493     // Append subId to the Uri
getUriForCurrSubId(Uri uri)494     private Uri getUriForCurrSubId(Uri uri) {
495         final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId()
496                 : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
497         if (SubscriptionManager.isValidSubscriptionId(subId)) {
498             return Uri.withAppendedPath(uri, "subId/" + String.valueOf(subId));
499         } else {
500             return uri;
501         }
502     }
503 
504     private class RestoreApnUiHandler extends Handler {
505         @Override
handleMessage(Message msg)506         public void handleMessage(Message msg) {
507             switch (msg.what) {
508                 case EVENT_RESTORE_DEFAULTAPN_COMPLETE:
509                     final Activity activity = getActivity();
510                     if (activity == null) {
511                         mRestoreDefaultApnMode = false;
512                         return;
513                     }
514                     fillList();
515                     getPreferenceScreen().setEnabled(true);
516                     mRestoreDefaultApnMode = false;
517                     removeDialog(DIALOG_RESTORE_DEFAULTAPN);
518                     Toast.makeText(
519                         activity,
520                         getResources().getString(
521                                 R.string.restore_default_apn_completed),
522                         Toast.LENGTH_LONG).show();
523                     restartPhoneStateListener(mSubId);
524                     break;
525             }
526         }
527     }
528 
529     private class RestoreApnProcessHandler extends Handler {
530         private Handler mRestoreApnUiHandler;
531 
RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler)532         RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
533             super(looper);
534             this.mRestoreApnUiHandler = restoreApnUiHandler;
535         }
536 
537         @Override
handleMessage(Message msg)538         public void handleMessage(Message msg) {
539             switch (msg.what) {
540                 case EVENT_RESTORE_DEFAULTAPN_START:
541                     final ContentResolver resolver = getContentResolver();
542                     resolver.delete(getUriForCurrSubId(DEFAULTAPN_URI), null, null);
543                     mRestoreApnUiHandler
544                         .sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_COMPLETE);
545                     break;
546             }
547         }
548     }
549 
550     @Override
onCreateDialog(int id)551     public Dialog onCreateDialog(int id) {
552         if (id == DIALOG_RESTORE_DEFAULTAPN) {
553             final ProgressDialog dialog = new ProgressDialog(getActivity()) {
554                 public boolean onTouchEvent(MotionEvent event) {
555                     return true;
556                 }
557             };
558             dialog.setMessage(getResources().getString(R.string.restore_default_apn));
559             dialog.setCancelable(false);
560             return dialog;
561         }
562         return null;
563     }
564 
565     @Override
getDialogMetricsCategory(int dialogId)566     public int getDialogMetricsCategory(int dialogId) {
567         if (dialogId == DIALOG_RESTORE_DEFAULTAPN) {
568             return SettingsEnums.DIALOG_APN_RESTORE_DEFAULT;
569         }
570         return 0;
571     }
572 }
573