summaryrefslogtreecommitdiffstats
path: root/src/com/android/messaging/sms/ApnDatabase.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/messaging/sms/ApnDatabase.java')
-rw-r--r--src/com/android/messaging/sms/ApnDatabase.java374
1 files changed, 374 insertions, 0 deletions
diff --git a/src/com/android/messaging/sms/ApnDatabase.java b/src/com/android/messaging/sms/ApnDatabase.java
new file mode 100644
index 0000000..a8d0d0c
--- /dev/null
+++ b/src/com/android/messaging/sms/ApnDatabase.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.provider.Telephony;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.messaging.R;
+import com.android.messaging.datamodel.data.ParticipantData;
+import com.android.messaging.util.LogUtil;
+import com.android.messaging.util.PhoneUtils;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * 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 String[] APN_PROJECTION = {
+ Telephony.Carriers.TYPE, // 0
+ Telephony.Carriers.MMSC, // 1
+ Telephony.Carriers.MMSPROXY, // 2
+ Telephony.Carriers.MMSPORT, // 3
+ Telephony.Carriers._ID, // 4
+ Telephony.Carriers.CURRENT, // 5
+ Telephony.Carriers.NUMERIC, // 6
+ Telephony.Carriers.NAME, // 7
+ Telephony.Carriers.MCC, // 8
+ Telephony.Carriers.MNC, // 9
+ Telephony.Carriers.APN, // 10
+ Telephony.Carriers.SUBSCRIPTION_ID // 11
+ };
+
+ public static final int COLUMN_TYPE = 0;
+ public static final int COLUMN_MMSC = 1;
+ public static final int COLUMN_MMSPROXY = 2;
+ public static final int COLUMN_MMSPORT = 3;
+ public static final int COLUMN_ID = 4;
+ public static final int COLUMN_CURRENT = 5;
+ public static final int COLUMN_NUMERIC = 6;
+ public static final int COLUMN_NAME = 7;
+ public static final int COLUMN_MCC = 8;
+ public static final int COLUMN_MNC = 9;
+ public static final int COLUMN_APN = 10;
+ public static final int COLUMN_SUB_ID = 11;
+
+ public static final String[] APN_FULL_PROJECTION = {
+ Telephony.Carriers.NAME,
+ Telephony.Carriers.MCC,
+ Telephony.Carriers.MNC,
+ Telephony.Carriers.APN,
+ Telephony.Carriers.USER,
+ Telephony.Carriers.SERVER,
+ Telephony.Carriers.PASSWORD,
+ Telephony.Carriers.PROXY,
+ Telephony.Carriers.PORT,
+ Telephony.Carriers.MMSC,
+ Telephony.Carriers.MMSPROXY,
+ Telephony.Carriers.MMSPORT,
+ Telephony.Carriers.AUTH_TYPE,
+ Telephony.Carriers.TYPE,
+ Telephony.Carriers.PROTOCOL,
+ Telephony.Carriers.ROAMING_PROTOCOL,
+ Telephony.Carriers.CARRIER_ENABLED,
+ Telephony.Carriers.BEARER,
+ Telephony.Carriers.MVNO_TYPE,
+ Telephony.Carriers.MVNO_MATCH_DATA,
+ Telephony.Carriers.CURRENT,
+ Telephony.Carriers.SUBSCRIPTION_ID,
+ };
+
+ private static final String CURRENT_SELECTION = Telephony.Carriers.CURRENT + " NOT NULL";
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Get a copy of user changes in the old table
+ *
+ * @return The list of user changed apns
+ */
+ public static List<ContentValues> loadUserDataFromOldTable(final SQLiteDatabase db) {
+ Cursor cursor = null;
+ try {
+ cursor = db.query(APN_TABLE,
+ APN_FULL_PROJECTION, CURRENT_SELECTION,
+ null/*selectionArgs*/,
+ null/*groupBy*/, null/*having*/, null/*orderBy*/);
+ if (cursor != null) {
+ final List<ContentValues> result = Lists.newArrayList();
+ while (cursor.moveToNext()) {
+ final ContentValues row = cursorToValues(cursor);
+ if (row != null) {
+ result.add(row);
+ }
+ }
+ return result;
+ }
+ } catch (final SQLiteException e) {
+ LogUtil.w(TAG, "ApnDatabase.loadUserDataFromOldTable: no old user data: " + e, e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return null;
+ }
+
+ private static final String[] ID_PROJECTION = new String[]{Telephony.Carriers._ID};
+
+ private static final String ID_SELECTION = Telephony.Carriers._ID + "=?";
+
+ /**
+ * Store use changes of old table into the new apn table
+ *
+ * @param data The user changes
+ */
+ public static void saveUserDataFromOldTable(
+ final SQLiteDatabase db, final List<ContentValues> data) {
+ if (data == null || data.size() < 1) {
+ return;
+ }
+ for (final ContentValues row : data) {
+ // Build query from the row data. It is an exact match, column by column,
+ // except the CURRENT column
+ final StringBuilder selectionBuilder = new StringBuilder();
+ final ArrayList<String> selectionArgs = Lists.newArrayList();
+ for (final String key : row.keySet()) {
+ if (!Telephony.Carriers.CURRENT.equals(key)) {
+ if (selectionBuilder.length() > 0) {
+ selectionBuilder.append(" AND ");
+ }
+ final String value = row.getAsString(key);
+ if (TextUtils.isEmpty(value)) {
+ selectionBuilder.append(key).append(" IS NULL");
+ } else {
+ selectionBuilder.append(key).append("=?");
+ selectionArgs.add(value);
+ }
+ }
+ }
+ Cursor cursor = null;
+ try {
+ cursor = db.query(APN_TABLE,
+ ID_PROJECTION,
+ selectionBuilder.toString(),
+ selectionArgs.toArray(new String[0]),
+ null/*groupBy*/, null/*having*/, null/*orderBy*/);
+ if (cursor != null && cursor.moveToFirst()) {
+ db.update(APN_TABLE, row, ID_SELECTION, new String[]{cursor.getString(0)});
+ } else {
+ // User APN does not exist, insert into the new table
+ row.put(Telephony.Carriers.NUMERIC,
+ PhoneUtils.canonicalizeMccMnc(
+ row.getAsString(Telephony.Carriers.MCC),
+ row.getAsString(Telephony.Carriers.MNC))
+ );
+ db.insert(APN_TABLE, null/*nullColumnHack*/, row);
+ }
+ } catch (final SQLiteException e) {
+ LogUtil.e(TAG, "ApnDatabase.saveUserDataFromOldTable: query error " + e, e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ }
+
+ // Convert Cursor to ContentValues
+ private static ContentValues cursorToValues(final Cursor cursor) {
+ final int columnCount = cursor.getColumnCount();
+ if (columnCount > 0) {
+ final ContentValues result = new ContentValues();
+ for (int i = 0; i < columnCount; i++) {
+ final String name = cursor.getColumnName(i);
+ final String value = cursor.getString(i);
+ result.put(name, value);
+ }
+ return result;
+ }
+ return null;
+ }
+
+ @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) {
+ if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
+ 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(new ApnsXmlProcessor.ApnHandler() {
+ @Override
+ public void process(final ContentValues 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);
+ }
+
+ /**
+ * Clear all tables
+ */
+ public static void clearTables() {
+ final SQLiteDatabase db = getApnDatabase().getWritableDatabase();
+ db.execSQL("DROP TABLE IF EXISTS " + APN_TABLE);
+ db.execSQL(APN_TABLE_SQL);
+ }
+}