/* * Copyright (C) 2010 beworx.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.bwx.bequick.handlers.apn; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.provider.BaseColumns; import android.util.Log; import com.bwx.bequick.Constants; /** * Modifies all current APN configurations according to user preferences. It adds * a prefix or a suffix to {@link #COLUMN_APN} and {@link #COLUMN_TYPE} values of * each APN configuration in order to disable them and removes any know prefixes or * suffixes to enable them back. * * @author sergej@beworx.com */ public class ApnControl { private static final String TAG = "bwx.ApnControl"; // this state is returned if we have only MMS but we are not allowed to disable them public static final int STATE_MMS_ONLY = -1; // this state is returned if we have no APNs configured public static final int STATE_NO_APNS = 0; // we have APNs to disable public static final int STATE_OFF = 1; // we have APNs to enable public static final int STATE_ON = 2; private static final String SUFFIX_QS_CLASSIC = "[disabled]"; // type 0 private static final String SUFFIX_APN = "apndroid"; // type 1 private static final String PREFIX_MINUS = "-"; // type 2 private static final String MMS = "mms"; private static final String COLUMN_ID = BaseColumns._ID; private static final String COLUMN_APN = "apn"; private static final String COLUMN_TYPE = "type"; private static final String COLUMN_APN_ID = "apn_id"; // for preferred APN private static final String[] PROJECTION = new String[] { COLUMN_ID, COLUMN_APN, COLUMN_TYPE }; private static final Uri CURRENT_APNS = Uri.parse("content://telephony/carriers/current"); private static final Uri PREFERRED_APN = Uri.parse("content://telephony/carriers/preferapn"); public static void setApnState(Context context, SharedPreferences prefs, boolean enabled) { boolean shouldDisableMms = shouldDisableMms(prefs); boolean restorePreferedApn = prefs.getBoolean(Constants.PREF_RESTORE_PREFERRED_APN, false); int modifierType = Integer.parseInt(prefs.getString(Constants.PREF_APN_MODIFIER, "2")); // "-" by default as most reliable // store APN for further restore always if (!enabled) storePreferredApn(context, prefs); ContentResolver resolver = context.getContentResolver(); ContentValues values = new ContentValues(); Cursor cursor = null; String[] args = new String[1]; try { // COLUMN_ID{0}, COLUMN_APN{1}, COLUMN_TYPE{2} cursor = resolver.query(CURRENT_APNS, PROJECTION, null, null, null); int idIndex = cursor.getColumnIndex(COLUMN_ID); int apnIndex = cursor.getColumnIndex(COLUMN_APN); int typeIndex = cursor.getColumnIndex(COLUMN_TYPE); cursor.moveToFirst(); while (!cursor.isAfterLast()) { String typeValue = cursor.getString(typeIndex); if (!enabled // we should disable && isMms(typeValue) // and this is MMS && !shouldDisableMms) { // but users disallow us to // disable MMS // ignore cursor.moveToNext(); continue; } args[0] = String.valueOf(cursor.getInt(idIndex)); // id values.put(COLUMN_APN, getAdaptedValue(cursor.getString(apnIndex), enabled, modifierType)); values.put(COLUMN_TYPE, getAdaptedValue(typeValue, enabled, modifierType)); resolver.update(CURRENT_APNS, values, COLUMN_ID + "=?", args); // move to next cursor.moveToNext(); } } catch (Exception e) { throw new RuntimeException(e); } finally { if (cursor != null) { cursor.close(); } } if (restorePreferedApn && enabled) { restorePreferredApn(context, prefs); } } private static void storePreferredApn(Context context, SharedPreferences prefs) { long id = getPreferedApnIdFromProvider(context); prefs.edit().putLong(Constants.PREF_PREFERRED_APN_ID, id).commit(); Log.d(TAG, "Stored prefered APN id=" + id); } private static void restorePreferredApn(Context context, SharedPreferences prefs) { long id = getPreferedApnIdFromProvider(context); if (id == -1L) { id = getPreferedApnIdFromPreferences(context, prefs); if (id == -1L) { id = getFirstCurrentApnId(context); } } if (id == -1L) { Log.d(TAG, "No prefered APN can be restored"); } else { ContentResolver resolver = context.getContentResolver(); // refresh preferred APN ContentValues values = new ContentValues(); values.putNull(COLUMN_APN_ID); resolver.update(PREFERRED_APN, values, null, null); values.put(COLUMN_APN_ID, id); resolver.update(PREFERRED_APN, values, null, null); Log.d(TAG, "Restored prefered APN id=" + id); } } private static long getPreferedApnIdFromPreferences(Context context, SharedPreferences prefs) { long id = prefs.getLong(Constants.PREF_PREFERRED_APN_ID, -1L); if (id == -1L) return id; // verify that there is still APN with such id ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(CURRENT_APNS, new String[] {COLUMN_ID}, COLUMN_ID + "=" + id, null, null); try { cursor.moveToFirst(); if (!cursor.isAfterLast()){ // yes! it is still there return id; } else { // no such APN anymore, return "not found" return -1; } } finally { if (cursor != null) cursor.close(); } } private static long getFirstCurrentApnId(Context context) { ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(CURRENT_APNS, new String[] {COLUMN_ID, COLUMN_TYPE}, null, null, null); try { cursor.moveToFirst(); String type; while (!cursor.isAfterLast()){ type = cursor.getString(1); if (!isMms(type)) { return cursor.getLong(0); // stop here, this is first not MMS } cursor.moveToNext(); } } finally { if (cursor != null) cursor.close(); } return -1; } private static long getPreferedApnIdFromProvider(Context context) { ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(PREFERRED_APN, new String[] {COLUMN_ID}, null, null, null); try { cursor.moveToFirst(); if (!cursor.isAfterLast()){ return cursor.getLong(0); } } finally { if (cursor != null) cursor.close(); } return -1L; } public static int getApnState(Context context, SharedPreferences prefs) { boolean shouldDisableMms = shouldDisableMms(prefs); ContentResolver resolver = context.getContentResolver(); boolean hasMMS = false; int counter = 0; Cursor cursor = null; try { cursor = resolver.query(CURRENT_APNS, PROJECTION, null, null, null); int typeIndex = cursor.getColumnIndex(COLUMN_TYPE); cursor.moveToNext(); while (!cursor.isAfterLast()) { String type = cursor.getString(typeIndex); if (isDisabled(type)) { return STATE_OFF; // no need to continue } if (!isMms(type) || shouldDisableMms) { counter++; } else { hasMMS = true; } cursor.moveToNext(); } } finally { if (cursor != null) { cursor.close(); } } return counter == 0 ? (hasMMS ? STATE_MMS_ONLY : STATE_NO_APNS) : STATE_ON; } private static boolean isMms(String type) { return type != null && type.toLowerCase().contains(MMS); } private static boolean isDisabled(String value) { return value != null && ( value.startsWith(PREFIX_MINUS) || value.endsWith(SUFFIX_APN) || value.endsWith(SUFFIX_QS_CLASSIC)); } public static boolean shouldDisableMms(SharedPreferences prefs) { return prefs.getBoolean(Constants.PREF_DISABLE_MMS, false); } private static String getAdaptedValue(String value, boolean enable, int modifierType) { final String modifier = getPreferedModifier(modifierType); // handle null-value if (value == null) return enable ? value : modifier; // remove any modifier so that value becomes enabled in any case value = removeModifiers(value); if (!enable) { // add required modifier value = addModifier(value, modifierType, modifier); } return value; } private static String getPreferedModifier(int modifierType) { if (modifierType == 2) { // the prefix return PREFIX_MINUS; } else { // a suffix return modifierType == 0 ? SUFFIX_APN : SUFFIX_QS_CLASSIC; } } private static String removeModifiers(String value) { if (value.startsWith(PREFIX_MINUS)) return value.substring(PREFIX_MINUS.length()); if (value.endsWith(SUFFIX_QS_CLASSIC)) return value.substring(0, value.length() - SUFFIX_QS_CLASSIC.length()); if (value.endsWith(SUFFIX_APN)) return value.substring(0, value.length() - SUFFIX_APN.length()); return value; } private static String addModifier(String value, int modifierType, String modifier) { if (modifierType == 2) { // the prefix return modifier + value; } else { // a suffix return value + modifier; } } }