summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/providers/telephony/MmsProvider.java17
-rw-r--r--src/com/android/providers/telephony/MmsSmsDatabaseHelper.java151
-rw-r--r--src/com/android/providers/telephony/MmsSmsProvider.java3
-rw-r--r--src/com/android/providers/telephony/ProviderUtil.java57
-rw-r--r--src/com/android/providers/telephony/SmsProvider.java118
-rw-r--r--src/com/android/providers/telephony/TelephonyBackupAgent.java1309
-rw-r--r--src/com/android/providers/telephony/TelephonyProvider.java829
7 files changed, 2076 insertions, 408 deletions
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index b7e410a..71e84c6 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -66,12 +66,13 @@ public class MmsProvider extends ContentProvider {
static final String VIEW_PDU_RESTRICTED = "pdu_restricted";
// The name of parts directory. The full dir is "app_parts".
- private static final String PARTS_DIR_NAME = "parts";
+ static final String PARTS_DIR_NAME = "parts";
@Override
public boolean onCreate() {
setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
- mOpenHelper = MmsSmsDatabaseHelper.getInstance(getContext());
+ mOpenHelper = MmsSmsDatabaseHelper.getInstanceForCe(getContext());
+ TelephonyBackupAgent.DeferredSmsMmsRestoreService.startIfFilesExist(getContext());
return true;
}
@@ -552,7 +553,7 @@ public class MmsProvider extends ContentProvider {
}
if (notify) {
- notifyChange();
+ notifyChange(res);
}
return res;
}
@@ -648,7 +649,7 @@ public class MmsProvider extends ContentProvider {
}
if ((deletedRows > 0) && notify) {
- notifyChange();
+ notifyChange(uri);
}
return deletedRows;
}
@@ -824,7 +825,7 @@ public class MmsProvider extends ContentProvider {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count = db.update(table, finalValues, finalSelection, selectionArgs);
if (notify && (count > 0)) {
- notifyChange();
+ notifyChange(uri);
}
return count;
}
@@ -941,9 +942,11 @@ public class MmsProvider extends ContentProvider {
values.remove(Mms._ID);
}
- private void notifyChange() {
- getContext().getContentResolver().notifyChange(
+ private void notifyChange(final Uri uri) {
+ final Context context = getContext();
+ context.getContentResolver().notifyChange(
MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
+ ProviderUtil.notifyIfNotDefaultSmsApp(uri, getCallingPackage(), context);
}
private final static String TAG = "MmsProvider";
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index e763f35..610418e 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -24,6 +24,7 @@ import android.content.IntentFilter;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.os.storage.StorageManager;
import android.provider.BaseColumns;
import android.provider.Telephony;
import android.provider.Telephony.Mms;
@@ -48,6 +49,23 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
+/**
+ * A {@link SQLiteOpenHelper} that handles DB management of SMS and MMS tables.
+ *
+ * From N, SMS and MMS tables are split into two groups with different levels of encryption.
+ * - the raw table, which lives inside DE(Device Encrypted) storage.
+ * - all other tables, which lives under CE(Credential Encrypted) storage.
+ *
+ * All tables are created by this class in the same database that can live either in DE or CE
+ * storage. But not all tables in the same database should be used. Only DE tables should be used
+ * in the database created in DE and only CE tables should be used in the database created in CE.
+ * The only exception is a non-FBE device migrating from M to N, in which case the DE and CE tables
+ * will actually live inside the same storage/database.
+ *
+ * This class provides methods to create instances that manage databases in different storage.
+ * It's the responsibility of the clients of this class to make sure the right instance is
+ * used to access tables that are supposed to live inside the intended storage.
+ */
public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "MmsSmsDatabaseHelper";
@@ -211,12 +229,13 @@ public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
" AND part.mid = pdu._id);" +
" END";
- private static MmsSmsDatabaseHelper sInstance = null;
+ private static MmsSmsDatabaseHelper sDeInstance = null;
+ private static MmsSmsDatabaseHelper sCeInstance = null;
private static boolean sTriedAutoIncrement = false;
private static boolean sFakeLowStorageTest = false; // for testing only
static final String DATABASE_NAME = "mmssms.db";
- static final int DATABASE_VERSION = 61;
+ static final int DATABASE_VERSION = 64;
private final Context mContext;
private LowStorageMonitor mLowStorageMonitor;
@@ -228,14 +247,29 @@ public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
}
/**
- * Return a singleton helper for the combined MMS and SMS
- * database.
+ * Returns a singleton helper for the combined MMS and SMS database in device encrypted storage.
+ */
+ /* package */ static synchronized MmsSmsDatabaseHelper getInstanceForDe(Context context) {
+ if (sDeInstance == null) {
+ sDeInstance = new MmsSmsDatabaseHelper(ProviderUtil.getDeviceEncryptedContext(context));
+ }
+ return sDeInstance;
+ }
+
+ /**
+ * Returns a singleton helper for the combined MMS and SMS database in credential encrypted
+ * storage. If FBE is not available, use the device encrypted storage instead.
*/
- /* package */ static synchronized MmsSmsDatabaseHelper getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new MmsSmsDatabaseHelper(context);
+ /* package */ static synchronized MmsSmsDatabaseHelper getInstanceForCe(Context context) {
+ if (sCeInstance == null) {
+ if (StorageManager.isFileEncryptedNativeOrEmulated()) {
+ sCeInstance = new MmsSmsDatabaseHelper(
+ ProviderUtil.getCredentialEncryptedContext(context));
+ } else {
+ sCeInstance = getInstanceForDe(context);
+ }
}
- return sInstance;
+ return sCeInstance;
}
/**
@@ -267,15 +301,15 @@ public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
// Now build a selection string of all the unique recipient ids
StringBuilder sb = new StringBuilder();
Iterator<Integer> iter = recipientIds.iterator();
+ sb.append("_id NOT IN (");
while (iter.hasNext()) {
- sb.append("_id != " + iter.next());
+ sb.append(iter.next());
if (iter.hasNext()) {
- sb.append(" AND ");
+ sb.append(",");
}
}
- if (sb.length() > 0) {
- int rows = db.delete("canonical_addresses", sb.toString(), null);
- }
+ sb.append(")");
+ int rows = db.delete("canonical_addresses", sb.toString(), null);
}
} finally {
c.close();
@@ -867,7 +901,9 @@ public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
"destination_port INTEGER," +
"address TEXT," +
"sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
- "pdu TEXT);"); // the raw PDU for this part
+ "pdu TEXT," + // the raw PDU for this part
+ "deleted INTEGER DEFAULT 0," + // bool to indicate if row is deleted
+ "message_body TEXT);"); // message body
db.execSQL("CREATE TABLE attachments (" +
"sms_id INTEGER," +
@@ -1363,6 +1399,55 @@ public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
} finally {
db.endTransaction();
}
+ // fall through
+ case 61:
+ if (currentVersion <= 61) {
+ return;
+ }
+
+ db.beginTransaction();
+ try {
+ upgradeDatabaseToVersion62(db);
+ db.setTransactionSuccessful();
+ } catch (Throwable ex) {
+ Log.e(TAG, ex.getMessage(), ex);
+ break;
+ } finally {
+ db.endTransaction();
+ }
+ // fall through
+ case 62:
+ if (currentVersion <= 62) {
+ return;
+ }
+
+ db.beginTransaction();
+ try {
+ upgradeDatabaseToVersion63(db);
+ db.setTransactionSuccessful();
+ } catch (Throwable ex) {
+ Log.e(TAG, ex.getMessage(), ex);
+ break;
+ } finally {
+ db.endTransaction();
+ }
+ // fall through
+ case 63:
+ if (currentVersion <= 63) {
+ return;
+ }
+
+ db.beginTransaction();
+ try {
+ upgradeDatabaseToVersion64(db);
+ db.setTransactionSuccessful();
+ } catch (Throwable ex) {
+ Log.e(TAG, ex.getMessage(), ex);
+ break;
+ } finally {
+ db.endTransaction();
+ }
+
return;
}
@@ -1607,6 +1692,44 @@ public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
}
+ private void upgradeDatabaseToVersion62(SQLiteDatabase db) {
+ // When a non-FBE device is upgraded to N, all MMS attachment files are moved from
+ // /data/data to /data/user_de. We need to update the paths stored in the parts table to
+ // reflect this change.
+ String newPartsDirPath;
+ try {
+ newPartsDirPath = mContext.getDir(MmsProvider.PARTS_DIR_NAME, 0).getCanonicalPath();
+ }
+ catch (IOException e){
+ Log.e(TAG, "openFile: check file path failed " + e, e);
+ return;
+ }
+
+ // The old path of the part files will be something like this:
+ // /data/data/0/com.android.providers.telephony/app_parts
+ // The new path of the part files will be something like this:
+ // /data/user_de/0/com.android.providers.telephony/app_parts
+ int partsDirIndex = newPartsDirPath.lastIndexOf(
+ File.separator, newPartsDirPath.lastIndexOf(MmsProvider.PARTS_DIR_NAME));
+ String partsDirName = newPartsDirPath.substring(partsDirIndex) + File.separator;
+ // The query to update the part path will be:
+ // UPDATE part SET _data = '/data/user_de/0/com.android.providers.telephony' ||
+ // SUBSTR(_data, INSTR(_data, '/app_parts/'))
+ // WHERE INSTR(_data, '/app_parts/') > 0
+ db.execSQL("UPDATE " + MmsProvider.TABLE_PART +
+ " SET " + Part._DATA + " = '" + newPartsDirPath.substring(0, partsDirIndex) + "' ||" +
+ " SUBSTR(" + Part._DATA + ", INSTR(" + Part._DATA + ", '" + partsDirName + "'))" +
+ " WHERE INSTR(" + Part._DATA + ", '" + partsDirName + "') > 0");
+ }
+
+ private void upgradeDatabaseToVersion63(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + SmsProvider.TABLE_RAW +" ADD COLUMN deleted INTEGER DEFAULT 0");
+ }
+
+ private void upgradeDatabaseToVersion64(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + SmsProvider.TABLE_RAW +" ADD COLUMN message_body TEXT");
+ }
+
@Override
public synchronized SQLiteDatabase getWritableDatabase() {
SQLiteDatabase db = super.getWritableDatabase();
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 0e4e447..d5e0ef9 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -308,10 +308,11 @@ public class MmsSmsProvider extends ContentProvider {
@Override
public boolean onCreate() {
setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
- mOpenHelper = MmsSmsDatabaseHelper.getInstance(getContext());
+ mOpenHelper = MmsSmsDatabaseHelper.getInstanceForCe(getContext());
mUseStrictPhoneNumberComparation =
getContext().getResources().getBoolean(
com.android.internal.R.bool.config_use_strict_phone_number_comparation);
+ TelephonyBackupAgent.DeferredSmsMmsRestoreService.startIfFilesExist(getContext());
return true;
}
diff --git a/src/com/android/providers/telephony/ProviderUtil.java b/src/com/android/providers/telephony/ProviderUtil.java
index 9435409..4234b06 100644
--- a/src/com/android/providers/telephony/ProviderUtil.java
+++ b/src/com/android/providers/telephony/ProviderUtil.java
@@ -16,10 +16,15 @@
package com.android.providers.telephony;
+import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Process;
import android.provider.Telephony;
+import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.telephony.SmsApplication;
@@ -27,6 +32,7 @@ import com.android.internal.telephony.SmsApplication;
* Helpers
*/
public class ProviderUtil {
+ private final static String TAG = "SmsProvider";
/**
* Check if a caller of the provider has restricted access,
@@ -68,4 +74,55 @@ public class ProviderUtil {
(values.containsKey(Telephony.Sms.CREATOR) ||
values.containsKey(Telephony.Mms.CREATOR));
}
+
+ /**
+ * Notify the default SMS app of an SMS/MMS provider change if the change is being made
+ * by a package other than the default SMS app itself.
+ *
+ * @param uri The uri the provider change applies to
+ * @param callingPackage The package name of the provider caller
+ * @param Context
+ */
+ public static void notifyIfNotDefaultSmsApp(final Uri uri, final String callingPackage,
+ final Context context) {
+ if (TextUtils.equals(callingPackage, Telephony.Sms.getDefaultSmsPackage(context))) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "notifyIfNotDefaultSmsApp - called from default sms app");
+ }
+ return;
+ }
+ // Direct the intent to only the default SMS app, and only if the SMS app has a receiver
+ // for the intent.
+ ComponentName componentName =
+ SmsApplication.getDefaultExternalTelephonyProviderChangedApplication(context, true);
+ if (componentName == null) {
+ return; // the default sms app doesn't have a receiver for this intent
+ }
+
+ final Intent intent =
+ new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE);
+ intent.setFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.setComponent(componentName);
+ if (uri != null) {
+ intent.setData(uri);
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "notifyIfNotDefaultSmsApp - called from " + callingPackage + ", notifying");
+ }
+ context.sendBroadcast(intent);
+ }
+
+ public static Context getCredentialEncryptedContext(Context context) {
+ if (context.isCredentialProtectedStorage()) {
+ return context;
+ }
+ return context.createCredentialProtectedStorageContext();
+ }
+
+ public static Context getDeviceEncryptedContext(Context context) {
+ if (context.isDeviceProtectedStorage()) {
+ return context;
+ }
+ return context.createDeviceProtectedStorageContext();
+ }
}
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index d48f1c6..f50f804 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -16,10 +16,12 @@
package com.android.providers.telephony;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -59,6 +61,9 @@ public class SmsProvider extends ContentProvider {
new String[] { Contacts.Phones.PERSON_ID };
private static final int PERSON_ID_COLUMN = 0;
+ /** Delete any raw messages or message segments marked deleted that are older than an hour. */
+ static final long RAW_MESSAGE_EXPIRE_AGE_MS = (long) (60 * 60 * 1000);
+
/**
* These are the columns that are available when reading SMS
* messages from the ICC. Columns whose names begin with "is_"
@@ -85,7 +90,9 @@ public class SmsProvider extends ContentProvider {
@Override
public boolean onCreate() {
setAppOps(AppOpsManager.OP_READ_SMS, AppOpsManager.OP_WRITE_SMS);
- mOpenHelper = MmsSmsDatabaseHelper.getInstance(getContext());
+ mDeOpenHelper = MmsSmsDatabaseHelper.getInstanceForDe(getContext());
+ mCeOpenHelper = MmsSmsDatabaseHelper.getInstanceForCe(getContext());
+ TelephonyBackupAgent.DeferredSmsMmsRestoreService.startIfFilesExist(getContext());
return true;
}
@@ -113,6 +120,7 @@ public class SmsProvider extends ContentProvider {
// Generate the body of the query.
int match = sURLMatcher.match(url);
+ SQLiteDatabase db = getDBOpenHelper(match).getReadableDatabase();
switch (match) {
case SMS_ALL:
constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL, smsTable);
@@ -201,6 +209,8 @@ public class SmsProvider extends ContentProvider {
break;
case SMS_RAW_MESSAGE:
+ // before querying purge old entries with deleted = 1
+ purgeDeletedMessagesInRawTable(db);
qb.setTables("raw");
break;
@@ -251,7 +261,6 @@ public class SmsProvider extends ContentProvider {
orderBy = Sms.DEFAULT_SORT_ORDER;
}
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor ret = qb.query(db, projectionIn, selection, selectionArgs,
null, null, orderBy);
@@ -261,6 +270,22 @@ public class SmsProvider extends ContentProvider {
return ret;
}
+ private void purgeDeletedMessagesInRawTable(SQLiteDatabase db) {
+ long oldTimestamp = System.currentTimeMillis() - RAW_MESSAGE_EXPIRE_AGE_MS;
+ int num = db.delete(TABLE_RAW, "deleted = 1 AND date < " + oldTimestamp, null);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "purgeDeletedMessagesInRawTable: num rows older than " + oldTimestamp +
+ " purged: " + num);
+ }
+ }
+
+ private SQLiteOpenHelper getDBOpenHelper(int match) {
+ if (match == SMS_RAW_MESSAGE) {
+ return mDeOpenHelper;
+ }
+ return mCeOpenHelper;
+ }
+
private Object[] convertIccToSms(SmsMessage message, int id) {
// N.B.: These calls must appear in the same order as the
// columns appear in ICC_COLUMNS.
@@ -384,12 +409,44 @@ public class SmsProvider extends ContentProvider {
}
@Override
+ public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] values) {
+ final int callerUid = Binder.getCallingUid();
+ final String callerPkg = getCallingPackage();
+ long token = Binder.clearCallingIdentity();
+ try {
+ int messagesInserted = 0;
+ for (ContentValues initialValues : values) {
+ Uri insertUri = insertInner(url, initialValues, callerUid, callerPkg);
+ if (insertUri != null) {
+ messagesInserted++;
+ }
+ }
+
+ // The raw table is used by the telephony layer for storing an sms before
+ // sending out a notification that an sms has arrived. We don't want to notify
+ // the default sms app of changes to this table.
+ final boolean notifyIfNotDefault = sURLMatcher.match(url) != SMS_RAW_MESSAGE;
+ notifyChange(notifyIfNotDefault, url, callerPkg);
+ return messagesInserted;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public Uri insert(Uri url, ContentValues initialValues) {
final int callerUid = Binder.getCallingUid();
final String callerPkg = getCallingPackage();
long token = Binder.clearCallingIdentity();
try {
- return insertInner(url, initialValues, callerUid, callerPkg);
+ Uri insertUri = insertInner(url, initialValues, callerUid, callerPkg);
+
+ // The raw table is used by the telephony layer for storing an sms before
+ // sending out a notification that an sms has arrived. We don't want to notify
+ // the default sms app of changes to this table.
+ final boolean notifyIfNotDefault = sURLMatcher.match(url) != SMS_RAW_MESSAGE;
+ notifyChange(notifyIfNotDefault, insertUri, callerPkg);
+ return insertUri;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -402,6 +459,7 @@ public class SmsProvider extends ContentProvider {
int match = sURLMatcher.match(url);
String table = TABLE_SMS;
+ boolean notifyIfNotDefault = true;
switch (match) {
case SMS_ALL:
@@ -440,6 +498,10 @@ public class SmsProvider extends ContentProvider {
case SMS_RAW_MESSAGE:
table = "raw";
+ // The raw table is used by the telephony layer for storing an sms before
+ // sending out a notification that an sms has arrived. We don't want to notify
+ // the default sms app of changes to this table.
+ notifyIfNotDefault = false;
break;
case SMS_STATUS_PENDING:
@@ -459,7 +521,7 @@ public class SmsProvider extends ContentProvider {
return null;
}
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
if (table.equals(TABLE_SMS)) {
boolean addDate = false;
@@ -575,10 +637,9 @@ public class SmsProvider extends ContentProvider {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "insert " + uri + " succeeded");
}
- notifyChange(uri);
return uri;
} else {
- Log.e(TAG,"insert: failed!");
+ Log.e(TAG, "insert: failed!");
}
return null;
@@ -588,7 +649,8 @@ public class SmsProvider extends ContentProvider {
public int delete(Uri url, String where, String[] whereArgs) {
int count;
int match = sURLMatcher.match(url);
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
+ boolean notifyIfNotDefault = true;
switch (match) {
case SMS_ALL:
count = db.delete(TABLE_SMS, where, whereArgs);
@@ -626,7 +688,21 @@ public class SmsProvider extends ContentProvider {
break;
case SMS_RAW_MESSAGE:
- count = db.delete("raw", where, whereArgs);
+ ContentValues cv = new ContentValues();
+ cv.put("deleted", 1);
+ count = db.update(TABLE_RAW, cv, where, whereArgs);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "delete: num rows marked deleted in raw table: " + count);
+ }
+ notifyIfNotDefault = false;
+ break;
+
+ case SMS_RAW_MESSAGE_PERMANENT_DELETE:
+ count = db.delete(TABLE_RAW, where, whereArgs);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "delete: num rows permanently deleted in raw table: " + count);
+ }
+ notifyIfNotDefault = false;
break;
case SMS_STATUS_PENDING:
@@ -643,7 +719,7 @@ public class SmsProvider extends ContentProvider {
}
if (count > 0) {
- notifyChange(url);
+ notifyChange(notifyIfNotDefault, url, getCallingPackage());
}
return count;
}
@@ -678,11 +754,14 @@ public class SmsProvider extends ContentProvider {
int count = 0;
String table = TABLE_SMS;
String extraWhere = null;
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ boolean notifyIfNotDefault = true;
+ int match = sURLMatcher.match(url);
+ SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
- switch (sURLMatcher.match(url)) {
+ switch (match) {
case SMS_RAW_MESSAGE:
table = TABLE_RAW;
+ notifyIfNotDefault = false;
break;
case SMS_STATUS_PENDING:
@@ -747,20 +826,27 @@ public class SmsProvider extends ContentProvider {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "update " + url + " succeeded");
}
- notifyChange(url);
+ notifyChange(notifyIfNotDefault, url, callerPkg);
}
return count;
}
- private void notifyChange(Uri uri) {
- ContentResolver cr = getContext().getContentResolver();
+ private void notifyChange(boolean notifyIfNotDefault, Uri uri, final String callingPackage) {
+ final Context context = getContext();
+ ContentResolver cr = context.getContentResolver();
cr.notifyChange(uri, null, true, UserHandle.USER_ALL);
cr.notifyChange(MmsSms.CONTENT_URI, null, true, UserHandle.USER_ALL);
cr.notifyChange(Uri.parse("content://mms-sms/conversations/"), null, true,
UserHandle.USER_ALL);
+ if (notifyIfNotDefault) {
+ ProviderUtil.notifyIfNotDefaultSmsApp(uri, callingPackage, context);
+ }
}
- private SQLiteOpenHelper mOpenHelper;
+ // Db open helper for tables stored in CE(Credential Encrypted) storage.
+ private SQLiteOpenHelper mCeOpenHelper;
+ // Db open helper for tables stored in DE(Device Encrypted) storage.
+ private SQLiteOpenHelper mDeOpenHelper;
private final static String TAG = "SmsProvider";
private final static String VND_ANDROID_SMS = "vnd.android.cursor.item/sms";
@@ -796,6 +882,7 @@ public class SmsProvider extends ContentProvider {
private static final int SMS_FAILED_ID = 25;
private static final int SMS_QUEUED = 26;
private static final int SMS_UNDELIVERED = 27;
+ private static final int SMS_RAW_MESSAGE_PERMANENT_DELETE = 28;
private static final UriMatcher sURLMatcher =
new UriMatcher(UriMatcher.NO_MATCH);
@@ -818,6 +905,7 @@ public class SmsProvider extends ContentProvider {
sURLMatcher.addURI("sms", "conversations", SMS_CONVERSATIONS);
sURLMatcher.addURI("sms", "conversations/*", SMS_CONVERSATIONS_ID);
sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE);
+ sURLMatcher.addURI("sms", "raw/permanentDelete", SMS_RAW_MESSAGE_PERMANENT_DELETE);
sURLMatcher.addURI("sms", "attachments", SMS_ATTACHMENT);
sURLMatcher.addURI("sms", "attachments/#", SMS_ATTACHMENT_ID);
sURLMatcher.addURI("sms", "threadID", SMS_NEW_THREAD_ID);
diff --git a/src/com/android/providers/telephony/TelephonyBackupAgent.java b/src/com/android/providers/telephony/TelephonyBackupAgent.java
new file mode 100644
index 0000000..7a9d701
--- /dev/null
+++ b/src/com/android/providers/telephony/TelephonyBackupAgent.java
@@ -0,0 +1,1309 @@
+/*
+ * Copyright (C) 2016 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.providers.telephony;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.pdu.CharacterSets;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import android.annotation.TargetApi;
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackupDataOutput;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.provider.BaseColumns;
+import android.provider.Telephony;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+/***
+ * Backup agent for backup and restore SMS's and text MMS's.
+ *
+ * This backup agent stores SMS's into "sms_backup" file as a JSON array. Example below.
+ * [{"self_phone":"+1234567891011","address":"+1234567891012","body":"Example sms",
+ * "date":"1450893518140","date_sent":"1450893514000","status":"-1","type":"1"},
+ * {"self_phone":"+1234567891011","address":"12345","body":"Example 2","date":"1451328022316",
+ * "date_sent":"1451328018000","status":"-1","type":"1"}]
+ *
+ * Text MMS's are stored into "mms_backup" file as a JSON array. Example below.
+ * [{"self_phone":"+1234567891011","date":"1451322716","date_sent":"0","m_type":"128","v":"18",
+ * "msg_box":"2","mms_addresses":[{"type":137,"address":"+1234567891011","charset":106},
+ * {"type":151,"address":"example@example.com","charset":106}],"mms_body":"Mms to email",
+ * "mms_charset":106},
+ * {"self_phone":"+1234567891011","sub":"MMS subject","date":"1451322955","date_sent":"0",
+ * "m_type":"132","v":"17","msg_box":"1","ct_l":"http://promms/servlets/NOK5BBqgUHAqugrQNM",
+ * "mms_addresses":[{"type":151,"address":"+1234567891011","charset":106}],
+ * "mms_body":"Mms\nBody\r\n",
+ * "mms_charset":106,"sub_cs":"106"}]
+ *
+ * It deflates the files on the flight.
+ * Every 1000 messages it backs up file, deletes it and creates a new one with the same name.
+ *
+ * It stores how many bytes we are over the quota and don't backup the oldest messages.
+ */
+
+@TargetApi(Build.VERSION_CODES.M)
+public class TelephonyBackupAgent extends BackupAgent {
+ private static final String TAG = "TelephonyBackupAgent";
+ private static final boolean DEBUG = false;
+
+
+ // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
+ private static final int DEFAULT_DURATION = 5000; //ms
+
+ // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
+ @VisibleForTesting
+ static final String sSmilTextOnly =
+ "<smil>" +
+ "<head>" +
+ "<layout>" +
+ "<root-layout/>" +
+ "<region id=\"Text\" top=\"0\" left=\"0\" "
+ + "height=\"100%%\" width=\"100%%\"/>" +
+ "</layout>" +
+ "</head>" +
+ "<body>" +
+ "%s" + // constructed body goes here
+ "</body>" +
+ "</smil>";
+
+ // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
+ @VisibleForTesting
+ static final String sSmilTextPart =
+ "<par dur=\"" + DEFAULT_DURATION + "ms\">" +
+ "<text src=\"%s\" region=\"Text\" />" +
+ "</par>";
+
+
+ // JSON key for phone number a message was sent from or received to.
+ private static final String SELF_PHONE_KEY = "self_phone";
+ // JSON key for list of addresses of MMS message.
+ private static final String MMS_ADDRESSES_KEY = "mms_addresses";
+ // JSON key for list of recipients of the message.
+ private static final String RECIPIENTS = "recipients";
+ // JSON key for MMS body.
+ private static final String MMS_BODY_KEY = "mms_body";
+ // JSON key for MMS charset.
+ private static final String MMS_BODY_CHARSET_KEY = "mms_charset";
+
+ // File names suffixes for backup/restore.
+ private static final String SMS_BACKUP_FILE_SUFFIX = "_sms_backup";
+ private static final String MMS_BACKUP_FILE_SUFFIX = "_mms_backup";
+
+ // File name formats for backup. It looks like 000000_sms_backup, 000001_sms_backup, etc.
+ private static final String SMS_BACKUP_FILE_FORMAT = "%06d"+SMS_BACKUP_FILE_SUFFIX;
+ private static final String MMS_BACKUP_FILE_FORMAT = "%06d"+MMS_BACKUP_FILE_SUFFIX;
+
+ // Charset being used for reading/writing backup files.
+ private static final String CHARSET_UTF8 = "UTF-8";
+
+ // Order by ID entries from database.
+ private static final String ORDER_BY_ID = BaseColumns._ID + " ASC";
+
+ // Order by Date entries from database. We start backup from the oldest.
+ private static final String ORDER_BY_DATE = "date ASC";
+
+ // This is a hard coded string rather than a localized one because we don't want it to
+ // change when you change locale.
+ @VisibleForTesting
+ static final String UNKNOWN_SENDER = "\u02BCUNKNOWN_SENDER!\u02BC";
+
+ // Thread id for UNKNOWN_SENDER.
+ private long mUnknownSenderThreadId;
+
+ // Columns from SMS database for backup/restore.
+ @VisibleForTesting
+ static final String[] SMS_PROJECTION = new String[] {
+ Telephony.Sms._ID,
+ Telephony.Sms.SUBSCRIPTION_ID,
+ Telephony.Sms.ADDRESS,
+ Telephony.Sms.BODY,
+ Telephony.Sms.SUBJECT,
+ Telephony.Sms.DATE,
+ Telephony.Sms.DATE_SENT,
+ Telephony.Sms.STATUS,
+ Telephony.Sms.TYPE,
+ Telephony.Sms.THREAD_ID
+ };
+
+ // Columns to fetch recepients of SMS.
+ private static final String[] SMS_RECIPIENTS_PROJECTION = {
+ Telephony.Threads._ID,
+ Telephony.Threads.RECIPIENT_IDS
+ };
+
+ // Columns from MMS database for backup/restore.
+ @VisibleForTesting
+ static final String[] MMS_PROJECTION = new String[] {
+ Telephony.Mms._ID,
+ Telephony.Mms.SUBSCRIPTION_ID,
+ Telephony.Mms.SUBJECT,
+ Telephony.Mms.SUBJECT_CHARSET,
+ Telephony.Mms.DATE,
+ Telephony.Mms.DATE_SENT,
+ Telephony.Mms.MESSAGE_TYPE,
+ Telephony.Mms.MMS_VERSION,
+ Telephony.Mms.MESSAGE_BOX,
+ Telephony.Mms.CONTENT_LOCATION,
+ Telephony.Mms.THREAD_ID,
+ Telephony.Mms.TRANSACTION_ID
+ };
+
+ // Columns from addr database for backup/restore. This database is used for fetching addresses
+ // for MMS message.
+ @VisibleForTesting
+ static final String[] MMS_ADDR_PROJECTION = new String[] {
+ Telephony.Mms.Addr.TYPE,
+ Telephony.Mms.Addr.ADDRESS,
+ Telephony.Mms.Addr.CHARSET
+ };
+
+ // Columns from part database for backup/restore. This database is used for fetching body text
+ // and charset for MMS message.
+ @VisibleForTesting
+ static final String[] MMS_TEXT_PROJECTION = new String[] {
+ Telephony.Mms.Part.TEXT,
+ Telephony.Mms.Part.CHARSET
+ };
+ static final int MMS_TEXT_IDX = 0;
+ static final int MMS_TEXT_CHARSET_IDX = 1;
+
+ // Buffer size for Json writer.
+ public static final int WRITER_BUFFER_SIZE = 32*1024; //32Kb
+
+ // We increase how many bytes backup size over quota by 10%, so we will fit into quota on next
+ // backup
+ public static final double BYTES_OVER_QUOTA_MULTIPLIER = 1.1;
+
+ // Maximum messages for one backup file. After reaching the limit the agent backs up the file,
+ // deletes it and creates a new one with the same name.
+ // Not final for the testing.
+ @VisibleForTesting
+ int mMaxMsgPerFile = 1000;
+
+ // Default values for SMS, MMS, Addresses restore.
+ private static ContentValues sDefaultValuesSms = new ContentValues(5);
+ private static ContentValues sDefaultValuesMms = new ContentValues(6);
+ private static final ContentValues sDefaultValuesAddr = new ContentValues(2);
+
+ // Shared preferences for the backup agent.
+ private static final String BACKUP_PREFS = "backup_shared_prefs";
+ // Key for storing quota bytes.
+ private static final String QUOTA_BYTES = "backup_quota_bytes";
+ // Key for storing backup data size.
+ private static final String BACKUP_DATA_BYTES = "backup_data_bytes";
+ // Key for storing timestamp when backup agent resets quota. It does that to get onQuotaExceeded
+ // call so it could get the new quota if it changed.
+ private static final String QUOTA_RESET_TIME = "reset_quota_time";
+ private static final long QUOTA_RESET_INTERVAL = 30 * AlarmManager.INTERVAL_DAY; // 30 days.
+
+
+ static {
+ // Consider restored messages read and seen.
+ sDefaultValuesSms.put(Telephony.Sms.READ, 1);
+ sDefaultValuesSms.put(Telephony.Sms.SEEN, 1);
+ sDefaultValuesSms.put(Telephony.Sms.ADDRESS, UNKNOWN_SENDER);
+ // If there is no sub_id with self phone number on restore set it to -1.
+ sDefaultValuesSms.put(Telephony.Sms.SUBSCRIPTION_ID, -1);
+
+ sDefaultValuesMms.put(Telephony.Mms.READ, 1);
+ sDefaultValuesMms.put(Telephony.Mms.SEEN, 1);
+ sDefaultValuesMms.put(Telephony.Mms.SUBSCRIPTION_ID, -1);
+ sDefaultValuesMms.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_ALL);
+ sDefaultValuesMms.put(Telephony.Mms.TEXT_ONLY, 1);
+
+ sDefaultValuesAddr.put(Telephony.Mms.Addr.TYPE, 0);
+ sDefaultValuesAddr.put(Telephony.Mms.Addr.CHARSET, CharacterSets.DEFAULT_CHARSET);
+ }
+
+
+ private SparseArray<String> mSubId2phone = new SparseArray<String>();
+ private Map<String, Integer> mPhone2subId = new ArrayMap<String, Integer>();
+ private Map<Long, Boolean> mThreadArchived = new HashMap<>();
+
+ private ContentResolver mContentResolver;
+ // How many bytes we can backup to fit into quota.
+ private long mBytesOverQuota;
+
+ // Cache list of recipients by threadId. It reduces db requests heavily. Used during backup.
+ @VisibleForTesting
+ Map<Long, List<String>> mCacheRecipientsByThread = null;
+ // Cache threadId by list of recipients. Used during restore.
+ @VisibleForTesting
+ Map<Set<String>, Long> mCacheGetOrCreateThreadId = null;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ final SubscriptionManager subscriptionManager = SubscriptionManager.from(this);
+ if (subscriptionManager != null) {
+ final List<SubscriptionInfo> subInfo =
+ subscriptionManager.getActiveSubscriptionInfoList();
+ if (subInfo != null) {
+ for (SubscriptionInfo sub : subInfo) {
+ final String phoneNumber = getNormalizedNumber(sub);
+ mSubId2phone.append(sub.getSubscriptionId(), phoneNumber);
+ mPhone2subId.put(phoneNumber, sub.getSubscriptionId());
+ }
+ }
+ }
+ mContentResolver = getContentResolver();
+ initUnknownSender();
+ }
+
+ @VisibleForTesting
+ void setContentResolver(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+ @VisibleForTesting
+ void setSubId(SparseArray<String> subId2Phone, Map<String, Integer> phone2subId) {
+ mSubId2phone = subId2Phone;
+ mPhone2subId = phone2subId;
+ }
+
+ @VisibleForTesting
+ void initUnknownSender() {
+ mUnknownSenderThreadId = getOrCreateThreadId(null);
+ sDefaultValuesSms.put(Telephony.Sms.THREAD_ID, mUnknownSenderThreadId);
+ sDefaultValuesMms.put(Telephony.Mms.THREAD_ID, mUnknownSenderThreadId);
+ }
+
+ @Override
+ public void onFullBackup(FullBackupDataOutput data) throws IOException {
+ SharedPreferences sharedPreferences = getSharedPreferences(BACKUP_PREFS, MODE_PRIVATE);
+ if (sharedPreferences.getLong(QUOTA_RESET_TIME, Long.MAX_VALUE) <
+ System.currentTimeMillis()) {
+ clearSharedPreferences();
+ }
+
+ mBytesOverQuota = sharedPreferences.getLong(BACKUP_DATA_BYTES, 0) -
+ sharedPreferences.getLong(QUOTA_BYTES, Long.MAX_VALUE);
+ if (mBytesOverQuota > 0) {
+ mBytesOverQuota *= BYTES_OVER_QUOTA_MULTIPLIER;
+ }
+
+ try (
+ Cursor smsCursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, SMS_PROJECTION,
+ null, null, ORDER_BY_DATE);
+ // Do not backup non text-only MMS's.
+ Cursor mmsCursor = mContentResolver.query(Telephony.Mms.CONTENT_URI, MMS_PROJECTION,
+ Telephony.Mms.TEXT_ONLY+"=1", null, ORDER_BY_DATE)) {
+
+ if (smsCursor != null) {
+ smsCursor.moveToFirst();
+ }
+ if (mmsCursor != null) {
+ mmsCursor.moveToFirst();
+ }
+
+ // It backs up messages from the oldest to newest. First it looks at the timestamp of
+ // the next SMS messages and MMS message. If the SMS is older it backs up 1000 SMS
+ // messages, otherwise 1000 MMS messages. Repeat until out of SMS's or MMS's.
+ // It ensures backups are incremental.
+ int fileNum = 0;
+ while (smsCursor != null && !smsCursor.isAfterLast() &&
+ mmsCursor != null && !mmsCursor.isAfterLast()) {
+ final long smsDate = TimeUnit.MILLISECONDS.toSeconds(getMessageDate(smsCursor));
+ final long mmsDate = getMessageDate(mmsCursor);
+ if (smsDate < mmsDate) {
+ backupAll(data, smsCursor,
+ String.format(Locale.US, SMS_BACKUP_FILE_FORMAT, fileNum++));
+ } else {
+ backupAll(data, mmsCursor, String.format(Locale.US,
+ MMS_BACKUP_FILE_FORMAT, fileNum++));
+ }
+ }
+
+ while (smsCursor != null && !smsCursor.isAfterLast()) {
+ backupAll(data, smsCursor,
+ String.format(Locale.US, SMS_BACKUP_FILE_FORMAT, fileNum++));
+ }
+
+ while (mmsCursor != null && !mmsCursor.isAfterLast()) {
+ backupAll(data, mmsCursor,
+ String.format(Locale.US, MMS_BACKUP_FILE_FORMAT, fileNum++));
+ }
+ }
+
+ mThreadArchived = new HashMap<>();
+ }
+
+ @VisibleForTesting
+ void clearSharedPreferences() {
+ getSharedPreferences(BACKUP_PREFS, MODE_PRIVATE).edit()
+ .remove(BACKUP_DATA_BYTES)
+ .remove(QUOTA_BYTES)
+ .remove(QUOTA_RESET_TIME)
+ .apply();
+ }
+
+ private static long getMessageDate(Cursor cursor) {
+ return cursor.getLong(cursor.getColumnIndex(Telephony.Sms.DATE));
+ }
+
+ @Override
+ public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ SharedPreferences sharedPreferences = getSharedPreferences(BACKUP_PREFS, MODE_PRIVATE);
+ if (sharedPreferences.contains(BACKUP_DATA_BYTES)
+ && sharedPreferences.contains(QUOTA_BYTES)) {
+ // Increase backup size by the size we skipped during previous backup.
+ backupDataBytes += (sharedPreferences.getLong(BACKUP_DATA_BYTES, 0)
+ - sharedPreferences.getLong(QUOTA_BYTES, 0)) * BYTES_OVER_QUOTA_MULTIPLIER;
+ }
+ sharedPreferences.edit()
+ .putLong(BACKUP_DATA_BYTES, backupDataBytes)
+ .putLong(QUOTA_BYTES, quotaBytes)
+ .putLong(QUOTA_RESET_TIME, System.currentTimeMillis() + QUOTA_RESET_INTERVAL)
+ .apply();
+ }
+
+ private void backupAll(FullBackupDataOutput data, Cursor cursor, String fileName)
+ throws IOException {
+ if (cursor == null || cursor.isAfterLast()) {
+ return;
+ }
+
+ int messagesWritten = 0;
+ try (JsonWriter jsonWriter = getJsonWriter(fileName)) {
+ if (fileName.endsWith(SMS_BACKUP_FILE_SUFFIX)) {
+ messagesWritten = putSmsMessagesToJson(cursor, jsonWriter);
+ } else {
+ messagesWritten = putMmsMessagesToJson(cursor, jsonWriter);
+ }
+ }
+ backupFile(messagesWritten, fileName, data);
+ }
+
+ @VisibleForTesting
+ int putMmsMessagesToJson(Cursor cursor,
+ JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginArray();
+ int msgCount;
+ for (msgCount = 0; msgCount < mMaxMsgPerFile && !cursor.isAfterLast();
+ cursor.moveToNext()) {
+ msgCount += writeMmsToWriter(jsonWriter, cursor);
+ }
+ jsonWriter.endArray();
+ return msgCount;
+ }
+
+ @VisibleForTesting
+ int putSmsMessagesToJson(Cursor cursor, JsonWriter jsonWriter) throws IOException {
+
+ jsonWriter.beginArray();
+ int msgCount;
+ for (msgCount = 0; msgCount < mMaxMsgPerFile && !cursor.isAfterLast();
+ ++msgCount, cursor.moveToNext()) {
+ writeSmsToWriter(jsonWriter, cursor);
+ }
+ jsonWriter.endArray();
+ return msgCount;
+ }
+
+ private void backupFile(int messagesWritten, String fileName, FullBackupDataOutput data)
+ throws IOException {
+ final File file = new File(getFilesDir().getPath() + "/" + fileName);
+ try {
+ if (messagesWritten > 0) {
+ if (mBytesOverQuota > 0) {
+ mBytesOverQuota -= file.length();
+ return;
+ }
+ super.fullBackupFile(file, data);
+ }
+ } finally {
+ file.delete();
+ }
+ }
+
+ public static class DeferredSmsMmsRestoreService extends IntentService {
+ private static final String TAG = "DeferredSmsMmsRestoreService";
+
+ private final Comparator<File> mFileComparator = new Comparator<File>() {
+ @Override
+ public int compare(File lhs, File rhs) {
+ return rhs.getName().compareTo(lhs.getName());
+ }
+ };
+
+ public DeferredSmsMmsRestoreService() {
+ super(TAG);
+ setIntentRedelivery(true);
+ }
+
+ private TelephonyBackupAgent mTelephonyBackupAgent;
+ private PowerManager.WakeLock mWakeLock;
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ try {
+ mWakeLock.acquire();
+ File[] files = getFilesToRestore(this);
+
+ if (files == null || files.length == 0) {
+ return;
+ }
+ Arrays.sort(files, mFileComparator);
+
+ for (File file : files) {
+ final String fileName = file.getName();
+ try (FileInputStream fileInputStream = new FileInputStream(file)) {
+ mTelephonyBackupAgent.doRestoreFile(fileName, fileInputStream.getFD());
+ } catch (Exception e) {
+ // Either IOException or RuntimeException.
+ Log.e(TAG, e.toString());
+ } finally {
+ file.delete();
+ }
+ }
+ } finally {
+ mWakeLock.release();
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mTelephonyBackupAgent = new TelephonyBackupAgent();
+ mTelephonyBackupAgent.attach(this);
+ mTelephonyBackupAgent.onCreate();
+
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mTelephonyBackupAgent != null) {
+ mTelephonyBackupAgent.onDestroy();
+ mTelephonyBackupAgent = null;
+ }
+ super.onDestroy();
+ }
+
+ static void startIfFilesExist(Context context) {
+ File[] files = getFilesToRestore(context);
+ if (files == null || files.length == 0) {
+ return;
+ }
+ context.startService(new Intent(context, DeferredSmsMmsRestoreService.class));
+ }
+
+ private static File[] getFilesToRestore(Context context) {
+ return context.getFilesDir().listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ return file.getName().endsWith(SMS_BACKUP_FILE_SUFFIX) ||
+ file.getName().endsWith(MMS_BACKUP_FILE_SUFFIX);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onRestoreFinished() {
+ super.onRestoreFinished();
+ DeferredSmsMmsRestoreService.startIfFilesExist(this);
+ }
+
+ private void doRestoreFile(String fileName, FileDescriptor fd) throws IOException {
+ if (DEBUG) {
+ Log.i(TAG, "Restoring file " + fileName);
+ }
+
+ try (JsonReader jsonReader = getJsonReader(fd)) {
+ if (fileName.endsWith(SMS_BACKUP_FILE_SUFFIX)) {
+ if (DEBUG) {
+ Log.i(TAG, "Restoring SMS");
+ }
+ putSmsMessagesToProvider(jsonReader);
+ } else if (fileName.endsWith(MMS_BACKUP_FILE_SUFFIX)) {
+ if (DEBUG) {
+ Log.i(TAG, "Restoring text MMS");
+ }
+ putMmsMessagesToProvider(jsonReader);
+ } else {
+ if (DEBUG) {
+ Log.e(TAG, "Unknown file to restore:" + fileName);
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void putSmsMessagesToProvider(JsonReader jsonReader) throws IOException {
+ jsonReader.beginArray();
+ int msgCount = 0;
+ final int bulkInsertSize = mMaxMsgPerFile;
+ ContentValues[] values = new ContentValues[bulkInsertSize];
+ while (jsonReader.hasNext()) {
+ ContentValues cv = readSmsValuesFromReader(jsonReader);
+ if (doesSmsExist(cv)) {
+ continue;
+ }
+ values[(msgCount++) % bulkInsertSize] = cv;
+ if (msgCount % bulkInsertSize == 0) {
+ mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI, values);
+ }
+ }
+ if (msgCount % bulkInsertSize > 0) {
+ mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI,
+ Arrays.copyOf(values, msgCount % bulkInsertSize));
+ }
+ jsonReader.endArray();
+ }
+
+ @VisibleForTesting
+ void putMmsMessagesToProvider(JsonReader jsonReader) throws IOException {
+ jsonReader.beginArray();
+ while (jsonReader.hasNext()) {
+ final Mms mms = readMmsFromReader(jsonReader);
+ if (doesMmsExist(mms)) {
+ if (DEBUG) {
+ Log.e(TAG, String.format("Mms: %s already exists", mms.toString()));
+ }
+ continue;
+ }
+ addMmsMessage(mms);
+ }
+ }
+
+ @VisibleForTesting
+ static final String[] PROJECTION_ID = {BaseColumns._ID};
+ private static final int ID_IDX = 0;
+
+ private boolean doesSmsExist(ContentValues smsValues) {
+ final String where = String.format(Locale.US, "%s = %d and %s = %s",
+ Telephony.Sms.DATE, smsValues.getAsLong(Telephony.Sms.DATE),
+ Telephony.Sms.BODY,
+ DatabaseUtils.sqlEscapeString(smsValues.getAsString(Telephony.Sms.BODY)));
+ try (Cursor cursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, PROJECTION_ID, where,
+ null, null)) {
+ return cursor != null && cursor.getCount() > 0;
+ }
+ }
+
+ private boolean doesMmsExist(Mms mms) {
+ final String where = String.format(Locale.US, "%s = %d",
+ Telephony.Sms.DATE, mms.values.getAsLong(Telephony.Mms.DATE));
+ try (Cursor cursor = mContentResolver.query(Telephony.Mms.CONTENT_URI, PROJECTION_ID, where,
+ null, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ final int mmsId = cursor.getInt(ID_IDX);
+ final MmsBody body = getMmsBody(mmsId);
+ if (body != null && body.equals(mms.body)) {
+ return true;
+ }
+ } while (cursor.moveToNext());
+ }
+ }
+ return false;
+ }
+
+ private static String getNormalizedNumber(SubscriptionInfo subscriptionInfo) {
+ if (subscriptionInfo == null) {
+ return null;
+ }
+ return PhoneNumberUtils.formatNumberToE164(subscriptionInfo.getNumber(),
+ subscriptionInfo.getCountryIso().toUpperCase(Locale.US));
+ }
+
+ private void writeSmsToWriter(JsonWriter jsonWriter, Cursor cursor) throws IOException {
+ jsonWriter.beginObject();
+
+ for (int i=0; i<cursor.getColumnCount(); ++i) {
+ final String name = cursor.getColumnName(i);
+ final String value = cursor.getString(i);
+ if (value == null) {
+ continue;
+ }
+ switch (name) {
+ case Telephony.Sms.SUBSCRIPTION_ID:
+ final int subId = cursor.getInt(i);
+ final String selfNumber = mSubId2phone.get(subId);
+ if (selfNumber != null) {
+ jsonWriter.name(SELF_PHONE_KEY).value(selfNumber);
+ }
+ break;
+ case Telephony.Sms.THREAD_ID:
+ final long threadId = cursor.getLong(i);
+ handleThreadId(jsonWriter, threadId);
+ break;
+ case Telephony.Sms._ID:
+ break;
+ default:
+ jsonWriter.name(name).value(value);
+ break;
+ }
+ }
+ jsonWriter.endObject();
+
+ }
+
+ private void handleThreadId(JsonWriter jsonWriter, long threadId) throws IOException {
+ final List<String> recipients = getRecipientsByThread(threadId);
+ if (recipients == null || recipients.isEmpty()) {
+ return;
+ }
+
+ writeRecipientsToWriter(jsonWriter.name(RECIPIENTS), recipients);
+ if (!mThreadArchived.containsKey(threadId)) {
+ boolean isArchived = isThreadArchived(threadId);
+ if (isArchived) {
+ jsonWriter.name(Telephony.Threads.ARCHIVED).value(true);
+ }
+ mThreadArchived.put(threadId, isArchived);
+ }
+ }
+
+ private static String[] THREAD_ARCHIVED_PROJECTION =
+ new String[] { Telephony.Threads.ARCHIVED };
+ private static int THREAD_ARCHIVED_IDX = 0;
+
+ private boolean isThreadArchived(long threadId) {
+ Uri.Builder builder = Telephony.Threads.CONTENT_URI.buildUpon();
+ builder.appendPath(String.valueOf(threadId)).appendPath("recipients");
+ Uri uri = builder.build();
+
+ try (Cursor cursor = getContentResolver().query(uri, THREAD_ARCHIVED_PROJECTION, null, null,
+ null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getInt(THREAD_ARCHIVED_IDX) == 1;
+ }
+ }
+ return false;
+ }
+
+ private static void writeRecipientsToWriter(JsonWriter jsonWriter, List<String> recipients)
+ throws IOException {
+ jsonWriter.beginArray();
+ if (recipients != null) {
+ for (String s : recipients) {
+ jsonWriter.value(s);
+ }
+ }
+ jsonWriter.endArray();
+ }
+
+ private ContentValues readSmsValuesFromReader(JsonReader jsonReader)
+ throws IOException {
+ ContentValues values = new ContentValues(6+sDefaultValuesSms.size());
+ values.putAll(sDefaultValuesSms);
+ long threadId = -1;
+ boolean isArchived = false;
+ jsonReader.beginObject();
+ while (jsonReader.hasNext()) {
+ String name = jsonReader.nextName();
+ switch (name) {
+ case Telephony.Sms.BODY:
+ case Telephony.Sms.DATE:
+ case Telephony.Sms.DATE_SENT:
+ case Telephony.Sms.STATUS:
+ case Telephony.Sms.TYPE:
+ case Telephony.Sms.SUBJECT:
+ case Telephony.Sms.ADDRESS:
+ values.put(name, jsonReader.nextString());
+ break;
+ case RECIPIENTS:
+ threadId = getOrCreateThreadId(getRecipients(jsonReader));
+ values.put(Telephony.Sms.THREAD_ID, threadId);
+ break;
+ case Telephony.Threads.ARCHIVED:
+ isArchived = jsonReader.nextBoolean();
+ break;
+ case SELF_PHONE_KEY:
+ final String selfPhone = jsonReader.nextString();
+ if (mPhone2subId.containsKey(selfPhone)) {
+ values.put(Telephony.Sms.SUBSCRIPTION_ID, mPhone2subId.get(selfPhone));
+ }
+ break;
+ default:
+ if (DEBUG) {
+ Log.w(TAG, "Unknown name:" + name);
+ }
+ jsonReader.skipValue();
+ break;
+ }
+ }
+ jsonReader.endObject();
+ archiveThread(threadId, isArchived);
+ return values;
+ }
+
+ private static Set<String> getRecipients(JsonReader jsonReader) throws IOException {
+ Set<String> recipients = new ArraySet<String>();
+ jsonReader.beginArray();
+ while (jsonReader.hasNext()) {
+ recipients.add(jsonReader.nextString());
+ }
+ jsonReader.endArray();
+ return recipients;
+ }
+
+ private int writeMmsToWriter(JsonWriter jsonWriter, Cursor cursor) throws IOException {
+ final int mmsId = cursor.getInt(ID_IDX);
+ final MmsBody body = getMmsBody(mmsId);
+ if (body == null || body.text == null) {
+ return 0;
+ }
+
+ boolean subjectNull = true;
+ jsonWriter.beginObject();
+ for (int i=0; i<cursor.getColumnCount(); ++i) {
+ final String name = cursor.getColumnName(i);
+ final String value = cursor.getString(i);
+ if (value == null) {
+ continue;
+ }
+ switch (name) {
+ case Telephony.Mms.SUBSCRIPTION_ID:
+ final int subId = cursor.getInt(i);
+ final String selfNumber = mSubId2phone.get(subId);
+ if (selfNumber != null) {
+ jsonWriter.name(SELF_PHONE_KEY).value(selfNumber);
+ }
+ break;
+ case Telephony.Mms.THREAD_ID:
+ final long threadId = cursor.getLong(i);
+ handleThreadId(jsonWriter, threadId);
+ break;
+ case Telephony.Mms._ID:
+ case Telephony.Mms.SUBJECT_CHARSET:
+ break;
+ case Telephony.Mms.SUBJECT:
+ subjectNull = false;
+ default:
+ jsonWriter.name(name).value(value);
+ break;
+ }
+ }
+ // Addresses.
+ writeMmsAddresses(jsonWriter.name(MMS_ADDRESSES_KEY), mmsId);
+ // Body (text of the message).
+ jsonWriter.name(MMS_BODY_KEY).value(body.text);
+ // Charset of the body text.
+ jsonWriter.name(MMS_BODY_CHARSET_KEY).value(body.charSet);
+
+ if (!subjectNull) {
+ // Subject charset.
+ writeStringToWriter(jsonWriter, cursor, Telephony.Mms.SUBJECT_CHARSET);
+ }
+ jsonWriter.endObject();
+ return 1;
+ }
+
+ private Mms readMmsFromReader(JsonReader jsonReader) throws IOException {
+ Mms mms = new Mms();
+ mms.values = new ContentValues(5+sDefaultValuesMms.size());
+ mms.values.putAll(sDefaultValuesMms);
+ jsonReader.beginObject();
+ String bodyText = null;
+ long threadId = -1;
+ boolean isArchived = false;
+ int bodyCharset = CharacterSets.DEFAULT_CHARSET;
+ while (jsonReader.hasNext()) {
+ String name = jsonReader.nextName();
+ switch (name) {
+ case SELF_PHONE_KEY:
+ final String selfPhone = jsonReader.nextString();
+ if (mPhone2subId.containsKey(selfPhone)) {
+ mms.values.put(Telephony.Mms.SUBSCRIPTION_ID, mPhone2subId.get(selfPhone));
+ }
+ break;
+ case MMS_ADDRESSES_KEY:
+ getMmsAddressesFromReader(jsonReader, mms);
+ break;
+ case MMS_BODY_KEY:
+ bodyText = jsonReader.nextString();
+ break;
+ case MMS_BODY_CHARSET_KEY:
+ bodyCharset = jsonReader.nextInt();
+ break;
+ case RECIPIENTS:
+ threadId = getOrCreateThreadId(getRecipients(jsonReader));
+ mms.values.put(Telephony.Sms.THREAD_ID, threadId);
+ break;
+ case Telephony.Threads.ARCHIVED:
+ isArchived = jsonReader.nextBoolean();
+ break;
+ case Telephony.Mms.SUBJECT:
+ case Telephony.Mms.SUBJECT_CHARSET:
+ case Telephony.Mms.DATE:
+ case Telephony.Mms.DATE_SENT:
+ case Telephony.Mms.MESSAGE_TYPE:
+ case Telephony.Mms.MMS_VERSION:
+ case Telephony.Mms.MESSAGE_BOX:
+ case Telephony.Mms.CONTENT_LOCATION:
+ case Telephony.Mms.TRANSACTION_ID:
+ mms.values.put(name, jsonReader.nextString());
+ break;
+ default:
+ if (DEBUG) {
+ Log.w(TAG, "Unknown name:" + name);
+ }
+ jsonReader.skipValue();
+ break;
+ }
+ }
+ jsonReader.endObject();
+
+ if (bodyText != null) {
+ mms.body = new MmsBody(bodyText, bodyCharset);
+ }
+
+ // Set default charset for subject.
+ if (mms.values.get(Telephony.Mms.SUBJECT) != null &&
+ mms.values.get(Telephony.Mms.SUBJECT_CHARSET) == null) {
+ mms.values.put(Telephony.Mms.SUBJECT_CHARSET, CharacterSets.DEFAULT_CHARSET);
+ }
+
+ archiveThread(threadId, isArchived);
+
+ return mms;
+ }
+
+ private static final String ARCHIVE_THREAD_SELECTION = Telephony.Threads._ID + "=?";
+
+ private void archiveThread(long threadId, boolean isArchived) {
+ if (threadId < 0 || !isArchived) {
+ return;
+ }
+ final ContentValues values = new ContentValues(1);
+ values.put(Telephony.Threads.ARCHIVED, 1);
+ if (mContentResolver.update(
+ Telephony.Threads.CONTENT_URI,
+ values,
+ ARCHIVE_THREAD_SELECTION,
+ new String[] { Long.toString(threadId)}) != 1) {
+ if (DEBUG) {
+ Log.e(TAG, "archiveThread: failed to update database");
+ }
+ }
+ }
+
+ private MmsBody getMmsBody(int mmsId) {
+ Uri MMS_PART_CONTENT_URI = Telephony.Mms.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(mmsId)).appendPath("part").build();
+
+ String body = null;
+ int charSet = 0;
+
+ try (Cursor cursor = mContentResolver.query(MMS_PART_CONTENT_URI, MMS_TEXT_PROJECTION,
+ Telephony.Mms.Part.CONTENT_TYPE + "=?", new String[]{ContentType.TEXT_PLAIN},
+ ORDER_BY_ID)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ body = (body == null ? cursor.getString(MMS_TEXT_IDX)
+ : body.concat(cursor.getString(MMS_TEXT_IDX)));
+ charSet = cursor.getInt(MMS_TEXT_CHARSET_IDX);
+ } while (cursor.moveToNext());
+ }
+ }
+ return (body == null ? null : new MmsBody(body, charSet));
+ }
+
+ private void writeMmsAddresses(JsonWriter jsonWriter, int mmsId) throws IOException {
+ Uri.Builder builder = Telephony.Mms.CONTENT_URI.buildUpon();
+ builder.appendPath(String.valueOf(mmsId)).appendPath("addr");
+ Uri uriAddrPart = builder.build();
+
+ jsonWriter.beginArray();
+ try (Cursor cursor = mContentResolver.query(uriAddrPart, MMS_ADDR_PROJECTION,
+ null/*selection*/, null/*selectionArgs*/, ORDER_BY_ID)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ if (cursor.getString(cursor.getColumnIndex(Telephony.Mms.Addr.ADDRESS))
+ != null) {
+ jsonWriter.beginObject();
+ writeIntToWriter(jsonWriter, cursor, Telephony.Mms.Addr.TYPE);
+ writeStringToWriter(jsonWriter, cursor, Telephony.Mms.Addr.ADDRESS);
+ writeIntToWriter(jsonWriter, cursor, Telephony.Mms.Addr.CHARSET);
+ jsonWriter.endObject();
+ }
+ } while (cursor.moveToNext());
+ }
+ }
+ jsonWriter.endArray();
+ }
+
+ private static void getMmsAddressesFromReader(JsonReader jsonReader, Mms mms)
+ throws IOException {
+ mms.addresses = new ArrayList<ContentValues>();
+ jsonReader.beginArray();
+ while (jsonReader.hasNext()) {
+ jsonReader.beginObject();
+ ContentValues addrValues = new ContentValues(sDefaultValuesAddr);
+ while (jsonReader.hasNext()) {
+ final String name = jsonReader.nextName();
+ switch (name) {
+ case Telephony.Mms.Addr.TYPE:
+ case Telephony.Mms.Addr.CHARSET:
+ addrValues.put(name, jsonReader.nextInt());
+ break;
+ case Telephony.Mms.Addr.ADDRESS:
+ addrValues.put(name, jsonReader.nextString());
+ break;
+ default:
+ if (DEBUG) {
+ Log.w(TAG, "Unknown name:" + name);
+ }
+ jsonReader.skipValue();
+ break;
+ }
+ }
+ jsonReader.endObject();
+ if (addrValues.containsKey(Telephony.Mms.Addr.ADDRESS)) {
+ mms.addresses.add(addrValues);
+ }
+ }
+ jsonReader.endArray();
+ }
+
+ private void addMmsMessage(Mms mms) {
+ if (DEBUG) {
+ Log.e(TAG, "Add mms:\n" + mms.toString());
+ }
+ final long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
+ final Uri partUri = Telephony.Mms.CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(dummyId)).appendPath("part").build();
+
+ final String srcName = String.format(Locale.US, "text.%06d.txt", 0);
+ { // Insert SMIL part.
+ final String smilBody = String.format(sSmilTextPart, srcName);
+ final String smil = String.format(sSmilTextOnly, smilBody);
+ final ContentValues values = new ContentValues(7);
+ values.put(Telephony.Mms.Part.MSG_ID, dummyId);
+ values.put(Telephony.Mms.Part.SEQ, -1);
+ values.put(Telephony.Mms.Part.CONTENT_TYPE, ContentType.APP_SMIL);
+ values.put(Telephony.Mms.Part.NAME, "smil.xml");
+ values.put(Telephony.Mms.Part.CONTENT_ID, "<smil>");
+ values.put(Telephony.Mms.Part.CONTENT_LOCATION, "smil.xml");
+ values.put(Telephony.Mms.Part.TEXT, smil);
+ if (mContentResolver.insert(partUri, values) == null) {
+ if (DEBUG) {
+ Log.e(TAG, "Could not insert SMIL part");
+ }
+ return;
+ }
+ }
+
+ { // Insert body part.
+ final ContentValues values = new ContentValues(8);
+ values.put(Telephony.Mms.Part.MSG_ID, dummyId);
+ values.put(Telephony.Mms.Part.SEQ, 0);
+ values.put(Telephony.Mms.Part.CONTENT_TYPE, ContentType.TEXT_PLAIN);
+ values.put(Telephony.Mms.Part.NAME, srcName);
+ values.put(Telephony.Mms.Part.CONTENT_ID, "<"+srcName+">");
+ values.put(Telephony.Mms.Part.CONTENT_LOCATION, srcName);
+ values.put(Telephony.Mms.Part.CHARSET, mms.body.charSet);
+ values.put(Telephony.Mms.Part.TEXT, mms.body.text);
+ if (mContentResolver.insert(partUri, values) == null) {
+ if (DEBUG) {
+ Log.e(TAG, "Could not insert body part");
+ }
+ return;
+ }
+ }
+
+ // Insert mms.
+ final Uri mmsUri = mContentResolver.insert(Telephony.Mms.CONTENT_URI, mms.values);
+ if (mmsUri == null) {
+ if (DEBUG) {
+ Log.e(TAG, "Could not insert mms");
+ }
+ return;
+ }
+
+ final long mmsId = ContentUris.parseId(mmsUri);
+ { // Update parts with the right mms id.
+ ContentValues values = new ContentValues(1);
+ values.put(Telephony.Mms.Part.MSG_ID, mmsId);
+ mContentResolver.update(partUri, values, null, null);
+ }
+
+ { // Insert adderesses into "addr".
+ final Uri addrUri = Uri.withAppendedPath(mmsUri, "addr");
+ for (ContentValues mmsAddress : mms.addresses) {
+ ContentValues values = new ContentValues(mmsAddress);
+ values.put(Telephony.Mms.Addr.MSG_ID, mmsId);
+ mContentResolver.insert(addrUri, values);
+ }
+ }
+ }
+
+ private static final class MmsBody {
+ public String text;
+ public int charSet;
+
+ public MmsBody(String text, int charSet) {
+ this.text = text;
+ this.charSet = charSet;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof MmsBody)) {
+ return false;
+ }
+ MmsBody typedObj = (MmsBody) obj;
+ return this.text.equals(typedObj.text) && this.charSet == typedObj.charSet;
+ }
+
+ @Override
+ public String toString() {
+ return "Text:" + text + " charSet:" + charSet;
+ }
+ }
+
+ private static final class Mms {
+ public ContentValues values;
+ public List<ContentValues> addresses;
+ public MmsBody body;
+ @Override
+ public String toString() {
+ return "Values:" + values.toString() + "\nRecipients:"+addresses.toString()
+ + "\nBody:" + body;
+ }
+ }
+
+ private JsonWriter getJsonWriter(final String fileName) throws IOException {
+ return new JsonWriter(new BufferedWriter(new OutputStreamWriter(new DeflaterOutputStream(
+ openFileOutput(fileName, MODE_PRIVATE)), CHARSET_UTF8), WRITER_BUFFER_SIZE));
+ }
+
+ private static JsonReader getJsonReader(final FileDescriptor fileDescriptor)
+ throws IOException {
+ return new JsonReader(new InputStreamReader(new InflaterInputStream(
+ new FileInputStream(fileDescriptor)), CHARSET_UTF8));
+ }
+
+ private static void writeStringToWriter(JsonWriter jsonWriter, Cursor cursor, String name)
+ throws IOException {
+ final String value = cursor.getString(cursor.getColumnIndex(name));
+ if (value != null) {
+ jsonWriter.name(name).value(value);
+ }
+ }
+
+ private static void writeIntToWriter(JsonWriter jsonWriter, Cursor cursor, String name)
+ throws IOException {
+ final int value = cursor.getInt(cursor.getColumnIndex(name));
+ if (value != 0) {
+ jsonWriter.name(name).value(value);
+ }
+ }
+
+ private long getOrCreateThreadId(Set<String> recipients) {
+ if (recipients == null) {
+ recipients = new ArraySet<String>();
+ }
+
+ if (recipients.isEmpty()) {
+ recipients.add(UNKNOWN_SENDER);
+ }
+
+ if (mCacheGetOrCreateThreadId == null) {
+ mCacheGetOrCreateThreadId = new HashMap<>();
+ }
+
+ if (!mCacheGetOrCreateThreadId.containsKey(recipients)) {
+ long threadId = mUnknownSenderThreadId;
+ try {
+ threadId = Telephony.Threads.getOrCreateThreadId(this, recipients);
+ } catch (RuntimeException e) {
+ if (DEBUG) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ mCacheGetOrCreateThreadId.put(recipients, threadId);
+ return threadId;
+ }
+
+ return mCacheGetOrCreateThreadId.get(recipients);
+ }
+
+ @VisibleForTesting
+ static final Uri THREAD_ID_CONTENT_URI = Uri.parse("content://mms-sms/threadID");
+
+ // Mostly copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
+ private List<String> getRecipientsByThread(final long threadId) {
+ if (mCacheRecipientsByThread == null) {
+ mCacheRecipientsByThread = new HashMap<>();
+ }
+
+ if (!mCacheRecipientsByThread.containsKey(threadId)) {
+ final String spaceSepIds = getRawRecipientIdsForThread(threadId);
+ if (!TextUtils.isEmpty(spaceSepIds)) {
+ mCacheRecipientsByThread.put(threadId, getAddresses(spaceSepIds));
+ } else {
+ mCacheRecipientsByThread.put(threadId, new ArrayList<String>());
+ }
+ }
+
+ return mCacheRecipientsByThread.get(threadId);
+ }
+
+ @VisibleForTesting
+ static final Uri ALL_THREADS_URI =
+ Telephony.Threads.CONTENT_URI.buildUpon().
+ appendQueryParameter("simple", "true").build();
+ private static final int RECIPIENT_IDS = 1;
+
+ // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
+ // NOTE: There are phones on which you can't get the recipients from the thread id for SMS
+ // until you have a message in the conversation!
+ private String getRawRecipientIdsForThread(final long threadId) {
+ if (threadId <= 0) {
+ return null;
+ }
+ final Cursor thread = mContentResolver.query(
+ ALL_THREADS_URI,
+ SMS_RECIPIENTS_PROJECTION, "_id=?", new String[]{String.valueOf(threadId)}, null);
+ if (thread != null) {
+ try {
+ if (thread.moveToFirst()) {
+ // recipientIds will be a space-separated list of ids into the
+ // canonical addresses table.
+ return thread.getString(RECIPIENT_IDS);
+ }
+ } finally {
+ thread.close();
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ static final Uri SINGLE_CANONICAL_ADDRESS_URI =
+ Uri.parse("content://mms-sms/canonical-address");
+
+ // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
+ private List<String> getAddresses(final String spaceSepIds) {
+ final List<String> numbers = new ArrayList<String>();
+ final String[] ids = spaceSepIds.split(" ");
+ for (final String id : ids) {
+ long longId;
+
+ try {
+ longId = Long.parseLong(id);
+ if (longId < 0) {
+ if (DEBUG) {
+ Log.e(TAG, "getAddresses: invalid id " + longId);
+ }
+ continue;
+ }
+ } catch (final NumberFormatException ex) {
+ if (DEBUG) {
+ Log.e(TAG, "getAddresses: invalid id. " + ex, ex);
+ }
+ // skip this id
+ continue;
+ }
+
+ // TODO: build a single query where we get all the addresses at once.
+ Cursor c = null;
+ try {
+ c = mContentResolver.query(
+ ContentUris.withAppendedId(SINGLE_CANONICAL_ADDRESS_URI, longId),
+ null, null, null, null);
+ } catch (final Exception e) {
+ if (DEBUG) {
+ Log.e(TAG, "getAddresses: query failed for id " + longId, e);
+ }
+ }
+ if (c != null) {
+ try {
+ if (c.moveToFirst()) {
+ final String number = c.getString(0);
+ if (!TextUtils.isEmpty(number)) {
+ numbers.add(number);
+ } else {
+ if (DEBUG) {
+ Log.w(TAG, "Canonical MMS/SMS address is empty for id: " + longId);
+ }
+ }
+ }
+ } finally {
+ c.close();
+ }
+ }
+ }
+ if (numbers.isEmpty()) {
+ if (DEBUG) {
+ Log.w(TAG, "No MMS addresses found from ids string [" + spaceSepIds + "]");
+ }
+ }
+ return numbers;
+ }
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException {
+ // Empty because is not used during full backup.
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode,
+ ParcelFileDescriptor newState) throws IOException {
+ // Empty because is not used during full restore.
+ }
+}
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index bf77aec..97e9b69 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -35,9 +35,9 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.provider.Telephony;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -55,19 +55,20 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
-import java.lang.NumberFormatException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import static android.provider.Telephony.Carriers.*;
+
public class TelephonyProvider extends ContentProvider
{
private static final String DATABASE_NAME = "telephony.db";
private static final boolean DBG = true;
private static final boolean VDBG = false; // STOPSHIP if true
- private static final int DATABASE_VERSION = 17 << 16;
+ private static final int DATABASE_VERSION = 18 << 16;
private static final int URL_UNKNOWN = 0;
private static final int URL_TELEPHONY = 1;
private static final int URL_CURRENT = 2;
@@ -89,7 +90,7 @@ public class TelephonyProvider extends ContentProvider
private static final String CARRIERS_TABLE_TMP = "carriers_tmp";
private static final String SIMINFO_TABLE = "siminfo";
- private static final String PREF_FILE = "preferred-apn";
+ private static final String PREF_FILE_APN = "preferred-apn";
private static final String COLUMN_APN_ID = "apn_id";
private static final String PREF_FILE_FULL_APN = "preferred-full-apn";
@@ -98,6 +99,9 @@ public class TelephonyProvider extends ContentProvider
private static final String BUILD_ID_FILE = "build-id";
private static final String RO_BUILD_ID = "ro_build_id";
+ private static final String PREF_FILE = "telephonyprovider";
+ private static final String APN_CONF_CHECKSUM = "apn_conf_checksum";
+
private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml";
private static final String OEM_APNS_PATH = "telephony/apns-conf.xml";
private static final String OTA_UPDATED_APNS_PATH = "misc/apns-conf.xml";
@@ -108,27 +112,44 @@ public class TelephonyProvider extends ContentProvider
private static final ContentValues s_currentNullMap;
private static final ContentValues s_currentSetMap;
+ private static final String IS_UNEDITED = EDITED + "=" + UNEDITED;
+ private static final String IS_EDITED = EDITED + "!=" + UNEDITED;
+ private static final String IS_USER_EDITED = EDITED + "=" + USER_EDITED;
+ private static final String IS_USER_DELETED = EDITED + "=" + USER_DELETED;
+ private static final String IS_NOT_USER_DELETED = EDITED + "!=" + USER_DELETED;
+ private static final String IS_USER_DELETED_BUT_PRESENT_IN_XML =
+ EDITED + "=" + USER_DELETED_BUT_PRESENT_IN_XML;
+ private static final String IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML =
+ EDITED + "!=" + USER_DELETED_BUT_PRESENT_IN_XML;
+ private static final String IS_CARRIER_EDITED = EDITED + "=" + CARRIER_EDITED;
+ private static final String IS_CARRIER_DELETED = EDITED + "=" + CARRIER_DELETED;
+ private static final String IS_NOT_CARRIER_DELETED = EDITED + "!=" + CARRIER_DELETED;
+ private static final String IS_CARRIER_DELETED_BUT_PRESENT_IN_XML =
+ EDITED + "=" + CARRIER_DELETED_BUT_PRESENT_IN_XML;
+ private static final String IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML =
+ EDITED + "!=" + CARRIER_DELETED_BUT_PRESENT_IN_XML;
+
private static final int INVALID_APN_ID = -1;
private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>();
static {
// Columns not included in UNIQUE constraint: name, current, edited, user, server, password,
- // authtype, type, protocol, roaming_protocol, sub_id, modem_cognitive, max_conns, wait_time,
- // max_conns_time, mtu, bearer_bitmask, user_visible
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.NUMERIC);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.MCC);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.MNC);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.APN);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.PROXY);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.PORT);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.MMSPROXY);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.MMSPORT);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.MMSC);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.CARRIER_ENABLED);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.BEARER);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.MVNO_TYPE);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.MVNO_MATCH_DATA);
- CARRIERS_UNIQUE_FIELDS.add(Telephony.Carriers.PROFILE_ID);
+ // authtype, type, protocol, roaming_protocol, sub_id, modem_cognitive, max_conns,
+ // wait_time, max_conns_time, mtu, bearer_bitmask, user_visible
+ CARRIERS_UNIQUE_FIELDS.add(NUMERIC);
+ CARRIERS_UNIQUE_FIELDS.add(MCC);
+ CARRIERS_UNIQUE_FIELDS.add(MNC);
+ CARRIERS_UNIQUE_FIELDS.add(APN);
+ CARRIERS_UNIQUE_FIELDS.add(PROXY);
+ CARRIERS_UNIQUE_FIELDS.add(PORT);
+ CARRIERS_UNIQUE_FIELDS.add(MMSPROXY);
+ CARRIERS_UNIQUE_FIELDS.add(MMSPORT);
+ CARRIERS_UNIQUE_FIELDS.add(MMSC);
+ CARRIERS_UNIQUE_FIELDS.add(CARRIER_ENABLED);
+ CARRIERS_UNIQUE_FIELDS.add(BEARER);
+ CARRIERS_UNIQUE_FIELDS.add(MVNO_TYPE);
+ CARRIERS_UNIQUE_FIELDS.add(MVNO_MATCH_DATA);
+ CARRIERS_UNIQUE_FIELDS.add(PROFILE_ID);
}
static {
@@ -151,10 +172,10 @@ public class TelephonyProvider extends ContentProvider
s_urlMatcher.addURI("telephony", "carriers/update_db", URL_UPDATE_DB);
s_currentNullMap = new ContentValues(1);
- s_currentNullMap.put(Telephony.Carriers.CURRENT, "0");
+ s_currentNullMap.put(CURRENT, "0");
s_currentSetMap = new ContentValues(1);
- s_currentSetMap.put(Telephony.Carriers.CURRENT, "1");
+ s_currentSetMap.put(CURRENT, "1");
}
private static class DatabaseHelper extends SQLiteOpenHelper {
@@ -240,6 +261,7 @@ public class TelephonyProvider extends ContentProvider
+ SubscriptionManager.DATA_ROAMING + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
+ SubscriptionManager.MCC + " INTEGER DEFAULT 0,"
+ SubscriptionManager.MNC + " INTEGER DEFAULT 0,"
+ + SubscriptionManager.SIM_PROVISIONING_STATUS + " INTEGER DEFAULT " + SubscriptionManager.SIM_PROVISIONED + ","
+ SubscriptionManager.CB_EXTREME_THREAT_ALERT + " INTEGER DEFAULT 1,"
+ SubscriptionManager.CB_SEVERE_THREAT_ALERT + " INTEGER DEFAULT 1,"
+ SubscriptionManager.CB_AMBER_ALERT + " INTEGER DEFAULT 1,"
@@ -261,49 +283,103 @@ public class TelephonyProvider extends ContentProvider
if (DBG) log("dbh.createCarriersTable: " + tableName);
db.execSQL("CREATE TABLE " + tableName +
"(_id INTEGER PRIMARY KEY," +
- "name TEXT DEFAULT ''," +
- "numeric TEXT DEFAULT ''," +
- "mcc TEXT DEFAULT ''," +
- "mnc TEXT DEFAULT ''," +
- "apn TEXT DEFAULT ''," +
- "user TEXT DEFAULT ''," +
- "server TEXT DEFAULT ''," +
- "password TEXT DEFAULT ''," +
- "proxy TEXT DEFAULT ''," +
- "port TEXT DEFAULT ''," +
- "mmsproxy TEXT DEFAULT ''," +
- "mmsport TEXT DEFAULT ''," +
- "mmsc TEXT DEFAULT ''," +
- "authtype INTEGER DEFAULT -1," +
- "type TEXT DEFAULT ''," +
- "current INTEGER," +
- "protocol TEXT DEFAULT 'IP'," +
- "roaming_protocol TEXT DEFAULT 'IP'," +
- "carrier_enabled BOOLEAN DEFAULT 1," +
- "bearer INTEGER DEFAULT 0," +
- "bearer_bitmask INTEGER DEFAULT 0," +
- "mvno_type TEXT DEFAULT ''," +
- "mvno_match_data TEXT DEFAULT ''," +
- "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," +
- "profile_id INTEGER DEFAULT 0," +
- "modem_cognitive BOOLEAN DEFAULT 0," +
- "max_conns INTEGER DEFAULT 0," +
- "wait_time INTEGER DEFAULT 0," +
- "max_conns_time INTEGER DEFAULT 0," +
- "mtu INTEGER DEFAULT 0," +
- "edited INTEGER DEFAULT " + Telephony.Carriers.UNEDITED + "," +
- "user_visible BOOLEAN DEFAULT 1," +
+ NAME + " TEXT DEFAULT ''," +
+ NUMERIC + " TEXT DEFAULT ''," +
+ MCC + " TEXT DEFAULT ''," +
+ MNC + " TEXT DEFAULT ''," +
+ APN + " TEXT DEFAULT ''," +
+ USER + " TEXT DEFAULT ''," +
+ SERVER + " TEXT DEFAULT ''," +
+ PASSWORD + " TEXT DEFAULT ''," +
+ PROXY + " TEXT DEFAULT ''," +
+ PORT + " TEXT DEFAULT ''," +
+ MMSPROXY + " TEXT DEFAULT ''," +
+ MMSPORT + " TEXT DEFAULT ''," +
+ MMSC + " TEXT DEFAULT ''," +
+ AUTH_TYPE + " INTEGER DEFAULT -1," +
+ TYPE + " TEXT DEFAULT ''," +
+ CURRENT + " INTEGER," +
+ PROTOCOL + " TEXT DEFAULT 'IP'," +
+ ROAMING_PROTOCOL + " TEXT DEFAULT 'IP'," +
+ CARRIER_ENABLED + " BOOLEAN DEFAULT 1," +
+ BEARER + " INTEGER DEFAULT 0," +
+ BEARER_BITMASK + " INTEGER DEFAULT 0," +
+ MVNO_TYPE + " TEXT DEFAULT ''," +
+ MVNO_MATCH_DATA + " TEXT DEFAULT ''," +
+ SUBSCRIPTION_ID + " INTEGER DEFAULT "
+ + SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," +
+ PROFILE_ID + " INTEGER DEFAULT 0," +
+ MODEM_COGNITIVE + " BOOLEAN DEFAULT 0," +
+ MAX_CONNS + " INTEGER DEFAULT 0," +
+ WAIT_TIME + " INTEGER DEFAULT 0," +
+ MAX_CONNS_TIME + " INTEGER DEFAULT 0," +
+ MTU + " INTEGER DEFAULT 0," +
+ EDITED + " INTEGER DEFAULT " + UNEDITED + "," +
+ USER_VISIBLE + " BOOLEAN DEFAULT 1," +
// Uniqueness collisions are used to trigger merge code so if a field is listed
// here it means we will accept both (user edited + new apn_conf definition)
// Columns not included in UNIQUE constraint: name, current, edited,
// user, server, password, authtype, type, protocol, roaming_protocol, sub_id,
// modem_cognitive, max_conns, wait_time, max_conns_time, mtu, bearer_bitmask,
// user_visible
- "UNIQUE (numeric, mcc, mnc, apn, proxy, port, mmsproxy, mmsport, mmsc," +
- "carrier_enabled, bearer, mvno_type, mvno_match_data, profile_id));");
+ "UNIQUE (" + TextUtils.join(", ", CARRIERS_UNIQUE_FIELDS) + "));");
if (DBG) log("dbh.createCarriersTable:-");
}
+ private long getChecksum(File file) {
+ long checksum = -1;
+ try {
+ checksum = FileUtils.checksumCrc32(file);
+ if (DBG) log("Checksum for " + file.getAbsolutePath() + " is " + checksum);
+ } catch (FileNotFoundException e) {
+ loge("FileNotFoundException for " + file.getAbsolutePath() + ":" + e);
+ } catch (IOException e) {
+ loge("IOException for " + file.getAbsolutePath() + ":" + e);
+ }
+ return checksum;
+ }
+
+ private long getApnConfChecksum() {
+ SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+ return sp.getLong(APN_CONF_CHECKSUM, -1);
+ }
+
+ private void setApnConfChecksum(long checksum) {
+ SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putLong(APN_CONF_CHECKSUM, checksum);
+ editor.apply();
+ }
+
+ private File getApnConfFile() {
+ // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+ File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
+ File oemConfFile = new File(Environment.getOemDirectory(), OEM_APNS_PATH);
+ File updatedConfFile = new File(Environment.getDataDirectory(), OTA_UPDATED_APNS_PATH);
+ confFile = getNewerFile(confFile, oemConfFile);
+ confFile = getNewerFile(confFile, updatedConfFile);
+ return confFile;
+ }
+
+ /**
+ * This function computes checksum for the file to be read and compares it against the
+ * last read file. DB needs to be updated only if checksum has changed, or old checksum does
+ * not exist.
+ * @return true if DB should be updated with new conf file, false otherwise
+ */
+ private boolean apnDbUpdateNeeded() {
+ File confFile = getApnConfFile();
+ long newChecksum = getChecksum(confFile);
+ long oldChecksum = getApnConfChecksum();
+ if (DBG) log("newChecksum: " + newChecksum);
+ if (DBG) log("oldChecksum: " + oldChecksum);
+ if (newChecksum == oldChecksum) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
/**
* This function adds APNs from xml file(s) to db. The db may or may not be empty to begin
* with.
@@ -326,12 +402,7 @@ public class TelephonyProvider extends ContentProvider
// Read external APNS data (partner-provided)
XmlPullParser confparser = null;
- // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
- File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
- File oemConfFile = new File(Environment.getOemDirectory(), OEM_APNS_PATH);
- File updatedConfFile = new File(Environment.getDataDirectory(), OTA_UPDATED_APNS_PATH);
- confFile = getNewerFile(confFile, oemConfFile);
- confFile = getNewerFile(confFile, updatedConfFile);
+ File confFile = getApnConfFile();
FileReader confreader = null;
if (DBG) log("confFile = " + confFile);
@@ -365,20 +436,17 @@ public class TelephonyProvider extends ContentProvider
}
// Delete USER_DELETED
- db.delete(CARRIERS_TABLE, "edited=" + Telephony.Carriers.USER_DELETED + " or " +
- "edited=" + Telephony.Carriers.CARRIER_DELETED, null);
+ db.delete(CARRIERS_TABLE, IS_USER_DELETED + " or " + IS_CARRIER_DELETED, null);
// Change USER_DELETED_BUT_PRESENT_IN_XML to USER_DELETED
ContentValues cv = new ContentValues();
- cv.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_DELETED);
- db.update(CARRIERS_TABLE, cv, "edited=" + Telephony.Carriers.USER_DELETED_BUT_PRESENT_IN_XML,
- null);
+ cv.put(EDITED, USER_DELETED);
+ db.update(CARRIERS_TABLE, cv, IS_USER_DELETED_BUT_PRESENT_IN_XML, null);
// Change CARRIER_DELETED_BUT_PRESENT_IN_XML to CARRIER_DELETED
cv = new ContentValues();
- cv.put(Telephony.Carriers.EDITED, Telephony.Carriers.CARRIER_DELETED);
- db.update(CARRIERS_TABLE, cv, "edited=" + Telephony.Carriers.CARRIER_DELETED_BUT_PRESENT_IN_XML,
- null);
+ cv.put(EDITED, CARRIER_DELETED);
+ db.update(CARRIERS_TABLE, cv, IS_CARRIER_DELETED_BUT_PRESENT_IN_XML, null);
if (confreader != null) {
try {
@@ -387,6 +455,9 @@ public class TelephonyProvider extends ContentProvider
// do nothing
}
}
+
+ // Update the stored checksum
+ setApnConfChecksum(getChecksum(confFile));
}
if (VDBG) log("dbh.initDatabase:- db=" + db);
@@ -571,15 +642,13 @@ public class TelephonyProvider extends ContentProvider
c = db.query(CARRIERS_TABLE, proj, null, null, null, null, null);
log("dbh.onUpgrade:- after upgrading total number of rows: " + c.getCount());
c.close();
- c = db.query(CARRIERS_TABLE, proj, "edited=" + Telephony.Carriers.UNEDITED,
- null, null, null, null);
- log("dbh.onUpgrade:- after upgrading total number of rows with edited="
- + Telephony.Carriers.UNEDITED + ": " + c.getCount());
+ c = db.query(CARRIERS_TABLE, proj, IS_UNEDITED, null, null, null, null);
+ log("dbh.onUpgrade:- after upgrading total number of rows with " + IS_UNEDITED +
+ ": " + c.getCount());
c.close();
- c = db.query(CARRIERS_TABLE, proj, "edited!=" + Telephony.Carriers.UNEDITED,
- null, null, null, null);
- log("dbh.onUpgrade:- after upgrading total number of rows with edited!="
- + Telephony.Carriers.UNEDITED + ": " + c.getCount());
+ c = db.query(CARRIERS_TABLE, proj, IS_EDITED, null, null, null, null);
+ log("dbh.onUpgrade:- after upgrading total number of rows with " + IS_EDITED +
+ ": " + c.getCount());
c.close();
}
@@ -627,13 +696,13 @@ public class TelephonyProvider extends ContentProvider
try {
c = db.query(CARRIERS_TABLE, null, null, null, null, null, null,
String.valueOf(1));
- if (c == null || c.getColumnIndex(Telephony.Carriers.USER_VISIBLE) == -1) {
+ if (c == null || c.getColumnIndex(USER_VISIBLE) == -1) {
db.execSQL("ALTER TABLE " + CARRIERS_TABLE + " ADD COLUMN " +
- Telephony.Carriers.USER_VISIBLE + " BOOLEAN DEFAULT 1;");
+ USER_VISIBLE + " BOOLEAN DEFAULT 1;");
} else {
if (DBG) {
log("onUpgrade skipping " + CARRIERS_TABLE + " upgrade. Column " +
- Telephony.Carriers.USER_VISIBLE + " already exists.");
+ USER_VISIBLE + " already exists.");
}
}
} finally {
@@ -643,6 +712,20 @@ public class TelephonyProvider extends ContentProvider
}
oldVersion = 17 << 16 | 6;
}
+ if (oldVersion < (18 << 16 | 6)) {
+ try {
+ // Try to update the siminfo table. It might not be there.
+ db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN " +
+ SubscriptionManager.SIM_PROVISIONING_STATUS + " INTEGER DEFAULT " +
+ SubscriptionManager.SIM_PROVISIONED + ";");
+ } catch (SQLiteException e) {
+ if (DBG) {
+ log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+ " The table will get created in onOpen.");
+ }
+ }
+ oldVersion = 18 << 16 | 6;
+ }
if (DBG) {
log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
}
@@ -663,12 +746,11 @@ public class TelephonyProvider extends ContentProvider
} catch (FileNotFoundException e) {
// This function is called only when upgrading db to version 15. Details about the
// upgrade are mentioned in onUpgrade(). This file missing means user/carrier added
- // APNs cannot be preserved. Throw an exception so that OEMs know they need to
+ // APNs cannot be preserved. Log an error message so that OEMs know they need to
// include old apns file for comparison.
- loge("preserveUserAndCarrierApns: FileNotFoundException");
- throw new RuntimeException("preserveUserAndCarrierApns: " + OLD_APNS_PATH +
- " not found. It is needed to upgrade from older versions of APN " +
- "db while preserving user/carrier added/edited entries.");
+ loge("PRESERVEUSERANDCARRIERAPNS: " + OLD_APNS_PATH +
+ " NOT FOUND. IT IS NEEDED TO UPGRADE FROM OLDER VERSIONS OF APN " +
+ "DB WHILE PRESERVING USER/CARRIER ADDED/EDITED ENTRIES.");
} catch (Exception e) {
loge("preserveUserAndCarrierApns: Exception while parsing '" +
confFile.getAbsolutePath() + "'" + e);
@@ -707,62 +789,87 @@ public class TelephonyProvider extends ContentProvider
}
}
+ private String queryValFirst(String field) {
+ return field + "=?";
+ }
+
+ private String queryVal(String field) {
+ return " and " + field + "=?";
+ }
+
+ private String queryValOrNull(String field) {
+ return " and (" + field + "=? or " + field + " is null)";
+ }
+
+ private String queryVal2OrNull(String field) {
+ return " and (" + field + "=? or " + field + "=? or " + field + " is null)";
+ }
+
private void deleteRow(SQLiteDatabase db, ContentValues values) {
if (VDBG) log("deleteRow");
- String where = "numeric=? and mcc=? and mnc=? and name=? and " +
- "(apn=? or apn is null) and " +
- "(user=? or user is null) and (server=? or server is null) and " +
- "(password=? or password is null) and (proxy=? or proxy is null) and " +
- "(port=? or port is null) and (mmsproxy=? or mmsproxy is null) and " +
- "(mmsport=? or mmsport is null) and (mmsc=? or mmsc is null) and " +
- "(authtype=? or authtype is null) and (type=? or type is null) and " +
- "(protocol=? or protocol is null) and " +
- "(roaming_protocol=? or roaming_protocol is null) and " +
- "(carrier_enabled=? or carrier_enabled=? or carrier_enabled is null) and " +
- "(bearer=? or bearer is null) and (mvno_type=? or mvno_type is null) and " +
- "(mvno_match_data=? or mvno_match_data is null) and " +
- "(profile_id=? or profile_id is null) and " +
- "(modem_cognitive=? or modem_cognitive=? or modem_cognitive is null) and " +
- "(max_conns=? or max_conns is null) and " +
- "(wait_time=? or wait_time is null) and " +
- "(max_conns_time=? or max_conns_time is null) and (mtu=? or mtu is null)";
+ String where = queryValFirst(NUMERIC) +
+ queryVal(MNC) +
+ queryVal(MNC) +
+ queryValOrNull(APN) +
+ queryValOrNull(USER) +
+ queryValOrNull(SERVER) +
+ queryValOrNull(PASSWORD) +
+ queryValOrNull(PROXY) +
+ queryValOrNull(PORT) +
+ queryValOrNull(MMSPROXY) +
+ queryValOrNull(MMSPORT) +
+ queryValOrNull(MMSC) +
+ queryValOrNull(AUTH_TYPE) +
+ queryValOrNull(TYPE) +
+ queryValOrNull(PROTOCOL) +
+ queryValOrNull(ROAMING_PROTOCOL) +
+ queryVal2OrNull(CARRIER_ENABLED) +
+ queryValOrNull(BEARER) +
+ queryValOrNull(MVNO_TYPE) +
+ queryValOrNull(MVNO_MATCH_DATA) +
+ queryValOrNull(PROFILE_ID) +
+ queryVal2OrNull(MODEM_COGNITIVE) +
+ queryValOrNull(MAX_CONNS) +
+ queryValOrNull(WAIT_TIME) +
+ queryValOrNull(MAX_CONNS_TIME) +
+ queryValOrNull(MTU);
String[] whereArgs = new String[29];
int i = 0;
- whereArgs[i++] = values.getAsString(Telephony.Carriers.NUMERIC);
- whereArgs[i++] = values.getAsString(Telephony.Carriers.MCC);
- whereArgs[i++] = values.getAsString(Telephony.Carriers.MNC);
- whereArgs[i++] = values.getAsString(Telephony.Carriers.NAME);
- whereArgs[i++] = values.containsKey(Telephony.Carriers.APN) ?
- values.getAsString(Telephony.Carriers.APN) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.USER) ?
- values.getAsString(Telephony.Carriers.USER) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.SERVER) ?
- values.getAsString(Telephony.Carriers.SERVER) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.PASSWORD) ?
- values.getAsString(Telephony.Carriers.PASSWORD) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.PROXY) ?
- values.getAsString(Telephony.Carriers.PROXY) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.PORT) ?
- values.getAsString(Telephony.Carriers.PORT) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MMSPROXY) ?
- values.getAsString(Telephony.Carriers.MMSPROXY) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MMSPORT) ?
- values.getAsString(Telephony.Carriers.MMSPORT) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MMSC) ?
- values.getAsString(Telephony.Carriers.MMSC) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.AUTH_TYPE) ?
- values.getAsString(Telephony.Carriers.AUTH_TYPE) : "-1";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.TYPE) ?
- values.getAsString(Telephony.Carriers.TYPE) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.PROTOCOL) ?
- values.getAsString(Telephony.Carriers.PROTOCOL) : "IP";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.ROAMING_PROTOCOL) ?
- values.getAsString(Telephony.Carriers.ROAMING_PROTOCOL) : "IP";
-
- if (values.containsKey(Telephony.Carriers.CARRIER_ENABLED) &&
- (values.getAsString(Telephony.Carriers.CARRIER_ENABLED).
+ whereArgs[i++] = values.getAsString(NUMERIC);
+ whereArgs[i++] = values.getAsString(MCC);
+ whereArgs[i++] = values.getAsString(MNC);
+ whereArgs[i++] = values.getAsString(NAME);
+ whereArgs[i++] = values.containsKey(APN) ?
+ values.getAsString(APN) : "";
+ whereArgs[i++] = values.containsKey(USER) ?
+ values.getAsString(USER) : "";
+ whereArgs[i++] = values.containsKey(SERVER) ?
+ values.getAsString(SERVER) : "";
+ whereArgs[i++] = values.containsKey(PASSWORD) ?
+ values.getAsString(PASSWORD) : "";
+ whereArgs[i++] = values.containsKey(PROXY) ?
+ values.getAsString(PROXY) : "";
+ whereArgs[i++] = values.containsKey(PORT) ?
+ values.getAsString(PORT) : "";
+ whereArgs[i++] = values.containsKey(MMSPROXY) ?
+ values.getAsString(MMSPROXY) : "";
+ whereArgs[i++] = values.containsKey(MMSPORT) ?
+ values.getAsString(MMSPORT) : "";
+ whereArgs[i++] = values.containsKey(MMSC) ?
+ values.getAsString(MMSC) : "";
+ whereArgs[i++] = values.containsKey(AUTH_TYPE) ?
+ values.getAsString(AUTH_TYPE) : "-1";
+ whereArgs[i++] = values.containsKey(TYPE) ?
+ values.getAsString(TYPE) : "";
+ whereArgs[i++] = values.containsKey(PROTOCOL) ?
+ values.getAsString(PROTOCOL) : "IP";
+ whereArgs[i++] = values.containsKey(ROAMING_PROTOCOL) ?
+ values.getAsString(ROAMING_PROTOCOL) : "IP";
+
+ if (values.containsKey(CARRIER_ENABLED) &&
+ (values.getAsString(CARRIER_ENABLED).
equalsIgnoreCase("false") ||
- values.getAsString(Telephony.Carriers.CARRIER_ENABLED).equals("0"))) {
+ values.getAsString(CARRIER_ENABLED).equals("0"))) {
whereArgs[i++] = "false";
whereArgs[i++] = "0";
} else {
@@ -770,19 +877,19 @@ public class TelephonyProvider extends ContentProvider
whereArgs[i++] = "1";
}
- whereArgs[i++] = values.containsKey(Telephony.Carriers.BEARER) ?
- values.getAsString(Telephony.Carriers.BEARER) : "0";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MVNO_TYPE) ?
- values.getAsString(Telephony.Carriers.MVNO_TYPE) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MVNO_MATCH_DATA) ?
- values.getAsString(Telephony.Carriers.MVNO_MATCH_DATA) : "";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.PROFILE_ID) ?
- values.getAsString(Telephony.Carriers.PROFILE_ID) : "0";
+ whereArgs[i++] = values.containsKey(BEARER) ?
+ values.getAsString(BEARER) : "0";
+ whereArgs[i++] = values.containsKey(MVNO_TYPE) ?
+ values.getAsString(MVNO_TYPE) : "";
+ whereArgs[i++] = values.containsKey(MVNO_MATCH_DATA) ?
+ values.getAsString(MVNO_MATCH_DATA) : "";
+ whereArgs[i++] = values.containsKey(PROFILE_ID) ?
+ values.getAsString(PROFILE_ID) : "0";
- if (values.containsKey(Telephony.Carriers.MODEM_COGNITIVE) &&
- (values.getAsString(Telephony.Carriers.MODEM_COGNITIVE).
+ if (values.containsKey(MODEM_COGNITIVE) &&
+ (values.getAsString(MODEM_COGNITIVE).
equalsIgnoreCase("true") ||
- values.getAsString(Telephony.Carriers.MODEM_COGNITIVE).equals("1"))) {
+ values.getAsString(MODEM_COGNITIVE).equals("1"))) {
whereArgs[i++] = "true";
whereArgs[i++] = "1";
} else {
@@ -790,14 +897,14 @@ public class TelephonyProvider extends ContentProvider
whereArgs[i++] = "0";
}
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MAX_CONNS) ?
- values.getAsString(Telephony.Carriers.MAX_CONNS) : "0";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.WAIT_TIME) ?
- values.getAsString(Telephony.Carriers.WAIT_TIME) : "0";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MAX_CONNS_TIME) ?
- values.getAsString(Telephony.Carriers.MAX_CONNS_TIME) : "0";
- whereArgs[i++] = values.containsKey(Telephony.Carriers.MTU) ?
- values.getAsString(Telephony.Carriers.MTU) : "0";
+ whereArgs[i++] = values.containsKey(MAX_CONNS) ?
+ values.getAsString(MAX_CONNS) : "0";
+ whereArgs[i++] = values.containsKey(WAIT_TIME) ?
+ values.getAsString(WAIT_TIME) : "0";
+ whereArgs[i++] = values.containsKey(MAX_CONNS_TIME) ?
+ values.getAsString(MAX_CONNS_TIME) : "0";
+ whereArgs[i++] = values.containsKey(MTU) ?
+ values.getAsString(MTU) : "0";
if (VDBG) {
log("deleteRow: where: " + where);
@@ -825,74 +932,70 @@ public class TelephonyProvider extends ContentProvider
// with default if there's a default value for the field
// String vals
- getStringValueFromCursor(cv, c, Telephony.Carriers.NAME);
- getStringValueFromCursor(cv, c, Telephony.Carriers.NUMERIC);
- getStringValueFromCursor(cv, c, Telephony.Carriers.MCC);
- getStringValueFromCursor(cv, c, Telephony.Carriers.MNC);
- getStringValueFromCursor(cv, c, Telephony.Carriers.APN);
- getStringValueFromCursor(cv, c, Telephony.Carriers.USER);
- getStringValueFromCursor(cv, c, Telephony.Carriers.SERVER);
- getStringValueFromCursor(cv, c, Telephony.Carriers.PASSWORD);
- getStringValueFromCursor(cv, c, Telephony.Carriers.PROXY);
- getStringValueFromCursor(cv, c, Telephony.Carriers.PORT);
- getStringValueFromCursor(cv, c, Telephony.Carriers.MMSPROXY);
- getStringValueFromCursor(cv, c, Telephony.Carriers.MMSPORT);
- getStringValueFromCursor(cv, c, Telephony.Carriers.MMSC);
- getStringValueFromCursor(cv, c, Telephony.Carriers.TYPE);
- getStringValueFromCursor(cv, c, Telephony.Carriers.PROTOCOL);
- getStringValueFromCursor(cv, c, Telephony.Carriers.ROAMING_PROTOCOL);
- getStringValueFromCursor(cv, c, Telephony.Carriers.MVNO_TYPE);
- getStringValueFromCursor(cv, c, Telephony.Carriers.MVNO_MATCH_DATA);
+ getStringValueFromCursor(cv, c, NAME);
+ getStringValueFromCursor(cv, c, NUMERIC);
+ getStringValueFromCursor(cv, c, MCC);
+ getStringValueFromCursor(cv, c, MNC);
+ getStringValueFromCursor(cv, c, APN);
+ getStringValueFromCursor(cv, c, USER);
+ getStringValueFromCursor(cv, c, SERVER);
+ getStringValueFromCursor(cv, c, PASSWORD);
+ getStringValueFromCursor(cv, c, PROXY);
+ getStringValueFromCursor(cv, c, PORT);
+ getStringValueFromCursor(cv, c, MMSPROXY);
+ getStringValueFromCursor(cv, c, MMSPORT);
+ getStringValueFromCursor(cv, c, MMSC);
+ getStringValueFromCursor(cv, c, TYPE);
+ getStringValueFromCursor(cv, c, PROTOCOL);
+ getStringValueFromCursor(cv, c, ROAMING_PROTOCOL);
+ getStringValueFromCursor(cv, c, MVNO_TYPE);
+ getStringValueFromCursor(cv, c, MVNO_MATCH_DATA);
// bool/int vals
- getIntValueFromCursor(cv, c, Telephony.Carriers.AUTH_TYPE);
- getIntValueFromCursor(cv, c, Telephony.Carriers.CURRENT);
- getIntValueFromCursor(cv, c, Telephony.Carriers.CARRIER_ENABLED);
- getIntValueFromCursor(cv, c, Telephony.Carriers.BEARER);
- getIntValueFromCursor(cv, c, Telephony.Carriers.SUBSCRIPTION_ID);
- getIntValueFromCursor(cv, c, Telephony.Carriers.PROFILE_ID);
- getIntValueFromCursor(cv, c, Telephony.Carriers.MODEM_COGNITIVE);
- getIntValueFromCursor(cv, c, Telephony.Carriers.MAX_CONNS);
- getIntValueFromCursor(cv, c, Telephony.Carriers.WAIT_TIME);
- getIntValueFromCursor(cv, c, Telephony.Carriers.MAX_CONNS_TIME);
- getIntValueFromCursor(cv, c, Telephony.Carriers.MTU);
+ getIntValueFromCursor(cv, c, AUTH_TYPE);
+ getIntValueFromCursor(cv, c, CURRENT);
+ getIntValueFromCursor(cv, c, CARRIER_ENABLED);
+ getIntValueFromCursor(cv, c, BEARER);
+ getIntValueFromCursor(cv, c, SUBSCRIPTION_ID);
+ getIntValueFromCursor(cv, c, PROFILE_ID);
+ getIntValueFromCursor(cv, c, MODEM_COGNITIVE);
+ getIntValueFromCursor(cv, c, MAX_CONNS);
+ getIntValueFromCursor(cv, c, WAIT_TIME);
+ getIntValueFromCursor(cv, c, MAX_CONNS_TIME);
+ getIntValueFromCursor(cv, c, MTU);
// Change bearer to a bitmask
- String bearerStr = c.getString(c.getColumnIndex(Telephony.Carriers.BEARER));
+ String bearerStr = c.getString(c.getColumnIndex(BEARER));
if (!TextUtils.isEmpty(bearerStr)) {
int bearer_bitmask = ServiceState.getBitmaskForTech(
Integer.parseInt(bearerStr));
- cv.put(Telephony.Carriers.BEARER_BITMASK, bearer_bitmask);
+ cv.put(BEARER_BITMASK, bearer_bitmask);
}
int userEditedColumnIdx = c.getColumnIndex("user_edited");
if (userEditedColumnIdx != -1) {
String user_edited = c.getString(userEditedColumnIdx);
if (!TextUtils.isEmpty(user_edited)) {
- cv.put(Telephony.Carriers.EDITED, new Integer(user_edited));
+ cv.put(EDITED, new Integer(user_edited));
}
} else {
- cv.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_EDITED);
+ cv.put(EDITED, USER_EDITED);
}
- // New EDITED column. Default value (Telephony.Carriers.UNEDITED) will
+ // New EDITED column. Default value (UNEDITED) will
// be used for all rows except for non-mvno entries for plmns indicated
// by resource: those will be set to CARRIER_EDITED to preserve
// their current values
- val = c.getString(c.getColumnIndex(Telephony.Carriers.NUMERIC));
+ val = c.getString(c.getColumnIndex(NUMERIC));
for (String s : persistApnsForPlmns) {
if (!TextUtils.isEmpty(val) && val.equals(s) &&
- (!cv.containsKey(Telephony.Carriers.MVNO_TYPE) ||
- TextUtils.isEmpty(cv.getAsString(Telephony.Carriers.
- MVNO_TYPE)))) {
+ (!cv.containsKey(MVNO_TYPE) ||
+ TextUtils.isEmpty(cv.getAsString(MVNO_TYPE)))) {
if (userEditedColumnIdx == -1) {
- cv.put(Telephony.Carriers.EDITED,
- Telephony.Carriers.CARRIER_EDITED);
+ cv.put(EDITED, CARRIER_EDITED);
} else { // if (oldVersion == 14) -- if db had user_edited column
- if (cv.getAsInteger(Telephony.Carriers.EDITED) ==
- Telephony.Carriers.USER_EDITED) {
- cv.put(Telephony.Carriers.EDITED,
- Telephony.Carriers.CARRIER_EDITED);
+ if (cv.getAsInteger(EDITED) == USER_EDITED) {
+ cv.put(EDITED, CARRIER_EDITED);
}
}
@@ -961,50 +1064,51 @@ public class TelephonyProvider extends ContentProvider
String mnc = parser.getAttributeValue(null, "mnc");
String numeric = mcc + mnc;
- map.put(Telephony.Carriers.NUMERIC, numeric);
- map.put(Telephony.Carriers.MCC, mcc);
- map.put(Telephony.Carriers.MNC, mnc);
- map.put(Telephony.Carriers.NAME, parser.getAttributeValue(null, "carrier"));
+ map.put(NUMERIC, numeric);
+ map.put(MCC, mcc);
+ map.put(MNC, mnc);
+ map.put(NAME, parser.getAttributeValue(null, "carrier"));
// do not add NULL to the map so that default values can be inserted in db
- addStringAttribute(parser, "apn", map, Telephony.Carriers.APN);
- addStringAttribute(parser, "user", map, Telephony.Carriers.USER);
- addStringAttribute(parser, "server", map, Telephony.Carriers.SERVER);
- addStringAttribute(parser, "password", map, Telephony.Carriers.PASSWORD);
- addStringAttribute(parser, "proxy", map, Telephony.Carriers.PROXY);
- addStringAttribute(parser, "port", map, Telephony.Carriers.PORT);
- addStringAttribute(parser, "mmsproxy", map, Telephony.Carriers.MMSPROXY);
- addStringAttribute(parser, "mmsport", map, Telephony.Carriers.MMSPORT);
- addStringAttribute(parser, "mmsc", map, Telephony.Carriers.MMSC);
- addStringAttribute(parser, "type", map, Telephony.Carriers.TYPE);
- addStringAttribute(parser, "protocol", map, Telephony.Carriers.PROTOCOL);
- addStringAttribute(parser, "roaming_protocol", map, Telephony.Carriers.ROAMING_PROTOCOL);
-
- addIntAttribute(parser, "authtype", map, Telephony.Carriers.AUTH_TYPE);
- addIntAttribute(parser, "bearer", map, Telephony.Carriers.BEARER);
- addIntAttribute(parser, "profile_id", map, Telephony.Carriers.PROFILE_ID);
- addIntAttribute(parser, "max_conns", map, Telephony.Carriers.MAX_CONNS);
- addIntAttribute(parser, "wait_time", map, Telephony.Carriers.WAIT_TIME);
- addIntAttribute(parser, "max_conns_time", map, Telephony.Carriers.MAX_CONNS_TIME);
- addIntAttribute(parser, "mtu", map, Telephony.Carriers.MTU);
-
-
- addBoolAttribute(parser, "carrier_enabled", map, Telephony.Carriers.CARRIER_ENABLED);
- addBoolAttribute(parser, "modem_cognitive", map, Telephony.Carriers.MODEM_COGNITIVE);
- addBoolAttribute(parser, "user_visible", map, Telephony.Carriers.USER_VISIBLE);
-
+ addStringAttribute(parser, "apn", map, APN);
+ addStringAttribute(parser, "user", map, USER);
+ addStringAttribute(parser, "server", map, SERVER);
+ addStringAttribute(parser, "password", map, PASSWORD);
+ addStringAttribute(parser, "proxy", map, PROXY);
+ addStringAttribute(parser, "port", map, PORT);
+ addStringAttribute(parser, "mmsproxy", map, MMSPROXY);
+ addStringAttribute(parser, "mmsport", map, MMSPORT);
+ addStringAttribute(parser, "mmsc", map, MMSC);
+ addStringAttribute(parser, "type", map, TYPE);
+ addStringAttribute(parser, "protocol", map, PROTOCOL);
+ addStringAttribute(parser, "roaming_protocol", map, ROAMING_PROTOCOL);
+
+ addIntAttribute(parser, "authtype", map, AUTH_TYPE);
+ addIntAttribute(parser, "bearer", map, BEARER);
+ addIntAttribute(parser, "profile_id", map, PROFILE_ID);
+ addIntAttribute(parser, "max_conns", map, MAX_CONNS);
+ addIntAttribute(parser, "wait_time", map, WAIT_TIME);
+ addIntAttribute(parser, "max_conns_time", map, MAX_CONNS_TIME);
+ addIntAttribute(parser, "mtu", map, MTU);
+
+
+ addBoolAttribute(parser, "carrier_enabled", map, CARRIER_ENABLED);
+ addBoolAttribute(parser, "modem_cognitive", map, MODEM_COGNITIVE);
+ addBoolAttribute(parser, "user_visible", map, USER_VISIBLE);
+
+ int bearerBitmask = 0;
String bearerList = parser.getAttributeValue(null, "bearer_bitmask");
if (bearerList != null) {
- int bearerBitmask = ServiceState.getBitmaskFromString(bearerList);
- map.put(Telephony.Carriers.BEARER_BITMASK, bearerBitmask);
+ bearerBitmask = ServiceState.getBitmaskFromString(bearerList);
}
+ map.put(BEARER_BITMASK, bearerBitmask);
String mvno_type = parser.getAttributeValue(null, "mvno_type");
if (mvno_type != null) {
String mvno_match_data = parser.getAttributeValue(null, "mvno_match_data");
if (mvno_match_data != null) {
- map.put(Telephony.Carriers.MVNO_TYPE, mvno_type);
- map.put(Telephony.Carriers.MVNO_MATCH_DATA, mvno_match_data);
+ map.put(MVNO_TYPE, mvno_type);
+ map.put(MVNO_MATCH_DATA, mvno_match_data);
}
}
@@ -1069,9 +1173,9 @@ public class TelephonyProvider extends ContentProvider
}
static public ContentValues setDefaultValue(ContentValues values) {
- if (!values.containsKey(Telephony.Carriers.SUBSCRIPTION_ID)) {
- int subId = SubscriptionManager.getDefaultSubId();
- values.put(Telephony.Carriers.SUBSCRIPTION_ID, subId);
+ if (!values.containsKey(SUBSCRIPTION_ID)) {
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
+ values.put(SUBSCRIPTION_ID, subId);
}
return values;
@@ -1080,8 +1184,7 @@ public class TelephonyProvider extends ContentProvider
private void insertAddingDefaults(SQLiteDatabase db, ContentValues row) {
row = setDefaultValue(row);
try {
- db.insertWithOnConflict(CARRIERS_TABLE, null, row,
- SQLiteDatabase.CONFLICT_ABORT);
+ db.insertWithOnConflict(CARRIERS_TABLE, null, row, SQLiteDatabase.CONFLICT_ABORT);
if (VDBG) log("dbh.insertAddingDefaults: db.insert returned >= 0; insert " +
"successful for cv " + row);
} catch (SQLException e) {
@@ -1095,20 +1198,19 @@ public class TelephonyProvider extends ContentProvider
if (oldRow != null) {
// Update the row
ContentValues mergedValues = new ContentValues();
- int edited = oldRow.getInt(oldRow.getColumnIndex(
- Telephony.Carriers.EDITED));
+ int edited = oldRow.getInt(oldRow.getColumnIndex(EDITED));
int old_edited = edited;
- if (edited != Telephony.Carriers.UNEDITED) {
- if (edited == Telephony.Carriers.USER_DELETED) {
+ if (edited != UNEDITED) {
+ if (edited == USER_DELETED) {
// USER_DELETED_BUT_PRESENT_IN_XML indicates entry has been deleted
// by user but present in apn xml file.
- edited = Telephony.Carriers.USER_DELETED_BUT_PRESENT_IN_XML;
- } else if (edited == Telephony.Carriers.CARRIER_DELETED) {
+ edited = USER_DELETED_BUT_PRESENT_IN_XML;
+ } else if (edited == CARRIER_DELETED) {
// CARRIER_DELETED_BUT_PRESENT_IN_XML indicates entry has been deleted
// by user but present in apn xml file.
- edited = Telephony.Carriers.CARRIER_DELETED_BUT_PRESENT_IN_XML;
+ edited = CARRIER_DELETED_BUT_PRESENT_IN_XML;
}
- mergedValues.put(Telephony.Carriers.EDITED, edited);
+ mergedValues.put(EDITED, edited);
}
mergeFieldsAndUpdateDb(db, CARRIERS_TABLE, oldRow, row, mergedValues, false,
@@ -1125,14 +1227,14 @@ public class TelephonyProvider extends ContentProvider
public static void mergeFieldsAndUpdateDb(SQLiteDatabase db, String table, Cursor oldRow,
ContentValues newRow, ContentValues mergedValues,
boolean onUpgrade, Context context) {
- if (newRow.containsKey(Telephony.Carriers.TYPE)) {
+ if (newRow.containsKey(TYPE)) {
// Merge the types
- String oldType = oldRow.getString(oldRow.getColumnIndex(Telephony.Carriers.TYPE));
- String newType = newRow.getAsString(Telephony.Carriers.TYPE);
+ String oldType = oldRow.getString(oldRow.getColumnIndex(TYPE));
+ String newType = newRow.getAsString(TYPE);
if (!oldType.equalsIgnoreCase(newType)) {
if (oldType.equals("") || newType.equals("")) {
- newRow.put(Telephony.Carriers.TYPE, "");
+ newRow.put(TYPE, "");
} else {
String[] oldTypes = oldType.toLowerCase().split(",");
String[] newTypes = newType.toLowerCase().split(",");
@@ -1140,9 +1242,9 @@ public class TelephonyProvider extends ContentProvider
if (VDBG) {
log("mergeFieldsAndUpdateDb: Calling separateRowsNeeded() oldType=" +
oldType + " old bearer=" + oldRow.getInt(oldRow.getColumnIndex(
- Telephony.Carriers.BEARER_BITMASK)) +
+ BEARER_BITMASK)) +
" old profile_id=" + oldRow.getInt(oldRow.getColumnIndex(
- Telephony.Carriers.PROFILE_ID)) +
+ PROFILE_ID)) +
" newRow " + newRow);
}
@@ -1166,26 +1268,24 @@ public class TelephonyProvider extends ContentProvider
for (int i = 0; i < mergedTypes.size(); i++) {
mergedType.append((i == 0 ? "" : ",") + mergedTypes.get(i));
}
- newRow.put(Telephony.Carriers.TYPE, mergedType.toString());
+ newRow.put(TYPE, mergedType.toString());
}
}
- mergedValues.put(Telephony.Carriers.TYPE, newRow.getAsString(
- Telephony.Carriers.TYPE));
+ mergedValues.put(TYPE, newRow.getAsString(
+ TYPE));
}
- if (newRow.containsKey(Telephony.Carriers.BEARER_BITMASK)) {
- int oldBearer = oldRow.getInt(oldRow.getColumnIndex(Telephony.Carriers.
- BEARER_BITMASK));
- int newBearer = newRow.getAsInteger(Telephony.Carriers.BEARER_BITMASK);
+ if (newRow.containsKey(BEARER_BITMASK)) {
+ int oldBearer = oldRow.getInt(oldRow.getColumnIndex(BEARER_BITMASK));
+ int newBearer = newRow.getAsInteger(BEARER_BITMASK);
if (oldBearer != newBearer) {
if (oldBearer == 0 || newBearer == 0) {
- newRow.put(Telephony.Carriers.BEARER_BITMASK, 0);
+ newRow.put(BEARER_BITMASK, 0);
} else {
- newRow.put(Telephony.Carriers.BEARER_BITMASK, (oldBearer | newBearer));
+ newRow.put(BEARER_BITMASK, (oldBearer | newBearer));
}
}
- mergedValues.put(Telephony.Carriers.BEARER_BITMASK, newRow.getAsInteger(
- Telephony.Carriers.BEARER_BITMASK));
+ mergedValues.put(BEARER_BITMASK, newRow.getAsInteger(BEARER_BITMASK));
}
if (!onUpgrade) {
@@ -1213,8 +1313,7 @@ public class TelephonyProvider extends ContentProvider
String[] persistApnsForPlmns = context.getResources().getStringArray(
R.array.persist_apns_for_plmn);
for (String s : persistApnsForPlmns) {
- if (s.equalsIgnoreCase(newRow.getAsString(Telephony.Carriers.
- NUMERIC))) {
+ if (s.equalsIgnoreCase(newRow.getAsString(NUMERIC))) {
match = true;
break;
}
@@ -1225,10 +1324,8 @@ public class TelephonyProvider extends ContentProvider
// APN falls under persist_apns_for_plmn
// Check if only difference between old type and new type is that
// one has dun
- ArrayList<String> oldTypesAl = new ArrayList<String>(
- Arrays.asList(oldTypes));
- ArrayList<String> newTypesAl = new ArrayList<String>(
- Arrays.asList(newTypes));
+ ArrayList<String> oldTypesAl = new ArrayList<String>(Arrays.asList(oldTypes));
+ ArrayList<String> newTypesAl = new ArrayList<String>(Arrays.asList(newTypes));
ArrayList<String> listWithDun = null;
ArrayList<String> listWithoutDun = null;
boolean dunInOld = false;
@@ -1243,8 +1340,7 @@ public class TelephonyProvider extends ContentProvider
return false;
}
- if (listWithDun.contains("dun") &&
- !listWithoutDun.contains("dun")) {
+ if (listWithDun.contains("dun") && !listWithoutDun.contains("dun")) {
listWithoutDun.add("dun");
if (!listWithDun.containsAll(listWithoutDun)) {
return false;
@@ -1253,8 +1349,7 @@ public class TelephonyProvider extends ContentProvider
// Only difference between old type and new type is that
// one has dun
// Check if profile_id is 0/not set
- if (oldRow.getInt(oldRow.getColumnIndex(Telephony.Carriers.
- PROFILE_ID)) == 0) {
+ if (oldRow.getInt(oldRow.getColumnIndex(PROFILE_ID)) == 0) {
if (dunInOld) {
// Update oldRow to remove dun from its type field
ContentValues updateOldRow = new ContentValues();
@@ -1268,18 +1363,16 @@ public class TelephonyProvider extends ContentProvider
}
String updatedType = sb.toString();
if (VDBG) {
- log("separateRowsNeeded: updating type in oldRow to " +
- updatedType);
+ log("separateRowsNeeded: updating type in oldRow to " + updatedType);
}
- updateOldRow.put(Telephony.Carriers.TYPE, updatedType);
+ updateOldRow.put(TYPE, updatedType);
db.update(table, updateOldRow,
"_id=" + oldRow.getInt(oldRow.getColumnIndex("_id")), null);
return true;
} else {
if (VDBG) log("separateRowsNeeded: adding profile id 1 to newRow");
// Update newRow to set profile_id to 1
- newRow.put(Telephony.Carriers.PROFILE_ID,
- new Integer(1));
+ newRow.put(PROFILE_ID, new Integer(1));
}
} else {
return false;
@@ -1288,13 +1381,11 @@ public class TelephonyProvider extends ContentProvider
// If match was found, both oldRow and newRow need to exist
// separately in db. Add newRow to db.
try {
- db.insertWithOnConflict(table, null, newRow,
- SQLiteDatabase.CONFLICT_REPLACE);
+ db.insertWithOnConflict(table, null, newRow, SQLiteDatabase.CONFLICT_REPLACE);
if (VDBG) log("separateRowsNeeded: added newRow with profile id 1 to db");
return true;
} catch (SQLException e) {
- loge("Exception on trying to add new row after " +
- "updating profile_id");
+ loge("Exception on trying to add new row after updating profile_id");
}
}
@@ -1305,50 +1396,37 @@ public class TelephonyProvider extends ContentProvider
ContentValues row) {
// Conflict is possible only when numeric, mcc, mnc (fields without any default value)
// are set in the new row
- if (!row.containsKey(Telephony.Carriers.NUMERIC) ||
- !row.containsKey(Telephony.Carriers.MCC) ||
- !row.containsKey(Telephony.Carriers.MNC)) {
+ if (!row.containsKey(NUMERIC) || !row.containsKey(MCC) || !row.containsKey(MNC)) {
loge("dbh.selectConflictingRow: called for non-conflicting row: " + row);
return null;
}
String[] columns = { "_id",
- Telephony.Carriers.TYPE,
- Telephony.Carriers.EDITED,
- Telephony.Carriers.BEARER_BITMASK,
- Telephony.Carriers.PROFILE_ID };
- String selection = "numeric=? AND mcc=? AND mnc=? AND apn=? AND proxy=? AND port=? "
- + "AND mmsproxy=? AND mmsport=? AND mmsc=? AND carrier_enabled=? AND bearer=? "
- + "AND mvno_type=? AND mvno_match_data=? AND profile_id=?";
+ TYPE,
+ EDITED,
+ BEARER_BITMASK,
+ PROFILE_ID };
+ String selection = TextUtils.join("=? AND ", CARRIERS_UNIQUE_FIELDS) + "=?";
int i = 0;
String[] selectionArgs = new String[14];
- selectionArgs[i++] = row.getAsString(Telephony.Carriers.NUMERIC);
- selectionArgs[i++] = row.getAsString(Telephony.Carriers.MCC);
- selectionArgs[i++] = row.getAsString(Telephony.Carriers.MNC);
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.APN) ?
- row.getAsString(Telephony.Carriers.APN) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.PROXY) ?
- row.getAsString(Telephony.Carriers.PROXY) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.PORT) ?
- row.getAsString(Telephony.Carriers.PORT) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.MMSPROXY) ?
- row.getAsString(Telephony.Carriers.MMSPROXY) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.MMSPORT) ?
- row.getAsString(Telephony.Carriers.MMSPORT) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.MMSC) ?
- row.getAsString(Telephony.Carriers.MMSC) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.CARRIER_ENABLED) &&
- (row.getAsString(Telephony.Carriers.CARRIER_ENABLED).equals("0") ||
- row.getAsString(Telephony.Carriers.CARRIER_ENABLED).equals("false")) ?
+ selectionArgs[i++] = row.getAsString(NUMERIC);
+ selectionArgs[i++] = row.getAsString(MCC);
+ selectionArgs[i++] = row.getAsString(MNC);
+ selectionArgs[i++] = row.containsKey(APN) ? row.getAsString(APN) : "";
+ selectionArgs[i++] = row.containsKey(PROXY) ? row.getAsString(PROXY) : "";
+ selectionArgs[i++] = row.containsKey(PORT) ? row.getAsString(PORT) : "";
+ selectionArgs[i++] = row.containsKey(MMSPROXY) ? row.getAsString(MMSPROXY) : "";
+ selectionArgs[i++] = row.containsKey(MMSPORT) ? row.getAsString(MMSPORT) : "";
+ selectionArgs[i++] = row.containsKey(MMSC) ? row.getAsString(MMSC) : "";
+ selectionArgs[i++] = row.containsKey(CARRIER_ENABLED) &&
+ (row.getAsString(CARRIER_ENABLED).equals("0") ||
+ row.getAsString(CARRIER_ENABLED).equals("false")) ?
"0" : "1";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.BEARER) ?
- row.getAsString(Telephony.Carriers.BEARER) : "0";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.MVNO_TYPE) ?
- row.getAsString(Telephony.Carriers.MVNO_TYPE) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.MVNO_MATCH_DATA) ?
- row.getAsString(Telephony.Carriers.MVNO_MATCH_DATA) : "";
- selectionArgs[i++] = row.containsKey(Telephony.Carriers.PROFILE_ID) ?
- row.getAsString(Telephony.Carriers.PROFILE_ID) : "0";
+ selectionArgs[i++] = row.containsKey(BEARER) ? row.getAsString(BEARER) : "0";
+ selectionArgs[i++] = row.containsKey(MVNO_TYPE) ? row.getAsString(MVNO_TYPE) : "";
+ selectionArgs[i++] = row.containsKey(MVNO_MATCH_DATA) ?
+ row.getAsString(MVNO_MATCH_DATA) : "";
+ selectionArgs[i++] = row.containsKey(PROFILE_ID) ? row.getAsString(PROFILE_ID) : "0";
Cursor c = db.query(table, columns, selection, selectionArgs, null, null, null);
@@ -1400,7 +1478,7 @@ public class TelephonyProvider extends ContentProvider
List<SubscriptionInfo> subInfoList = sm.getAllSubscriptionInfoList();
for (SubscriptionInfo subInfo : subInfoList) {
SharedPreferences spPrefFile = getContext().getSharedPreferences(
- PREF_FILE + subInfo.getSubscriptionId(), Context.MODE_PRIVATE);
+ PREF_FILE_APN + subInfo.getSubscriptionId(), Context.MODE_PRIVATE);
if (spPrefFile != null) {
SharedPreferences.Editor editor = spPrefFile.edit();
editor.clear();
@@ -1424,7 +1502,8 @@ public class TelephonyProvider extends ContentProvider
}
private void setPreferredApnId(Long id, int subId) {
- SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+ SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN,
+ Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putLong(COLUMN_APN_ID + subId, id != null ? id.longValue() : INVALID_APN_ID);
editor.apply();
@@ -1435,7 +1514,8 @@ public class TelephonyProvider extends ContentProvider
}
private long getPreferredApnId(int subId, boolean checkApnSp) {
- SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+ SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN,
+ Context.MODE_PRIVATE);
long apnId = sp.getLong(COLUMN_APN_ID + subId, INVALID_APN_ID);
if (apnId == INVALID_APN_ID && checkApnSp) {
apnId = getPreferredApnIdFromApn(subId);
@@ -1448,7 +1528,8 @@ public class TelephonyProvider extends ContentProvider
}
private void deletePreferredApnId() {
- SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
+ SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN,
+ Context.MODE_PRIVATE);
// before deleting, save actual preferred apns (not the ids) in a separate SP
Map<String, ?> allPrefApnId = sp.getAll();
for (String key : allPrefApnId.keySet()) {
@@ -1512,7 +1593,8 @@ public class TelephonyProvider extends ContentProvider
}
i++;
}
- Cursor c = db.query(CARRIERS_TABLE, new String[]{"_id"}, where, whereArgs, null, null, null);
+ Cursor c = db.query(CARRIERS_TABLE, new String[]{"_id"}, where, whereArgs, null, null,
+ null);
if (c != null) {
if (c.getCount() == 1) {
c.moveToFirst();
@@ -1551,7 +1633,7 @@ public class TelephonyProvider extends ContentProvider
+ selection + "selectionArgs=" + selectionArgs + ", sort=" + sort);
TelephonyManager mTelephonyManager =
(TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
String subIdString;
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setStrict(true); // a little protection from injection attacks
@@ -1568,7 +1650,7 @@ public class TelephonyProvider extends ContentProvider
return null;
}
if (DBG) log("subIdString = " + subIdString + " subId = " + subId);
- qb.appendWhere("numeric = '" + mTelephonyManager.getSimOperator(subId)+"'");
+ qb.appendWhere(NUMERIC + " = '" + mTelephonyManager.getSimOperator(subId) + "'");
// FIXME alter the selection to pass subId
// selection = selection + "and subId = "
}
@@ -1634,11 +1716,11 @@ public class TelephonyProvider extends ContentProvider
if (match != URL_SIMINFO) {
if (projectionIn != null) {
for (String column : projectionIn) {
- if (Telephony.Carriers.TYPE.equals(column) ||
- Telephony.Carriers.MMSC.equals(column) ||
- Telephony.Carriers.MMSPROXY.equals(column) ||
- Telephony.Carriers.MMSPORT.equals(column) ||
- Telephony.Carriers.APN.equals(column)) {
+ if (TYPE.equals(column) ||
+ MMSC.equals(column) ||
+ MMSPROXY.equals(column) ||
+ MMSPORT.equals(column) ||
+ APN.equals(column)) {
// noop
} else {
checkPermission();
@@ -1661,10 +1743,10 @@ public class TelephonyProvider extends ContentProvider
} else {
selection += " and ";
}
- selection += "edited!=" + Telephony.Carriers.USER_DELETED + " and edited!="
- + Telephony.Carriers.USER_DELETED_BUT_PRESENT_IN_XML + " and edited!="
- + Telephony.Carriers.CARRIER_DELETED + " and edited!="
- + Telephony.Carriers.CARRIER_DELETED_BUT_PRESENT_IN_XML;
+ selection += IS_NOT_USER_DELETED + " and " +
+ IS_NOT_USER_DELETED_BUT_PRESENT_IN_XML + " and " +
+ IS_NOT_CARRIER_DELETED + " and " +
+ IS_NOT_CARRIER_DELETED_BUT_PRESENT_IN_XML;
if (VDBG) log("query: selection modified to " + selection);
}
ret = qb.query(db, projectionIn, selection, selectionArgs, null, null, sort);
@@ -1702,7 +1784,7 @@ public class TelephonyProvider extends ContentProvider
public synchronized Uri insert(Uri url, ContentValues initialValues)
{
Uri result = null;
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
checkPermission();
@@ -1734,18 +1816,18 @@ public class TelephonyProvider extends ContentProvider
}
values = DatabaseHelper.setDefaultValue(values);
- if (!values.containsKey(Telephony.Carriers.EDITED)) {
- values.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_EDITED);
+ if (!values.containsKey(EDITED)) {
+ values.put(EDITED, USER_EDITED);
}
try {
// Replace on conflict so that if same APN is present in db with edited
- // as Telephony.Carriers.UNEDITED or USER/CARRIER_DELETED, it is replaced with
+ // as UNEDITED or USER/CARRIER_DELETED, it is replaced with
// edited USER/CARRIER_EDITED
long rowID = db.insertWithOnConflict(CARRIERS_TABLE, null, values,
SQLiteDatabase.CONFLICT_REPLACE);
if (rowID >= 0) {
- result = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, rowID);
+ result = ContentUris.withAppendedId(CONTENT_URI, rowID);
notify = true;
}
if (VDBG) log("insert: inserted " + values.toString() + " rowID = " + rowID);
@@ -1783,11 +1865,11 @@ public class TelephonyProvider extends ContentProvider
case URL_CURRENT:
{
// zero out the previous operator
- db.update(CARRIERS_TABLE, s_currentNullMap, "current!=0", null);
+ db.update(CARRIERS_TABLE, s_currentNullMap, CURRENT + "!=0", null);
- String numeric = initialValues.getAsString(Telephony.Carriers.NUMERIC);
+ String numeric = initialValues.getAsString(NUMERIC);
int updated = db.update(CARRIERS_TABLE, s_currentSetMap,
- "numeric = '" + numeric + "'", null);
+ NUMERIC + " = '" + numeric + "'", null);
if (updated > 0)
{
@@ -1833,7 +1915,7 @@ public class TelephonyProvider extends ContentProvider
}
if (notify) {
- getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null,
+ getContext().getContentResolver().notifyChange(CONTENT_URI, null,
true, UserHandle.USER_ALL);
}
@@ -1844,15 +1926,15 @@ public class TelephonyProvider extends ContentProvider
public synchronized int delete(Uri url, String where, String[] whereArgs)
{
int count = 0;
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
String userOrCarrierEdited = ") and (" +
- Telephony.Carriers.EDITED + "=" + Telephony.Carriers.USER_EDITED + " or " +
- Telephony.Carriers.EDITED + "=" + Telephony.Carriers.CARRIER_EDITED + ")";
+ EDITED + "=" + USER_EDITED + " or " +
+ EDITED + "=" + CARRIER_EDITED + ")";
String notUserOrCarrierEdited = ") and (" +
- Telephony.Carriers.EDITED + "!=" + Telephony.Carriers.USER_EDITED + " and " +
- Telephony.Carriers.EDITED + "!=" + Telephony.Carriers.CARRIER_EDITED + ")";
+ EDITED + "!=" + USER_EDITED + " and " +
+ EDITED + "!=" + CARRIER_EDITED + ")";
ContentValues cv = new ContentValues();
- cv.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_DELETED);
+ cv.put(EDITED, USER_DELETED);
checkPermission();
@@ -1911,12 +1993,12 @@ public class TelephonyProvider extends ContentProvider
{
// Delete user/carrier edited entries
count = db.delete(CARRIERS_TABLE,
- "(" + Telephony.Carriers._ID + "=?" + userOrCarrierEdited,
+ "(" + _ID + "=?" + userOrCarrierEdited,
new String[] { url.getLastPathSegment() });
// Otherwise mark as user deleted instead of deleting
count += db.update(CARRIERS_TABLE, cv,
- "(" + Telephony.Carriers._ID + "=?" + notUserOrCarrierEdited,
- new String[] { url.getLastPathSegment() });
+ "(" + _ID + "=?" + notUserOrCarrierEdited,
+ new String[]{url.getLastPathSegment() });
break;
}
@@ -1953,7 +2035,7 @@ public class TelephonyProvider extends ContentProvider
case URL_PREFERAPN:
case URL_PREFERAPN_NO_UPDATE:
{
- setPreferredApnId((long)-1, subId);
+ setPreferredApnId((long)INVALID_APN_ID, subId);
if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1;
break;
}
@@ -1975,7 +2057,7 @@ public class TelephonyProvider extends ContentProvider
}
if (count > 0) {
- getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null,
+ getContext().getContentResolver().notifyChange(CONTENT_URI, null,
true, UserHandle.USER_ALL);
}
@@ -1987,7 +2069,7 @@ public class TelephonyProvider extends ContentProvider
{
int count = 0;
int uriType = URL_UNKNOWN;
- int subId = SubscriptionManager.getDefaultSubId();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
checkPermission();
@@ -2011,12 +2093,12 @@ public class TelephonyProvider extends ContentProvider
case URL_TELEPHONY:
{
- if (!values.containsKey(Telephony.Carriers.EDITED)) {
- values.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_EDITED);
+ if (!values.containsKey(EDITED)) {
+ values.put(EDITED, USER_EDITED);
}
// Replace on conflict so that if same APN is present in db with edited
- // as Telephony.Carriers.UNEDITED or USER/CARRIER_DELETED, it is replaced with
+ // as UNEDITED or USER/CARRIER_DELETED, it is replaced with
// edited USER/CARRIER_EDITED
count = db.updateWithOnConflict(CARRIERS_TABLE, values, where, whereArgs,
SQLiteDatabase.CONFLICT_REPLACE);
@@ -2039,11 +2121,11 @@ public class TelephonyProvider extends ContentProvider
case URL_CURRENT:
{
- if (!values.containsKey(Telephony.Carriers.EDITED)) {
- values.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_EDITED);
+ if (!values.containsKey(EDITED)) {
+ values.put(EDITED, USER_EDITED);
}
// Replace on conflict so that if same APN is present in db with edited
- // as Telephony.Carriers.UNEDITED or USER/CARRIER_DELETED, it is replaced with
+ // as UNEDITED or USER/CARRIER_DELETED, it is replaced with
// edited USER/CARRIER_EDITED
count = db.updateWithOnConflict(CARRIERS_TABLE, values, where, whereArgs,
SQLiteDatabase.CONFLICT_REPLACE);
@@ -2056,14 +2138,14 @@ public class TelephonyProvider extends ContentProvider
throw new UnsupportedOperationException(
"Cannot update URL " + url + " with a where clause");
}
- if (!values.containsKey(Telephony.Carriers.EDITED)) {
- values.put(Telephony.Carriers.EDITED, Telephony.Carriers.USER_EDITED);
+ if (!values.containsKey(EDITED)) {
+ values.put(EDITED, USER_EDITED);
}
// Replace on conflict so that if same APN is present in db with edited
- // as Telephony.Carriers.UNEDITED or USER/CARRIER_DELETED, it is replaced with
+ // as UNEDITED or USER/CARRIER_DELETED, it is replaced with
// edited USER/CARRIER_EDITED
count = db.updateWithOnConflict(CARRIERS_TABLE, values,
- Telephony.Carriers._ID + "=?", new String[] { url.getLastPathSegment() },
+ _ID + "=?", new String[] { url.getLastPathSegment() },
SQLiteDatabase.CONFLICT_REPLACE);
break;
}
@@ -2115,7 +2197,7 @@ public class TelephonyProvider extends ContentProvider
break;
default:
getContext().getContentResolver().notifyChange(
- Telephony.Carriers.CONTENT_URI, null, true, UserHandle.USER_ALL);
+ CONTENT_URI, null, true, UserHandle.USER_ALL);
}
}
@@ -2153,11 +2235,16 @@ public class TelephonyProvider extends ContentProvider
} catch (SQLException e) {
loge("got exception when deleting to restore: " + e);
}
- setPreferredApnId((long)-1, subId);
+ setPreferredApnId((long) INVALID_APN_ID, subId);
mOpenHelper.initDatabase(db);
}
private synchronized void updateApnDb() {
+ if (!mOpenHelper.apnDbUpdateNeeded()) {
+ log("Skipping apn db update since apn-conf has not changed.");
+ return;
+ }
+
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
// Delete preferred APN for all subIds
@@ -2165,8 +2252,8 @@ public class TelephonyProvider extends ContentProvider
// Delete entries in db
try {
- if (VDBG) log("updateApnDb: deleting edited=Telephony.Carriers.UNEDITED entries");
- db.delete(CARRIERS_TABLE, "edited=" + Telephony.Carriers.UNEDITED, null);
+ if (VDBG) log("updateApnDb: deleting edited=UNEDITED entries");
+ db.delete(CARRIERS_TABLE, IS_UNEDITED, null);
} catch (SQLException e) {
loge("got exception when deleting to update: " + e);
}
@@ -2175,7 +2262,7 @@ public class TelephonyProvider extends ContentProvider
// Notify listereners of DB change since DB has been updated
getContext().getContentResolver().notifyChange(
- Telephony.Carriers.CONTENT_URI, null, true, UserHandle.USER_ALL);
+ CONTENT_URI, null, true, UserHandle.USER_ALL);
}