Changes to support read-only apn types.

User will not be allowed to add/modify APNs for the read-only types.

Bug: 31074581
Change-Id: I954bd68249a93ce9deb0146725c1432ea6cb0f38
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java
index 8a963d8..333a195 100644
--- a/src/com/android/settings/ApnEditor.java
+++ b/src/com/android/settings/ApnEditor.java
@@ -24,11 +24,13 @@
 import android.app.DialogFragment;
 import android.content.ContentUris;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.provider.Telephony;
 import android.support.v14.preference.MultiSelectListPreference;
 import android.support.v14.preference.SwitchPreference;
@@ -36,6 +38,7 @@
 import android.support.v7.preference.ListPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
+import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -49,8 +52,12 @@
 import android.view.View.OnKeyListener;
 
 import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.util.ArrayUtils;
 
+import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 public class ApnEditor extends SettingsPreferenceFragment
@@ -105,6 +112,8 @@
     private int mBearerInitialVal = 0;
     private String mMvnoTypeStr;
     private String mMvnoMatchDataStr;
+    private String[] mReadOnlyApnTypes;
+    private boolean mReadOnlyApn;
 
     /**
      * Standard projection for the interesting columns of a normal note.
@@ -132,7 +141,8 @@
             Telephony.Carriers.BEARER_BITMASK, // 19
             Telephony.Carriers.ROAMING_PROTOCOL, // 20
             Telephony.Carriers.MVNO_TYPE,   // 21
-            Telephony.Carriers.MVNO_MATCH_DATA  // 22
+            Telephony.Carriers.MVNO_MATCH_DATA,  // 22
+            Telephony.Carriers.EDITED   // 23
     };
 
     private static final int ID_INDEX = 0;
@@ -157,6 +167,7 @@
     private static final int ROAMING_PROTOCOL_INDEX = 20;
     private static final int MVNO_TYPE_INDEX = 21;
     private static final int MVNO_MATCH_DATA_INDEX = 22;
+    private static final int EDITED_INDEX = 23;
 
 
     @Override
@@ -206,6 +217,8 @@
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
 
         mFirstTime = icicle == null;
+        mReadOnlyApn = false;
+        mReadOnlyApnTypes = null;
 
         if (action.equals(Intent.ACTION_EDIT)) {
             Uri uri = intent.getData();
@@ -214,6 +227,15 @@
                 finish();
                 return;
             }
+            CarrierConfigManager configManager = (CarrierConfigManager)
+                    getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (configManager != null) {
+                PersistableBundle b = configManager.getConfig();
+                if (b != null) {
+                    mReadOnlyApnTypes = b.getStringArray(
+                            CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY);
+                }
+            }
             mUri = uri;
         } else if (action.equals(Intent.ACTION_INSERT)) {
             if (mFirstTime || icicle.getInt(SAVED_POS) == 0) {
@@ -255,6 +277,17 @@
 
         mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
 
+        Log.d(TAG, "onCreate: EDITED " + mCursor.getInt(EDITED_INDEX));
+        // if it's not a USER_EDITED apn, check if it's read-only
+        if (mCursor.getInt(EDITED_INDEX) != Telephony.Carriers.USER_EDITED &&
+                apnTypesMatch(mReadOnlyApnTypes, mCursor.getString(TYPE_INDEX))) {
+            Log.d(TAG, "onCreate: apnTypesMatch; read-only APN");
+            mReadOnlyApn = true;
+            disableAllFields();
+        } /* else (if specific fields are read-only) {
+            disable specific fields
+        } */
+
         for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
             getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this);
         }
@@ -262,6 +295,87 @@
         fillUi();
     }
 
+    /**
+     * Check if passed in array of APN types indicates all APN types
+     * @param apnTypes array of APN types. "*" indicates all types.
+     * @return true if all apn types are included in the array, false otherwise
+     */
+    private boolean hasAllApns(String[] apnTypes) {
+        if (ArrayUtils.isEmpty(apnTypes)) {
+            return false;
+        }
+
+        List apnList = Arrays.asList(apnTypes);
+        if (apnList.contains(PhoneConstants.APN_TYPE_ALL)) {
+            Log.d(TAG, "hasAllApns: true because apnList.contains(PhoneConstants.APN_TYPE_ALL)");
+            return true;
+        }
+        for (String apn : PhoneConstants.APN_TYPES) {
+            if (!apnList.contains(apn)) {
+                return false;
+            }
+        }
+
+        Log.d(TAG, "hasAllApns: true");
+        return true;
+    }
+
+    /**
+     * Check if APN types overlap.
+     * @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all
+     *                       types
+     * @param apnTypes2 comma separated string of APN types. Empty string represents all types.
+     * @return if any apn type matches return true, otherwise return false
+     */
+    private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) {
+        if (ArrayUtils.isEmpty(apnTypesArray1)) {
+            return false;
+        }
+
+        if (hasAllApns(apnTypesArray1) || TextUtils.isEmpty(apnTypes2)) {
+            return true;
+        }
+
+        List apnTypesList1 = Arrays.asList(apnTypesArray1);
+        String[] apnTypesArray2 = apnTypes2.split(",");
+
+        for (String apn : apnTypesArray2) {
+            if (apnTypesList1.contains(apn.trim())) {
+                Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim());
+                return true;
+            }
+        }
+
+        Log.d(TAG, "apnTypesMatch: false");
+        return false;
+    }
+
+    /**
+     * Disables all fields so that user cannot modify the APN
+     */
+    private void disableAllFields() {
+        mName.setEnabled(false);
+        mApn.setEnabled(false);
+        mProxy.setEnabled(false);
+        mPort.setEnabled(false);
+        mUser.setEnabled(false);
+        mServer.setEnabled(false);
+        mPassword.setEnabled(false);
+        mMmsProxy.setEnabled(false);
+        mMmsPort.setEnabled(false);
+        mMmsc.setEnabled(false);
+        mMcc.setEnabled(false);
+        mMnc.setEnabled(false);
+        mApnType.setEnabled(false);
+        mAuthType.setEnabled(false);
+        mProtocol.setEnabled(false);
+        mRoamingProtocol.setEnabled(false);
+        mCarrierEnabled.setEnabled(false);
+        mBearerMulti.setEnabled(false);
+        mMvnoType.setEnabled(false);
+        mMvnoMatchData.setEnabled(false);
+    }
+
     @Override
     protected int getMetricsCategory() {
         return MetricsEvent.APN_EDITOR;
@@ -537,7 +651,7 @@
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
         // If it's a new APN, then cancel will delete the new entry in onPause
-        if (!mNewApn) {
+        if (!mNewApn && !mReadOnlyApn) {
             menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
                 .setIcon(R.drawable.ic_menu_delete);
         }