Messaging: Get rid of everything around the local apn database

Change-Id: Ie1916e93b274009494e76d6d99f36a025139127d
This commit is contained in:
Michael W
2025-04-12 14:59:26 +02:00
parent aad904a01b
commit 6161d0fca1
6 changed files with 5 additions and 4000 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project * Copyright (C) 2024-2025 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,8 +18,6 @@
package android.support.v7.mms; package android.support.v7.mms;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.net.Uri; import android.net.Uri;
@@ -28,8 +26,6 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.messaging.R;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -112,95 +108,6 @@ class DefaultApnSettingsLoader implements ApnSettingsLoader {
public void setSuccess() { public void setSuccess() {
// Do nothing // Do nothing
} }
public boolean equals(final BaseApn other) {
return TextUtils.equals(mMmsc, other.getMmsc()) &&
TextUtils.equals(mMmsProxy, other.getMmsProxy()) &&
mMmsProxyPort == other.getMmsProxyPort();
}
}
/**
* An in-memory implementation of an APN. These APNs are organized into an in-memory list.
* The order of the list can be changed by the setSuccess method.
*/
private static class MemoryApn implements Apn {
/**
* Create an in-memory APN loaded from resources
*
* @param apns the in-memory APN list
* @param typesIn the APN type field
* @param mmscIn the APN mmsc field
* @param proxyIn the APN mmsproxy field
* @param portIn the APN mmsport field
* @return an in-memory APN instance, null if there is invalid parameter
*/
public static MemoryApn from(final List<Apn> apns, final String typesIn,
final String mmscIn, final String proxyIn, final String portIn) {
if (apns == null) {
return null;
}
final BaseApn base = BaseApn.from(typesIn, mmscIn, proxyIn, portIn);
if (base == null) {
return null;
}
for (final Apn apn : apns) {
if (apn instanceof MemoryApn && ((MemoryApn) apn).equals(base)) {
return null;
}
}
return new MemoryApn(apns, base);
}
private final List<Apn> mApns;
private final BaseApn mBase;
public MemoryApn(final List<Apn> apns, final BaseApn base) {
mApns = apns;
mBase = base;
}
@Override
public String getMmsc() {
return mBase.getMmsc();
}
@Override
public String getMmsProxy() {
return mBase.getMmsProxy();
}
@Override
public int getMmsProxyPort() {
return mBase.getMmsProxyPort();
}
@Override
public void setSuccess() {
// If this is being marked as a successful APN, move it to the top of the list so
// next time it will be tried first
boolean moved = false;
synchronized (mApns) {
if (mApns.get(0) != this) {
mApns.remove(this);
mApns.add(0, this);
moved = true;
}
}
if (moved) {
Log.d(MmsService.TAG, "Set APN ["
+ "MMSC=" + getMmsc() + ", "
+ "PROXY=" + getMmsProxy() + ", "
+ "PORT=" + getMmsProxyPort() + "] to be first");
}
}
public boolean equals(final BaseApn other) {
if (other == null) {
return false;
}
return mBase.equals(other);
}
} }
/** /**
@@ -222,14 +129,6 @@ class DefaultApnSettingsLoader implements ApnSettingsLoader {
private static final int COLUMN_MMSPROXY = 2; private static final int COLUMN_MMSPROXY = 2;
private static final int COLUMN_MMSPORT = 3; private static final int COLUMN_MMSPORT = 3;
private static final String APN_MCC = "mcc";
private static final String APN_MNC = "mnc";
private static final String APN_APN = "apn";
private static final String APN_TYPE = "type";
private static final String APN_MMSC = "mmsc";
private static final String APN_MMSPROXY = "mmsproxy";
private static final String APN_MMSPORT = "mmsport";
private final Context mContext; private final Context mContext;
// Cached APNs for subIds // Cached APNs for subIds
@@ -263,16 +162,6 @@ class DefaultApnSettingsLoader implements ApnSettingsLoader {
private void loadLocked(final int subId, final String apnName, final List<Apn> apns) { private void loadLocked(final int subId, final String apnName, final List<Apn> apns) {
// Try system APN table first // Try system APN table first
loadFromSystem(subId, apnName, apns); loadFromSystem(subId, apnName, apns);
if (apns.size() > 0) {
return;
}
// Try loading from apns.xml in resources
loadFromResources(subId, apnName, apns);
if (apns.size() > 0) {
return;
}
// Try resources but without APN name
loadFromResources(subId, null/*apnName*/, apns);
} }
/** /**
@@ -384,48 +273,6 @@ class DefaultApnSettingsLoader implements ApnSettingsLoader {
return null; return null;
} }
/**
* Find matching APNs using builtin APN list resource
*
* @param subId the SIM subId
* @param apnName the APN name to match
* @param apns the list for returning results
*/
private void loadFromResources(final int subId, final String apnName, final List<Apn> apns) {
Log.i(MmsService.TAG, "Loading APNs from resources, apnName=" + apnName);
final int[] mccMnc = Utils.getMccMnc(mContext, subId);
if (mccMnc[0] == 0) {
Log.w(MmsService.TAG, "Can not get valid mcc/mnc from system");
return;
}
// MCC/MNC is good, loading/querying APNs from XML
try (XmlResourceParser xml = mContext.getResources().getXml(R.xml.apns)) {
new ApnsXmlParser(xml, apnValues -> {
final String mcc = trimWithNullCheck(apnValues.getAsString(APN_MCC));
final String mnc = trimWithNullCheck(apnValues.getAsString(APN_MNC));
final String apn = trimWithNullCheck(apnValues.getAsString(APN_APN));
try {
if (mccMnc[0] == Integer.parseInt(mcc) &&
mccMnc[1] == Integer.parseInt(mnc) &&
(TextUtils.isEmpty(apnName) || apnName.equalsIgnoreCase(apn))) {
final String type = apnValues.getAsString(APN_TYPE);
final String mmsc = apnValues.getAsString(APN_MMSC);
final String mmsproxy = apnValues.getAsString(APN_MMSPROXY);
final String mmsport = apnValues.getAsString(APN_MMSPORT);
final Apn newApn = MemoryApn.from(apns, type, mmsc, mmsproxy, mmsport);
if (newApn != null) {
apns.add(newApn);
}
}
} catch (final NumberFormatException e) {
// Ignore
}
}).parse();
} catch (final Resources.NotFoundException e) {
Log.w(MmsService.TAG, "Can not get apns.xml " + e);
}
}
private static String trimWithNullCheck(final String value) { private static String trimWithNullCheck(final String value) {
return value != null ? value.trim() : null; return value != null ? value.trim() : null;
} }

