/* * This file is part of APNdroid. * * APNdroid is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * APNdroid is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with APNdroid. If not, see <http://www.gnu.org/licenses/>. */ package cm.android.apn; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; /** * @author Martin Adamek <martin.adamek@gmail.com> * @author Pavlov Dmitry <pavlov.dmitry.n@gmail.com> */ public final class ApnDao { public static final int STATE_OFF = 0; public static final int STATE_ON = 1; private static final String SUFFIX = "apndroid"; private static final String ID = "_id"; private static final String APN = "apn"; private static final String TYPE = "type"; // from frameworks/base/core/java/android/provider/Telephony.java private static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers"); private static final String DB_LIKE_SUFFIX = "%" + SUFFIX; private static final String DB_LIKE_TYPE_SUFFIX = "%" + SUFFIX + "%"; private static final String[] DB_LIKE_MMS_TYPE_SUFFIX = new String[]{"%mms" + SUFFIX + "%"}; private final ContentResolver mContentResolver; private int mMmsTarget = STATE_ON; private boolean mDisableAll = false; public ApnDao(ContentResolver contentResolver) { mContentResolver = contentResolver; } public boolean isDataEnabled() { return countDisabledApns() <= 0; } public boolean isMmsEnabled() { return !(countMmsApns() > 0 && countDisabledMmsApns() > 0); } public boolean setDataEnabled(boolean enable) { return setDataEnabled(enable, enable); } public boolean setDataEnabled(boolean enableData, boolean enableMms) { setMmsEnabled(enableMms); if (enableData) { return enableAllInDb(); } else { return disableAllInDb(); } } public boolean setMmsEnabled(boolean enable) { if (enable) { return enableApnList(selectDisabledMmsApns()); } else { final List<ApnInfo> mmsList = selectEnabledMmsApns(); return mmsList.size() != 0 && disableApnList(mmsList); } } public void setMmsTarget(int mmsTarget) { this.mMmsTarget = mmsTarget; } public void setDisableAllApns(boolean disableAll) { this.mDisableAll = disableAll; } private List<ApnInfo> getEnabledApnsMap() { String query; boolean disableAll = this.mDisableAll; String disableAllQuery = disableAll ? null : "current is not null"; if (mMmsTarget == STATE_OFF) { query = disableAllQuery; } else { query = "(not lower(type)='mms' or type is null)"; if (!disableAll) { query += " and " + disableAllQuery; } } return selectApnInfo(query, null); } private List<ApnInfo> getDisabledApnsMap() { return selectApnInfo("apn like ? or type like ?", new String[]{DB_LIKE_SUFFIX, DB_LIKE_TYPE_SUFFIX}); } private List<ApnInfo> selectApnInfo(String whereQuery, String[] whereParams) { Cursor cursor = null; try { cursor = mContentResolver.query(CONTENT_URI, new String[]{ID, APN, TYPE}, whereQuery, whereParams, null); if (cursor == null) { return Collections.emptyList(); } return createApnList(cursor); } finally { if (cursor != null) { cursor.close(); } } } private boolean enableAllInDb() { List<ApnInfo> apns = getDisabledApnsMap(); return enableApnList(apns); } /** * Creates list of apn dtos from a DB cursor * * @param mCursor db cursor with select result set * @return list of APN dtos */ private List<ApnInfo> createApnList(Cursor mCursor) { List<ApnInfo> result = new ArrayList<ApnInfo>(); mCursor.moveToFirst(); while (!mCursor.isAfterLast()) { long id = mCursor.getLong(0); String apn = mCursor.getString(1); String type = mCursor.getString(2); result.add(new ApnInfo(id, apn, type)); mCursor.moveToNext(); } return result; } /** * Tries to disable apn's according to user preferences. * * @return {@code true} if one o more apns changed and {@code false} if all APNs did not changed * their states */ private boolean disableAllInDb() { List<ApnInfo> apns = getEnabledApnsMap(); // when selected apns is empty if (apns.isEmpty()) { return countDisabledApns() > 0; } return disableApnList(apns); } /** * Use this one if you have fresh list of APNs already and you can save one query to DB * * @param apns list of apns data to modify * @return {@code true} if switch was successfull and {@code false} otherwise */ private boolean enableApnList(List<ApnInfo> apns) { final ContentResolver contentResolver = this.mContentResolver; for (ApnInfo apnInfo : apns) { ContentValues values = new ContentValues(); String newApnName = removeSuffix(apnInfo.apn); values.put(APN, newApnName); String newApnType = removeComplexSuffix(apnInfo.type); if ("".equals(newApnType)) { values.putNull(TYPE); } else { values.put(TYPE, newApnType); } contentResolver.update(CONTENT_URI, values, ID + "=?", new String[]{String.valueOf(apnInfo.id)}); } return true;// we always return true because in any situation we can // reset all apns to initial state } private boolean disableApnList(List<ApnInfo> apns) { final ContentResolver contentResolver = this.mContentResolver; for (ApnInfo apnInfo : apns) { ContentValues values = new ContentValues(); String newApnName = addSuffix(apnInfo.apn); values.put(APN, newApnName); String newApnType = addComplexSuffix(apnInfo.type); values.put(TYPE, newApnType); contentResolver.update(CONTENT_URI, values, ID + "=?", new String[]{String.valueOf(apnInfo.id)}); } return true; } private int countDisabledApns() { return executeCountQuery("apn like ? or type like ?", new String[]{DB_LIKE_SUFFIX, DB_LIKE_TYPE_SUFFIX}); } private int countMmsApns() { return executeCountQuery("(type like ? or type like '%mms%')" + getCurrentCriteria(), DB_LIKE_MMS_TYPE_SUFFIX); } private int countDisabledMmsApns() { return executeCountQuery("type like ?", DB_LIKE_MMS_TYPE_SUFFIX); } private int executeCountQuery(String whereQuery, String[] whereParams) { Cursor cursor = null; try { cursor = mContentResolver.query(CONTENT_URI, new String[]{"count(*)"}, whereQuery, whereParams, null); if (cursor != null && cursor.moveToFirst()) { return cursor.getInt(0); } else { return -1; } } finally { if (cursor != null) { cursor.close(); } } } private List<ApnInfo> selectDisabledMmsApns() { return selectApnInfo("type like ?", DB_LIKE_MMS_TYPE_SUFFIX); } private List<ApnInfo> selectEnabledMmsApns() { return selectApnInfo("type like ? and type not like ?" + getCurrentCriteria(), new String[]{"%mms%", DB_LIKE_MMS_TYPE_SUFFIX[0]}); } private String getCurrentCriteria() { return mDisableAll ? "" : " and current is not null"; } public static String addSuffix(String currentName) { if (currentName == null) { return SUFFIX; } else { return currentName + SUFFIX; } } public String addComplexSuffix(String complexString) { if (complexString == null) { return SUFFIX; } StringBuilder builder = new StringBuilder(complexString.length()); StringTokenizer tokenizer = new StringTokenizer(complexString, ","); boolean leaveMmsEnabled = mMmsTarget == STATE_OFF; while (tokenizer.hasMoreTokens()) { String str = tokenizer.nextToken().trim(); if (leaveMmsEnabled && "mms".equals(str)) { builder.append(str); } else { builder.append(addSuffix(str)); } if (tokenizer.hasMoreTokens()) { builder.append(","); } } return builder.toString(); } public static String removeSuffix(String currentName) { if (currentName.endsWith(SUFFIX)) { return currentName.substring(0, currentName.length() - SUFFIX.length()); } else { return currentName; } } public static String removeComplexSuffix(String complexString) { if (complexString == null) { return ""; } StringBuilder builder = new StringBuilder(complexString.length()); StringTokenizer tokenizer = new StringTokenizer(complexString, ","); while (tokenizer.hasMoreTokens()) { builder.append(removeSuffix(tokenizer.nextToken().trim())); if (tokenizer.hasMoreTokens()) { builder.append(","); } } return builder.toString(); } /** * Selection of few interesting columns from APN table */ private static final class ApnInfo { final long id; final String apn; final String type; public ApnInfo(long id, String apn, String type) { this.id = id; this.apn = apn; this.type = type; } } }