View File

@@ -32,7 +32,6 @@ import androidx.annotation.NonNull;
import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DataModel;
import com.android.messaging.receiver.SmsReceiver; import com.android.messaging.receiver.SmsReceiver;
import com.android.messaging.sms.ApnDatabase;
import com.android.messaging.sms.BugleApnSettingsLoader; import com.android.messaging.sms.BugleApnSettingsLoader;
import com.android.messaging.sms.BugleUserAgentInfoLoader; import com.android.messaging.sms.BugleUserAgentInfoLoader;
import com.android.messaging.sms.MmsConfig; import com.android.messaging.sms.MmsConfig;
@@ -85,8 +84,6 @@ public class BugleApplication extends Application implements UncaughtExceptionHa
// Initialize MMS lib // Initialize MMS lib
initMmsLib(context, carrierConfigValuesLoader); initMmsLib(context, carrierConfigValuesLoader);
// Initialize APN database
ApnDatabase.initializeAppContext(context);
// Fixup messages in flight if we crashed and send any pending // Fixup messages in flight if we crashed and send any pending
dataModel.onApplicationCreated(); dataModel.onApplicationCreated();
// Register carrier config change receiver // Register carrier config change receiver

View File

@@ -1,190 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024-2025 The LineageOS Project
*
* 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.android.messaging.sms;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.Telephony;
import android.util.Log;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.util.LogUtil;
import java.io.File;
/*
* Database helper class for looking up APNs. This database has a single table
* which stores the APNs that are initially created from an xml file.
*/
public class ApnDatabase extends SQLiteOpenHelper {
private static final int DB_VERSION = 3; // added sub_id columns
private static final String TAG = LogUtil.BUGLE_TAG;
private static final boolean DEBUG = false;
private static Context sContext;
private static ApnDatabase sApnDatabase;
private static final String APN_DATABASE_NAME = "apn.db";
/** table for carrier APN's */
public static final String APN_TABLE = "apn";
// APN table
private static final String APN_TABLE_SQL =
"CREATE TABLE " + APN_TABLE +
"(_id INTEGER PRIMARY KEY," +
Telephony.Carriers.NAME + " TEXT," +
Telephony.Carriers.NUMERIC + " TEXT," +
Telephony.Carriers.MCC + " TEXT," +
Telephony.Carriers.MNC + " TEXT," +
Telephony.Carriers.APN + " TEXT," +
Telephony.Carriers.USER + " TEXT," +
Telephony.Carriers.SERVER + " TEXT," +
Telephony.Carriers.PASSWORD + " TEXT," +
Telephony.Carriers.PROXY + " TEXT," +
Telephony.Carriers.PORT + " TEXT," +
Telephony.Carriers.MMSPROXY + " TEXT," +
Telephony.Carriers.MMSPORT + " TEXT," +
Telephony.Carriers.MMSC + " TEXT," +
Telephony.Carriers.AUTH_TYPE + " INTEGER," +
Telephony.Carriers.TYPE + " TEXT," +
Telephony.Carriers.CURRENT + " INTEGER," +
Telephony.Carriers.PROTOCOL + " TEXT," +
Telephony.Carriers.ROAMING_PROTOCOL + " TEXT," +
Telephony.Carriers.CARRIER_ENABLED + " BOOLEAN," +
Telephony.Carriers.BEARER + " INTEGER," +
Telephony.Carriers.MVNO_TYPE + " TEXT," +
Telephony.Carriers.MVNO_MATCH_DATA + " TEXT," +
Telephony.Carriers.SUBSCRIPTION_ID + " INTEGER DEFAULT " +
ParticipantData.DEFAULT_SELF_SUB_ID + ");";
public static final int COLUMN_ID = 4;
/**
* ApnDatabase is initialized asynchronously from the application.onCreate
* To ensure that it works in a testing environment it needs to never access the factory context
*/
public static void initializeAppContext(final Context context) {
sContext = context;
}
private ApnDatabase() {
super(sContext, APN_DATABASE_NAME, null, DB_VERSION);
if (DEBUG) {
LogUtil.d(TAG, "ApnDatabase constructor");
}
}
public static ApnDatabase getApnDatabase() {
if (sApnDatabase == null) {
sApnDatabase = new ApnDatabase();
}
return sApnDatabase;
}
public static boolean doesDatabaseExist() {
final File dbFile = sContext.getDatabasePath(APN_DATABASE_NAME);
return dbFile.exists();
}
@Override
public void onCreate(final SQLiteDatabase db) {
if (DEBUG) {
LogUtil.d(TAG, "ApnDatabase onCreate");
}
// Build the table using defaults (apn info bundled with the app)
rebuildTables(db);
}
@Override
public void onOpen(final SQLiteDatabase db) {
super.onOpen(db);
if (DEBUG) {
LogUtil.d(TAG, "ApnDatabase onOpen");
}
}
@Override
public void close() {
super.close();
if (DEBUG) {
LogUtil.d(TAG, "ApnDatabase close");
}
}
private void rebuildTables(final SQLiteDatabase db) {
if (DEBUG) {
LogUtil.d(TAG, "ApnDatabase rebuildTables");
}
db.execSQL("DROP TABLE IF EXISTS " + APN_TABLE + ";");
db.execSQL(APN_TABLE_SQL);
loadApnTable(db);
}
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
if (DEBUG) {
LogUtil.d(TAG, "ApnDatabase onUpgrade");
}
rebuildTables(db);
}
@Override
public void onDowngrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
if (DEBUG) {
LogUtil.d(TAG, "ApnDatabase onDowngrade");
}
rebuildTables(db);
}
/**
* Load APN table from app resources
*/
private static void loadApnTable(final SQLiteDatabase db) {
LogUtil.v(TAG, "ApnDatabase loadApnTable");
final Resources r = sContext.getResources();
final XmlResourceParser parser = r.getXml(R.xml.apns);
final ApnsXmlProcessor processor = ApnsXmlProcessor.get(parser);
processor.setApnHandler(apnValues -> db.insert(APN_TABLE, null/*nullColumnHack*/,
apnValues));
try {
processor.process();
} catch (final Exception e) {
Log.e(TAG, "Got exception while loading APN database.", e);
} finally {
parser.close();
}
}
public static void forceBuildAndLoadApnTables() {
final SQLiteDatabase db = getApnDatabase().getWritableDatabase();
db.execSQL("DROP TABLE IF EXISTS " + APN_TABLE);
// Table(s) always need for JB MR1 for APN support for MMS because JB MR1 throws
// a SecurityException when trying to access the carriers table (which holds the
// APNs). Some JB MR2 devices also throw the security exception, so we're building
// the table for JB MR2, too.
db.execSQL(APN_TABLE_SQL);
loadApnTable(db);
}
}

View File

@@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project * Copyright (C) 2024-2025 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,18 +18,15 @@
package com.android.messaging.sms; package com.android.messaging.sms;
import android.content.ContentValues; import android.content.ContentValues;
import android.provider.Telephony;
import com.android.messaging.util.Assert; import com.android.messaging.util.Assert;
import com.android.messaging.util.LogUtil; import com.android.messaging.util.LogUtil;
import com.android.messaging.util.PhoneUtils; import com.android.messaging.util.PhoneUtils;
import com.google.common.collect.Maps;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/* /*
* XML processor for the following files: * XML processor for the following files:
@@ -37,46 +34,14 @@ import java.util.Map;
* 2. res/xml/mms_config.xml (or related overlay files) * 2. res/xml/mms_config.xml (or related overlay files)
*/ */
class ApnsXmlProcessor { class ApnsXmlProcessor {
public interface ApnHandler {
void process(ContentValues apnValues);
}
public interface MmsConfigHandler { public interface MmsConfigHandler {
void process(String mccMnc, String key, String value, String type); void process(String mccMnc, String key, String value, String type);
} }
private static final String TAG = LogUtil.BUGLE_TAG; private static final String TAG = LogUtil.BUGLE_TAG;
private static final Map<String, String> APN_ATTRIBUTE_MAP = Maps.newHashMap();
static {
APN_ATTRIBUTE_MAP.put("mcc", Telephony.Carriers.MCC);
APN_ATTRIBUTE_MAP.put("mnc", Telephony.Carriers.MNC);
APN_ATTRIBUTE_MAP.put("carrier", Telephony.Carriers.NAME);
APN_ATTRIBUTE_MAP.put("apn", Telephony.Carriers.APN);
APN_ATTRIBUTE_MAP.put("mmsc", Telephony.Carriers.MMSC);
APN_ATTRIBUTE_MAP.put("mmsproxy", Telephony.Carriers.MMSPROXY);
APN_ATTRIBUTE_MAP.put("mmsport", Telephony.Carriers.MMSPORT);
APN_ATTRIBUTE_MAP.put("type", Telephony.Carriers.TYPE);
APN_ATTRIBUTE_MAP.put("user", Telephony.Carriers.USER);
APN_ATTRIBUTE_MAP.put("password", Telephony.Carriers.PASSWORD);
APN_ATTRIBUTE_MAP.put("authtype", Telephony.Carriers.AUTH_TYPE);
APN_ATTRIBUTE_MAP.put("mvno_match_data", Telephony.Carriers.MVNO_MATCH_DATA);
APN_ATTRIBUTE_MAP.put("mvno_type", Telephony.Carriers.MVNO_TYPE);
APN_ATTRIBUTE_MAP.put("protocol", Telephony.Carriers.PROTOCOL);
APN_ATTRIBUTE_MAP.put("bearer", Telephony.Carriers.BEARER);
APN_ATTRIBUTE_MAP.put("server", Telephony.Carriers.SERVER);
APN_ATTRIBUTE_MAP.put("roaming_protocol", Telephony.Carriers.ROAMING_PROTOCOL);
APN_ATTRIBUTE_MAP.put("proxy", Telephony.Carriers.PROXY);
APN_ATTRIBUTE_MAP.put("port", Telephony.Carriers.PORT);
APN_ATTRIBUTE_MAP.put("carrier_enabled", Telephony.Carriers.CARRIER_ENABLED);
}
private static final String TAG_APNS = "apns";
private static final String TAG_APN = "apn";
private static final String TAG_MMS_CONFIG = "mms_config"; private static final String TAG_MMS_CONFIG = "mms_config";
// Handler to process one apn
private ApnHandler mApnHandler;
// Handler to process one mms_config key/value pair // Handler to process one mms_config key/value pair
private MmsConfigHandler mMmsConfigHandler; private MmsConfigHandler mMmsConfigHandler;
@@ -86,7 +51,6 @@ class ApnsXmlProcessor {
private ApnsXmlProcessor(XmlPullParser parser) { private ApnsXmlProcessor(XmlPullParser parser) {
mInputParser = parser; mInputParser = parser;
mApnHandler = null;
mMmsConfigHandler = null; mMmsConfigHandler = null;
} }
@@ -95,11 +59,6 @@ class ApnsXmlProcessor {
return new ApnsXmlProcessor(parser); return new ApnsXmlProcessor(parser);
} }
public ApnsXmlProcessor setApnHandler(ApnHandler handler) {
mApnHandler = handler;
return this;
}
public ApnsXmlProcessor setMmsConfigHandler(MmsConfigHandler handler) { public ApnsXmlProcessor setMmsConfigHandler(MmsConfigHandler handler) {
mMmsConfigHandler = handler; mMmsConfigHandler = handler;
return this; return this;
@@ -131,22 +90,8 @@ class ApnsXmlProcessor {
// an apn element // an apn element
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
String tagName = mInputParser.getName(); String tagName = mInputParser.getName();
// Top level tag can be "apns" (apns.xml) // Top level tag can be "mms_config" (mms_config.xml)
// or "mms_config" (mms_config.xml) if (TAG_MMS_CONFIG.equals(tagName)) {
if (TAG_APNS.equals(tagName)) {
// For "apns", there could be "apn" or both "apn" and "mms_config"
for (;;) {
if (advanceToNextEvent(XmlPullParser.START_TAG) != XmlPullParser.START_TAG) {
break;
}
tagName = mInputParser.getName();
if (TAG_APN.equals(tagName)) {
processApn(values);
} else if (TAG_MMS_CONFIG.equals(tagName)) {
processMmsConfig();
}
}
} else if (TAG_MMS_CONFIG.equals(tagName)) {
// mms_config.xml resource // mms_config.xml resource
processMmsConfig(); processMmsConfig();
} }
@@ -157,28 +102,6 @@ class ApnsXmlProcessor {
} }
} }
private Integer parseInt(String text, Integer defaultValue, String logHint) {
Integer value = defaultValue;
try {
value = Integer.parseInt(text);
} catch (Exception e) {
LogUtil.e(TAG,
"Invalid value " + text + "for" + logHint + " @" + xmlParserDebugContext());
}
return value;
}
private Boolean parseBoolean(String text, Boolean defaultValue, String logHint) {
Boolean value = defaultValue;
try {
value = Boolean.parseBoolean(text);
} catch (Exception e) {
LogUtil.e(TAG,
"Invalid value " + text + "for" + logHint + " @" + xmlParserDebugContext());
}
return value;
}
private static String xmlParserEventString(int event) { private static String xmlParserEventString(int event) {
switch (event) { switch (event) {
case XmlPullParser.START_DOCUMENT: return "START_DOCUMENT"; case XmlPullParser.START_DOCUMENT: return "START_DOCUMENT";
@@ -219,51 +142,6 @@ class ApnsXmlProcessor {
return "Unknown"; return "Unknown";
} }
/**
* Process one apn
*
* @param apnValues Where we store the parsed apn
*/
private void processApn(ContentValues apnValues) throws IOException, XmlPullParserException {
Assert.notNull(apnValues);
apnValues.clear();
// Collect all the attributes
for (int i = 0; i < mInputParser.getAttributeCount(); i++) {
final String key = APN_ATTRIBUTE_MAP.get(mInputParser.getAttributeName(i));
if (key != null) {
apnValues.put(key, mInputParser.getAttributeValue(i));
}
}
// Set numeric to be canonicalized mcc/mnc like "310120", always 6 digits
final String canonicalMccMnc = PhoneUtils.canonicalizeMccMnc(
apnValues.getAsString(Telephony.Carriers.MCC),
apnValues.getAsString(Telephony.Carriers.MNC));
apnValues.put(Telephony.Carriers.NUMERIC, canonicalMccMnc);
// Some of the values should not be string type, converting them to desired types
final String authType = apnValues.getAsString(Telephony.Carriers.AUTH_TYPE);
if (authType != null) {
apnValues.put(Telephony.Carriers.AUTH_TYPE, parseInt(authType, -1, "apn authtype"));
}
final String carrierEnabled = apnValues.getAsString(Telephony.Carriers.CARRIER_ENABLED);
if (carrierEnabled != null) {
apnValues.put(Telephony.Carriers.CARRIER_ENABLED,
parseBoolean(carrierEnabled, null, "apn carrierEnabled"));
}
final String bearer = apnValues.getAsString(Telephony.Carriers.BEARER);
if (bearer != null) {
apnValues.put(Telephony.Carriers.BEARER, parseInt(bearer, 0, "apn bearer"));
}
// We are at the end tag
if (mInputParser.next() != XmlPullParser.END_TAG) {
throw new XmlPullParserException("Apn: expecting end tag @"
+ xmlParserDebugContext());
}
// We are done parsing one APN, call the handler
if (mApnHandler != null) {
mApnHandler.process(apnValues);
}
}
/** /**
* Process one mms_config. * Process one mms_config.
* *

View File

@@ -17,10 +17,8 @@
package com.android.messaging.sms; package com.android.messaging.sms;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.net.Uri; import android.net.Uri;
import android.provider.Telephony; import android.provider.Telephony;
@@ -119,151 +117,6 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
public void setSuccess() { public void setSuccess() {
// Do nothing // Do nothing
} }
public boolean equals(final BaseApn other) {
return TextUtils.equals(mMmsc, other.getMmsc()) &&
TextUtils.equals(mMmsProxy, other.getMmsProxy()) &&
mMmsProxyPort == other.getMmsProxyPort();
}
}
/**
* The APN represented by the local APN table row
*/
private static class DatabaseApn implements Apn {
private static final ContentValues CURRENT_NULL_VALUE;
private static final ContentValues CURRENT_SET_VALUE;
static {
CURRENT_NULL_VALUE = new ContentValues(1);
CURRENT_NULL_VALUE.putNull(Telephony.Carriers.CURRENT);
CURRENT_SET_VALUE = new ContentValues(1);
CURRENT_SET_VALUE.put(Telephony.Carriers.CURRENT, "1"); // 1 for auto selected APN
}
private static final String CLEAR_UPDATE_SELECTION = Telephony.Carriers.CURRENT + " =?";
private static final String[] CLEAR_UPDATE_SELECTION_ARGS = new String[] { "1" };
private static final String SET_UPDATE_SELECTION = Telephony.Carriers._ID + " =?";
/**
* Create an APN loaded from local database
*
* @param apns the in-memory APN list
* @param typesIn the APN type field
* @param mmscIn the APN mmsc field
* @param proxyIn the APN mmsproxy field
* @param portIn the APN mmsport field
* @param rowId the APN's row ID in database
* @param current the value of CURRENT column in database
* @return an in-memory APN instance for database APN row, null if parameter invalid
*/
public static DatabaseApn from(final List<Apn> apns, final String typesIn,
final String mmscIn, final String proxyIn, final String portIn,
final long rowId, final int current) {
if (apns == null) {
return null;
}
final BaseApn base = BaseApn.from(typesIn, mmscIn, proxyIn, portIn);
if (base == null) {
return null;
}
for (final ApnSettingsLoader.Apn apn : apns) {
if (apn instanceof DatabaseApn && ((DatabaseApn) apn).equals(base)) {
return null;
}
}
return new DatabaseApn(apns, base, rowId, current);
}
private final List<Apn> mApns;
private final BaseApn mBase;
private final long mRowId;
private int mCurrent;
public DatabaseApn(final List<Apn> apns, final BaseApn base, final long rowId,
final int current) {
mApns = apns;
mBase = base;
mRowId = rowId;
mCurrent = current;
}
@Override
public String getMmsc() {
return mBase.getMmsc();
}
@Override
public String getMmsProxy() {
return mBase.getMmsProxy();
}
@Override
public int getMmsProxyPort() {
return mBase.getMmsProxyPort();
}
@Override
public void setSuccess() {
moveToListHead();
setCurrentInDatabase();
}
/**
* Try to move this APN to the head of in-memory list
*/
private void moveToListHead() {
// If this is being marked as a successful APN, move it to the top of the list so
// next time it will be tried first
boolean moved = false;
synchronized (mApns) {
if (mApns.get(0) != this) {
mApns.remove(this);
mApns.add(0, this);
moved = true;
}
}
if (moved) {
LogUtil.d(LogUtil.BUGLE_TAG, "Set APN ["
+ "MMSC=" + getMmsc() + ", "
+ "PROXY=" + getMmsProxy() + ", "
+ "PORT=" + getMmsProxyPort() + "] to be first");
}
}
/**
* Try to set the APN to be CURRENT in its database table
*/
private void setCurrentInDatabase() {
synchronized (this) {
if (mCurrent > 0) {
// Already current
return;
}
mCurrent = 1;
}
LogUtil.d(LogUtil.BUGLE_TAG, "Set APN @" + mRowId + " to be CURRENT in local db");
final SQLiteDatabase database = ApnDatabase.getApnDatabase().getWritableDatabase();
database.beginTransaction();
try {
// clear the previous current=1 apn
// we don't clear current=2 apn since it is manually selected by user
// and we should not override it.
database.update(ApnDatabase.APN_TABLE, CURRENT_NULL_VALUE,
CLEAR_UPDATE_SELECTION, CLEAR_UPDATE_SELECTION_ARGS);
// set this one to be current (1)
database.update(ApnDatabase.APN_TABLE, CURRENT_SET_VALUE, SET_UPDATE_SELECTION,
new String[] { Long.toString(mRowId) });
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
}
public boolean equals(final BaseApn other) {
if (other == null) {
return false;
}
return mBase.equals(other);
}
} }
/** /**
@@ -280,14 +133,7 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
Telephony.Carriers.MMSPROXY, Telephony.Carriers.MMSPROXY,
Telephony.Carriers.MMSPORT, Telephony.Carriers.MMSPORT,
}; };
private static final String[] APN_PROJECTION_LOCAL = {
Telephony.Carriers.TYPE,
Telephony.Carriers.MMSC,
Telephony.Carriers.MMSPROXY,
Telephony.Carriers.MMSPORT,
Telephony.Carriers.CURRENT,
Telephony.Carriers._ID,
};
private static final int COLUMN_TYPE = 0; private static final int COLUMN_TYPE = 0;
private static final int COLUMN_MMSC = 1; private static final int COLUMN_MMSC = 1;
private static final int COLUMN_MMSPROXY = 2; private static final int COLUMN_MMSPROXY = 2;
@@ -297,8 +143,6 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
private static final String SELECTION_APN = Telephony.Carriers.APN + "=?"; private static final String SELECTION_APN = Telephony.Carriers.APN + "=?";
private static final String SELECTION_CURRENT = Telephony.Carriers.CURRENT + " IS NOT NULL"; private static final String SELECTION_CURRENT = Telephony.Carriers.CURRENT + " IS NOT NULL";
private static final String SELECTION_NUMERIC = Telephony.Carriers.NUMERIC + "=?";
private static final String ORDER_BY = Telephony.Carriers.CURRENT + " DESC";
private final Context mContext; private final Context mContext;
@@ -334,14 +178,6 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
private void loadLocked(final int subId, final String apnName, final List<Apn> apns) { private void loadLocked(final int subId, final String apnName, final List<Apn> apns) {
// Try system APN table // Try system APN table
loadFromSystem(subId, apnName, apns); loadFromSystem(subId, apnName, apns);
if (apns.size() > 0) {
return;
}
// Try local APN table
loadFromLocalDatabase(apnName, apns);
if (apns.size() <= 0) {
LogUtil.w(LogUtil.BUGLE_TAG, "Failed to load any APN");
}
} }
/** /**
@@ -455,85 +291,6 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
return null; return null;
} }
/**
* Load matching APNs from local APN table.
* We try both using the APN name and not using the APN name.
*
* @param apnName the APN name
* @param apns the list of results to return
*/
private void loadFromLocalDatabase(final String apnName, final List<Apn> apns) {
LogUtil.i(LogUtil.BUGLE_TAG, "Loading APNs from local APN table");
final SQLiteDatabase database = ApnDatabase.getApnDatabase().getWritableDatabase();
final String mccMnc = PhoneUtils.getMccMncString(PhoneUtils.getDefault().getMccMnc());
Cursor cursor;
cursor = queryLocalDatabase(database, mccMnc, apnName);
if (cursor == null) {
cursor = queryLocalDatabase(database, mccMnc, null/*apnName*/);
}
if (cursor == null) {
LogUtil.w(LogUtil.BUGLE_TAG, "Could not find any APN in local table");
return;
}
try {
while (cursor.moveToNext()) {
final Apn apn = DatabaseApn.from(apns,
cursor.getString(COLUMN_TYPE),
cursor.getString(COLUMN_MMSC),
cursor.getString(COLUMN_MMSPROXY),
cursor.getString(COLUMN_MMSPORT),
cursor.getLong(COLUMN_ID),
cursor.getInt(COLUMN_CURRENT));
if (apn != null) {
apns.add(apn);
}
}
} finally {
cursor.close();
}
}
/**
* Make a query of local APN table based on MCC/MNC and APN name, sorted by CURRENT
* column in descending order
*
* @param db the local database
* @param numeric the MCC/MNC string
* @param apnName the optional APN name to match
* @return the cursor of the query, null if no result
*/
private static Cursor queryLocalDatabase(final SQLiteDatabase db, final String numeric,
final String apnName) {
final String selection;
final String[] selectionArgs;
if (TextUtils.isEmpty(apnName)) {
selection = SELECTION_NUMERIC;
selectionArgs = new String[] { numeric };
} else {
selection = SELECTION_NUMERIC + " AND " + SELECTION_APN;
selectionArgs = new String[] { numeric, apnName };
}
Cursor cursor;
try {
cursor = db.query(ApnDatabase.APN_TABLE, APN_PROJECTION_LOCAL, selection, selectionArgs,
null/*groupBy*/, null/*having*/, ORDER_BY, null/*limit*/);
} catch (final SQLiteException e) {
LogUtil.w(LogUtil.BUGLE_TAG, "Local APN table does not exist. Try rebuilding.", e);
ApnDatabase.forceBuildAndLoadApnTables();
cursor = db.query(ApnDatabase.APN_TABLE, APN_PROJECTION_LOCAL, selection, selectionArgs,
null/*groupBy*/, null/*having*/, ORDER_BY, null/*limit*/);
}
if (cursor == null || cursor.getCount() < 1) {
if (cursor != null) {
cursor.close();
}
LogUtil.w(LogUtil.BUGLE_TAG, "Query local APNs with apn " + apnName
+ " returned empty");
return null;
}
return cursor;
}
private static String trimWithNullCheck(final String value) { private static String trimWithNullCheck(final String value) {
return value != null ? value.trim() : null; return value != null ? value.trim() : null;
} }