Messaging: Remove GServices

We don't have an actual implementation returning anything but the
defaults, so apply the defaults directly anywhere we need them

Change-Id: Ic651b4b13977799f2f4d2c702c430798fbff77f8
This commit is contained in:
Michael W
2024-12-26 14:55:12 +01:00
parent 7f22a3eea7
commit a2f67b4f7b
64 changed files with 91 additions and 3763 deletions
@@ -38,9 +38,6 @@ import com.android.messaging.sms.BugleApnSettingsLoader;
import com.android.messaging.sms.BugleUserAgentInfoLoader;
import com.android.messaging.sms.MmsConfig;
import com.android.messaging.ui.ConversationDrawables;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.BuglePrefsKeys;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.LogUtil;
@@ -83,8 +80,6 @@ public class BugleApplication extends Application implements UncaughtExceptionHa
public void initializeSync(final Factory factory) {
Trace.beginSection("app.initializeSync");
final Context context = factory.getApplicationContext();
final BugleGservices bugleGservices = factory.getBugleGservices();
final BuglePrefs buglePrefs = factory.getApplicationPrefs();
final DataModel dataModel = factory.getDataModel();
final CarrierConfigValuesLoader carrierConfigValuesLoader =
factory.getCarrierConfigValuesLoader();
@@ -94,7 +89,7 @@ public class BugleApplication extends Application implements UncaughtExceptionHa
BugleApplication.updateAppConfig(context);
// Initialize MMS lib
initMmsLib(context, bugleGservices, carrierConfigValuesLoader);
initMmsLib(context, carrierConfigValuesLoader);
// Initialize APN database
ApnDatabase.initializeAppContext(context);
// Fixup messages in flight if we crashed and send any pending
@@ -116,21 +111,12 @@ public class BugleApplication extends Application implements UncaughtExceptionHa
Context.RECEIVER_EXPORTED/*UNAUDITED*/);
}
private static void initMmsLib(final Context context, final BugleGservices bugleGservices,
private static void initMmsLib(final Context context,
final CarrierConfigValuesLoader carrierConfigValuesLoader) {
MmsManager.setApnSettingsLoader(new BugleApnSettingsLoader(context));
MmsManager.setCarrierConfigValuesLoader(carrierConfigValuesLoader);
MmsManager.setUserAgentInfoLoader(new BugleUserAgentInfoLoader(context));
MmsManager.setUseWakeLock(true);
// If Gservices is configured not to use mms api, force MmsManager to always use
// legacy mms sending logic
MmsManager.setForceLegacyMms(!bugleGservices.getBoolean(
BugleGservicesKeys.USE_MMS_API_IF_PRESENT,
BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT));
bugleGservices.registerForChanges(() -> MmsManager.setForceLegacyMms(
!bugleGservices.getBoolean(
BugleGservicesKeys.USE_MMS_API_IF_PRESENT,
BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT)));
}
public static void updateAppConfig(final Context context) {
+1 -2
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +27,6 @@ import com.android.messaging.datamodel.media.MediaResourceManager;
import com.android.messaging.sms.BugleCarrierConfigValuesLoader;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.MediaUtil;
import com.android.messaging.util.PhoneUtils;
@@ -56,7 +56,6 @@ public abstract class Factory {
public abstract Context getApplicationContext();
public abstract DataModel getDataModel();
public abstract BugleGservices getBugleGservices();
public abstract BuglePrefs getApplicationPrefs();
public abstract BuglePrefs getSubscriptionPrefs(int subId);
public abstract BuglePrefs getWidgetPrefs();
@@ -35,8 +35,6 @@ import com.android.messaging.ui.UIIntents;
import com.android.messaging.ui.UIIntentsImpl;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleApplicationPrefs;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesImpl;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.BugleSubscriptionPrefs;
import com.android.messaging.util.BugleWidgetPrefs;
@@ -51,7 +49,6 @@ import java.util.concurrent.ConcurrentHashMap;
class FactoryImpl extends Factory {
private BugleApplication mApplication;
private DataModel mDataModel;
private BugleGservices mBugleGservices;
private BugleApplicationPrefs mBugleApplicationPrefs;
private BugleWidgetPrefs mBugleWidgetPrefs;
private Context mApplicationContext;
@@ -89,7 +86,6 @@ class FactoryImpl extends Factory {
factory.mMemoryCacheManager = new MemoryCacheManager();
factory.mMediaCacheManager = new BugleMediaCacheManager();
factory.mMediaResourceManager = new MediaResourceManager();
factory.mBugleGservices = new BugleGservicesImpl(applicationContext);
factory.mBugleApplicationPrefs = new BugleApplicationPrefs(applicationContext);
factory.mDataModel = new DataModelImpl(applicationContext);
factory.mBugleWidgetPrefs = new BugleWidgetPrefs(applicationContext);
@@ -99,9 +95,6 @@ class FactoryImpl extends Factory {
factory.mSubscriptionPrefs = new SparseArray<BugleSubscriptionPrefs>();
factory.mCarrierConfigValuesLoader = new BugleCarrierConfigValuesLoader(applicationContext);
Assert.initializeGservices(factory.mBugleGservices);
LogUtil.initializeGservices(factory.mBugleGservices);
if (OsUtil.hasRequiredPermissions()) {
factory.onRequiredPermissionsAcquired();
}
@@ -135,11 +128,6 @@ class FactoryImpl extends Factory {
return mDataModel;
}
@Override
public BugleGservices getBugleGservices() {
return mBugleGservices;
}
@Override
public BuglePrefs getApplicationPrefs() {
return mBugleApplicationPrefs;
@@ -68,7 +68,6 @@ import com.android.messaging.sms.MmsUtils;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.Assert;
import com.android.messaging.util.AvatarUriUtil;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.BuglePrefsKeys;
@@ -605,10 +604,9 @@ public class BugleNotifications {
// Find out the last time we dinged for this conversation
Long lastTime = sLastMessageDingTime.get(conversationId);
if (sTimeBetweenDingsMs == 0) {
sTimeBetweenDingsMs = BugleGservices.get().getInt(
BugleGservicesKeys.NOTIFICATION_TIME_BETWEEN_RINGS_SECONDS,
BugleGservicesKeys.NOTIFICATION_TIME_BETWEEN_RINGS_SECONDS_DEFAULT) *
1000;
sTimeBetweenDingsMs =
BugleGservicesKeys.NOTIFICATION_TIME_BETWEEN_RINGS_SECONDS_DEFAULT *
1000;
}
if (lastTime == null
|| SystemClock.elapsedRealtime() - lastTime > sTimeBetweenDingsMs) {
@@ -27,17 +27,13 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.util.SparseArray;
import com.android.messaging.Factory;
import com.android.messaging.R;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.UiUtils;
import java.util.Locale;
import java.util.Stack;
import java.util.regex.Pattern;
public class DatabaseWrapper {
private static final String TAG = LogUtil.BUGLE_DATABASE_TAG;
@@ -45,12 +41,6 @@ public class DatabaseWrapper {
private final SQLiteDatabase mDatabase;
private final Context mContext;
private final boolean mLog;
/**
* Set mExplainQueryPlanRegexp (via {@link BugleGservicesKeys#EXPLAIN_QUERY_PLAN_REGEXP}
* to regex matching queries to see query plans. For example, ".*" to show all query plans.
*/
// See
private final String mExplainQueryPlanRegexp;
private static final int sTimingThreshold = 50; // in milliseconds
public static final int INDEX_INSERT_MESSAGE_PART = 0;
@@ -77,8 +67,6 @@ public class DatabaseWrapper {
DatabaseWrapper(final Context context, final SQLiteDatabase db) {
mLog = LogUtil.isLoggable(LogUtil.BUGLE_DATABASE_PERF_TAG, LogUtil.VERBOSE);
mExplainQueryPlanRegexp = Factory.get().getBugleGservices().getString(
BugleGservicesKeys.EXPLAIN_QUERY_PLAN_REGEXP, null);
mDatabase = db;
mContext = context;
mCompiledStatements = new SparseArray<SQLiteStatement>();
@@ -96,10 +84,6 @@ public class DatabaseWrapper {
return compiled;
}
private void maybePlayDebugNoise() {
DebugUtils.maybePlayDebugNoise(mContext, DebugUtils.DEBUG_SOUND_DB_OP);
}
private static void printTiming(final long t1, final String msg) {
final int transactionDepth = sTransactionDepth.get().size();
final long t2 = System.currentTimeMillis();
@@ -190,64 +174,10 @@ public class DatabaseWrapper {
}
}
private void explainQueryPlan(final SQLiteQueryBuilder qb, final SQLiteDatabase db,
final String[] projection, final String selection,
@SuppressWarnings("unused")
final String[] queryArgs,
final String groupBy,
@SuppressWarnings("unused")
final String having,
final String sortOrder, final String limit) {
final String queryString = qb.buildQuery(
projection,
selection,
groupBy,
null/*having*/,
sortOrder,
limit);
explainQueryPlan(db, queryString, queryArgs);
}
private void explainQueryPlan(final SQLiteDatabase db, final String sql,
final String[] queryArgs) {
if (!Pattern.matches(mExplainQueryPlanRegexp, sql)) {
return;
}
final Cursor planCursor = db.rawQuery("explain query plan " + sql, queryArgs);
try {
if (planCursor != null && planCursor.moveToFirst()) {
final int detailColumn = planCursor.getColumnIndex("detail");
final StringBuilder sb = new StringBuilder();
do {
sb.append(planCursor.getString(detailColumn));
sb.append("\n");
} while (planCursor.moveToNext());
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
LogUtil.v(TAG, "for query " + sql + "\nplan is: "
+ sb);
}
} catch (final Exception e) {
LogUtil.w(TAG, "Query plan failed ", e);
} finally {
if (planCursor != null) {
planCursor.close();
}
}
}
public Cursor query(final String searchTable, final String[] projection,
final String selection, final String[] selectionArgs, final String groupBy,
final String having, final String orderBy, final String limit) {
if (mExplainQueryPlanRegexp != null) {
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(searchTable);
explainQueryPlan(qb, mDatabase, projection, selection, selectionArgs,
groupBy, having, orderBy, limit);
}
maybePlayDebugNoise();
long t1 = 0;
if (mLog) {
t1 = System.currentTimeMillis();
@@ -274,11 +204,6 @@ public class DatabaseWrapper {
public Cursor query(final SQLiteQueryBuilder qb,
final String[] projection, final String selection, final String[] queryArgs,
final String groupBy, final String having, final String sortOrder, final String limit) {
if (mExplainQueryPlanRegexp != null) {
explainQueryPlan(qb, mDatabase, projection, selection, queryArgs,
groupBy, having, sortOrder, limit);
}
maybePlayDebugNoise();
long t1 = 0;
if (mLog) {
t1 = System.currentTimeMillis();
@@ -300,7 +225,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
final long retval =
DatabaseUtils.queryNumEntries(mDatabase, table, selection, selectionArgs);
if (mLog){
@@ -313,14 +237,10 @@ public class DatabaseWrapper {
}
public Cursor rawQuery(final String sql, final String[] args) {
if (mExplainQueryPlanRegexp != null) {
explainQueryPlan(mDatabase, sql, args);
}
long t1 = 0;
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
final Cursor cursor = mDatabase.rawQuery(sql, args);
if (mLog) {
printTiming(
@@ -336,7 +256,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
int count = 0;
try {
count = mDatabase.update(table, values, selection, selectionArgs);
@@ -356,7 +275,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
int count = 0;
try {
count = mDatabase.delete(table, whereClause, whereArgs);
@@ -378,7 +296,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
long rowId = -1;
try {
rowId = mDatabase.insert(table, nullColumnHack, values);
@@ -398,7 +315,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
long rowId = -1;
try {
rowId = mDatabase.replace(table, nullColumnHack, values);
@@ -421,7 +337,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
try {
mDatabase.execSQL(sql, bindArgs);
} catch (SQLiteFullException ex) {
@@ -439,7 +354,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
try {
mDatabase.execSQL(sql);
} catch (SQLiteFullException ex) {
@@ -457,7 +371,6 @@ public class DatabaseWrapper {
if (mLog) {
t1 = System.currentTimeMillis();
}
maybePlayDebugNoise();
final SQLiteStatement statement = mDatabase.compileStatement(sql);
int rowsUpdated = 0;
try {
@@ -51,7 +51,6 @@ import com.android.messaging.sms.MmsUtils;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.Assert;
import com.android.messaging.util.AvatarUriUtil;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.ContentType;
import com.android.messaging.util.ConversationIdSet;
@@ -1009,13 +1008,9 @@ public abstract class MessageNotificationState extends NotificationState {
private static int getMaxMessagesInConversationNotification() {
if (!BugleNotifications.isWearCompanionAppInstalled()) {
return BugleGservices.get().getInt(
BugleGservicesKeys.MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION,
BugleGservicesKeys.MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_DEFAULT);
return BugleGservicesKeys.MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_DEFAULT;
}
return BugleGservices.get().getInt(
BugleGservicesKeys.MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_WITH_WEARABLE,
BugleGservicesKeys.MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_WITH_WEARABLE_DEFAULT);
return BugleGservicesKeys.MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_WITH_WEARABLE_DEFAULT;
}
/**
@@ -452,8 +452,6 @@ public class MessagingContentProvider extends ContentProvider {
defaultSmsApp = "None";
}
writer.println("Default SMS app: " + defaultSmsApp);
// Now dump logs
LogUtil.dump(writer);
}
@Override
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +28,6 @@ import com.android.messaging.datamodel.action.SyncMessagesAction;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.BuglePrefsKeys;
@@ -206,13 +206,11 @@ public class SyncManager {
* @return 0 if allowed to run now, else delay in ms
*/
public long delayUntilFullSync(final long startTimestamp) {
final BugleGservices bugleGservices = BugleGservices.get();
final BuglePrefs prefs = BuglePrefs.getApplicationPrefs();
final long lastFullSyncTime = prefs.getLong(BuglePrefsKeys.LAST_FULL_SYNC_TIME, -1L);
final long smsFullSyncBackoffTimeMillis = bugleGservices.getLong(
BugleGservicesKeys.SMS_FULL_SYNC_BACKOFF_TIME_MILLIS,
BugleGservicesKeys.SMS_FULL_SYNC_BACKOFF_TIME_MILLIS_DEFAULT);
final long smsFullSyncBackoffTimeMillis =
BugleGservicesKeys.SMS_FULL_SYNC_BACKOFF_TIME_MILLIS_DEFAULT;
final long noFullSyncBefore = (lastFullSyncTime < 0 ? startTimestamp :
lastFullSyncTime + smsFullSyncBackoffTimeMillis);
@@ -1,127 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.datamodel.action;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import com.android.messaging.Factory;
import com.android.messaging.datamodel.DatabaseHelper;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.LogUtil;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DumpDatabaseAction extends Action implements Parcelable {
private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
public static final String DUMP_NAME = "db_copy.db";
private static final int BUFFER_SIZE = 16384;
/**
* Copy the database to external storage
*/
public static void dumpDatabase() {
final DumpDatabaseAction action = new DumpDatabaseAction();
action.start();
}
private DumpDatabaseAction() {
}
@Override
protected Object executeAction() {
final Context context = Factory.get().getApplicationContext();
final String dbName = DatabaseHelper.DATABASE_NAME;
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
long originalSize = 0;
final File inFile = context.getDatabasePath(dbName);
if (inFile.exists() && inFile.isFile()) {
originalSize = inFile.length();
}
final File outFile = DebugUtils.getDebugFile(DUMP_NAME, true);
if (outFile != null) {
int totalBytes = 0;
try {
bos = new BufferedOutputStream(new FileOutputStream(outFile));
bis = new BufferedInputStream(new FileInputStream(inFile));
final byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = bis.read(buffer)) > 0) {
bos.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
}
} catch (final IOException e) {
LogUtil.w(TAG, "Exception copying the database;"
+ " destination may not be complete.", e);
} finally {
if (bos != null) {
try {
bos.close();
} catch (final IOException e) {
// Nothing to do
}
}
if (bis != null) {
try {
bis.close();
} catch (final IOException e) {
// Nothing to do
}
}
DebugUtils.ensureReadable(outFile);
LogUtil.i(TAG, "Dump complete; orig size: " + originalSize +
", copy size: " + totalBytes);
}
}
return null;
}
private DumpDatabaseAction(final Parcel in) {
super(in);
}
public static final Parcelable.Creator<DumpDatabaseAction> CREATOR
= new Parcelable.Creator<DumpDatabaseAction>() {
@Override
public DumpDatabaseAction createFromParcel(final Parcel in) {
return new DumpDatabaseAction(in);
}
@Override
public DumpDatabaseAction[] newArray(final int size) {
return new DumpDatabaseAction[size];
}
};
@Override
public void writeToParcel(@NonNull final Parcel parcel, final int flags) {
writeActionToParcel(parcel, flags);
}
}
@@ -1,156 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.datamodel.action;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony.Threads;
import android.provider.Telephony.ThreadsColumns;
import androidx.annotation.NonNull;
import com.android.messaging.Factory;
import com.android.messaging.mmslib.SqliteWrapper;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.LogUtil;
public class LogTelephonyDatabaseAction extends Action implements Parcelable {
// Because we use sanitizePII, we should also use BUGLE_TAG
private static final String TAG = LogUtil.BUGLE_TAG;
private static final String[] ALL_THREADS_PROJECTION = {
Threads._ID,
Threads.DATE,
Threads.MESSAGE_COUNT,
Threads.RECIPIENT_IDS,
Threads.SNIPPET,
Threads.SNIPPET_CHARSET,
Threads.READ,
Threads.ERROR,
Threads.HAS_ATTACHMENT };
// Constants from the Telephony Database
private static final int ID = 0;
private static final int DATE = 1;
private static final int MESSAGE_COUNT = 2;
private static final int RECIPIENT_IDS = 3;
private static final int SNIPPET = 4;
private static final int SNIPPET_CHAR_SET = 5;
private static final int READ = 6;
private static final int ERROR = 7;
private static final int HAS_ATTACHMENT = 8;
/**
* Log telephony data to logcat
*/
public static void dumpDatabase() {
final LogTelephonyDatabaseAction action = new LogTelephonyDatabaseAction();
action.start();
}
private LogTelephonyDatabaseAction() {
}
@Override
protected Object executeAction() {
final Context context = Factory.get().getApplicationContext();
if (!DebugUtils.isDebugEnabled()) {
LogUtil.e(TAG, "Can't log telephony database unless debugging is enabled");
return null;
}
if (!LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
LogUtil.w(TAG, "Can't log telephony database unless DEBUG is turned on for TAG: " +
TAG);
return null;
}
LogUtil.d(TAG, "\n");
LogUtil.d(TAG, "Dump of canoncial_addresses table");
LogUtil.d(TAG, "*********************************");
Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
Uri.parse("content://mms-sms/canonical-addresses"), null, null, null, null);
if (cursor == null) {
LogUtil.w(TAG, "null Cursor in content://mms-sms/canonical-addresses");
} else {
try {
while (cursor.moveToNext()) {
long id = cursor.getLong(0);
String number = cursor.getString(1);
LogUtil.d(TAG, LogUtil.sanitizePII("id: " + id + " number: " + number));
}
} finally {
cursor.close();
}
}
LogUtil.d(TAG, "\n");
LogUtil.d(TAG, "Dump of threads table");
LogUtil.d(TAG, "*********************");
cursor = SqliteWrapper.query(context, context.getContentResolver(),
Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build(),
ALL_THREADS_PROJECTION, null, null, "date ASC");
try {
while (cursor.moveToNext()) {
LogUtil.d(TAG, LogUtil.sanitizePII("threadId: " + cursor.getLong(ID) +
" " + ThreadsColumns.DATE + " : " + cursor.getLong(DATE) +
" " + ThreadsColumns.MESSAGE_COUNT + " : " + cursor.getInt(MESSAGE_COUNT) +
" " + ThreadsColumns.SNIPPET + " : " + cursor.getString(SNIPPET) +
" " + ThreadsColumns.READ + " : " + cursor.getInt(READ) +
" " + ThreadsColumns.ERROR + " : " + cursor.getInt(ERROR) +
" " + ThreadsColumns.HAS_ATTACHMENT + " : " +
cursor.getInt(HAS_ATTACHMENT) +
" " + ThreadsColumns.RECIPIENT_IDS + " : " +
cursor.getString(RECIPIENT_IDS)));
}
} finally {
cursor.close();
}
return null;
}
private LogTelephonyDatabaseAction(final Parcel in) {
super(in);
}
public static final Parcelable.Creator<LogTelephonyDatabaseAction> CREATOR
= new Parcelable.Creator<LogTelephonyDatabaseAction>() {
@Override
public LogTelephonyDatabaseAction createFromParcel(final Parcel in) {
return new LogTelephonyDatabaseAction(in);
}
@Override
public LogTelephonyDatabaseAction[] newArray(final int size) {
return new LogTelephonyDatabaseAction[size];
}
};
@Override
public void writeToParcel(@NonNull final Parcel parcel, final int flags) {
writeActionToParcel(parcel, flags);
}
}
@@ -271,9 +271,6 @@ public class ProcessDownloadedMmsAction extends Action {
if (downloadedData != null) {
final RetrieveConf retrieveConf =
MmsSender.parseRetrieveConf(downloadedData, subId);
if (MmsUtils.isDumpMmsEnabled()) {
MmsUtils.dumpPdu(downloadedData, retrieveConf);
}
if (retrieveConf != null) {
// Insert the downloaded MMS into telephony
final Uri notificationUri = actionParameters.getParcelable(
@@ -35,7 +35,6 @@ import com.android.messaging.datamodel.DatabaseWrapper;
import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.data.MessageData;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.BuglePrefsKeys;
@@ -153,12 +152,8 @@ public class ProcessPendingMessagesAction extends Action implements Parcelable {
final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
action.actionParameters.putInt(KEY_SUB_ID, subId);
final long initialBackoffMs = BugleGservices.get().getLong(
BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS,
BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS_DEFAULT);
final long maxDelayMs = BugleGservices.get().getLong(
BugleGservicesKeys.MAX_MESSAGE_RESEND_DELAY_MS,
BugleGservicesKeys.MAX_MESSAGE_RESEND_DELAY_MS_DEFAULT);
final long initialBackoffMs = BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS_DEFAULT;
final long maxDelayMs = BugleGservicesKeys.MAX_MESSAGE_RESEND_DELAY_MS_DEFAULT;
long delayMs;
long nextDelayMs = initialBackoffMs;
do {
@@ -43,7 +43,6 @@ import com.android.messaging.sms.DatabaseMessages.MmsMessage;
import com.android.messaging.sms.DatabaseMessages.SmsMessage;
import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.BuglePrefsKeys;
@@ -76,10 +75,8 @@ public class SyncMessagesAction extends Action implements Parcelable {
* Start a full sync (backed off a few seconds to avoid pulling sending/receiving messages).
*/
public static void fullSync() {
final BugleGservices bugleGservices = BugleGservices.get();
final long smsSyncBackoffTimeMillis = bugleGservices.getLong(
BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS,
BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS_DEFAULT);
final long smsSyncBackoffTimeMillis =
BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS_DEFAULT;
final long now = System.currentTimeMillis();
// TODO: Could base this off most recent message in db but now should be okay...
@@ -94,10 +91,8 @@ public class SyncMessagesAction extends Action implements Parcelable {
* Start an incremental sync to pull messages since last sync (backed off a few seconds)..
*/
public static void sync() {
final BugleGservices bugleGservices = BugleGservices.get();
final long smsSyncBackoffTimeMillis = bugleGservices.getLong(
BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS,
BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS_DEFAULT);
final long smsSyncBackoffTimeMillis =
BugleGservicesKeys.SMS_SYNC_BACKOFF_TIME_MILLIS_DEFAULT;
final long now = System.currentTimeMillis();
// TODO: Could base this off most recent message in db but now should be okay...
@@ -195,20 +190,16 @@ public class SyncMessagesAction extends Action implements Parcelable {
@Override
protected Bundle doBackgroundWork() {
final BugleGservices bugleGservices = BugleGservices.get();
final DatabaseWrapper db = DataModel.get().getDatabase();
final int maxMessagesToScan = bugleGservices.getInt(
BugleGservicesKeys.SMS_SYNC_BATCH_MAX_MESSAGES_TO_SCAN,
BugleGservicesKeys.SMS_SYNC_BATCH_MAX_MESSAGES_TO_SCAN_DEFAULT);
final int maxMessagesToScan =
BugleGservicesKeys.SMS_SYNC_BATCH_MAX_MESSAGES_TO_SCAN_DEFAULT;
final int initialMaxMessagesToUpdate = actionParameters.getInt(KEY_MAX_UPDATE);
final int smsSyncSubsequentBatchSizeMin = bugleGservices.getInt(
BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MIN,
BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MIN_DEFAULT);
final int smsSyncSubsequentBatchSizeMax = bugleGservices.getInt(
BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MAX,
BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MAX_DEFAULT);
final int smsSyncSubsequentBatchSizeMin =
BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MIN_DEFAULT;
final int smsSyncSubsequentBatchSizeMax =
BugleGservicesKeys.SMS_SYNC_BATCH_SIZE_MAX_DEFAULT;
// Cap sync size to GServices limits
final int maxMessagesToUpdate = Math.max(smsSyncSubsequentBatchSizeMin,
@@ -512,10 +503,8 @@ public class SyncMessagesAction extends Action implements Parcelable {
* @return Target number of messages to sync for next batch
*/
private static int nextBatchSize(final int messagesUpdated, final long txnTimeMillis) {
final BugleGservices bugleGservices = BugleGservices.get();
final long smsSyncSubsequentBatchTimeLimitMillis = bugleGservices.getLong(
BugleGservicesKeys.SMS_SYNC_BATCH_TIME_LIMIT_MILLIS,
BugleGservicesKeys.SMS_SYNC_BATCH_TIME_LIMIT_MILLIS_DEFAULT);
final long smsSyncSubsequentBatchTimeLimitMillis =
BugleGservicesKeys.SMS_SYNC_BATCH_TIME_LIMIT_MILLIS_DEFAULT;
if (txnTimeMillis <= 0) {
return 0;
@@ -30,7 +30,6 @@ import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
import com.android.messaging.datamodel.DatabaseHelper.PartColumns;
import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.ContentType;
import com.android.messaging.util.Dates;
@@ -396,9 +395,7 @@ public class ConversationMessageData {
if (!TextUtils.isEmpty(firstTextPart)) {
sb.append(firstTextPart);
}
separator = BugleGservices.get().getString(
BugleGservicesKeys.MMS_TEXT_CONCAT_SEPARATOR,
BugleGservicesKeys.MMS_TEXT_CONCAT_SEPARATOR_DEFAULT);
separator = BugleGservicesKeys.MMS_TEXT_CONCAT_SEPARATOR_DEFAULT;
}
final String partText = part.getText();
if (!TextUtils.isEmpty(partText)) {
@@ -34,7 +34,6 @@ import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.Assert;
import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
import com.android.messaging.util.Assert.RunsOnMainThread;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.PhoneUtils;
@@ -471,9 +470,7 @@ public class DraftMessageData extends BindableData implements ReadDraftDataActio
}
private int getAttachmentLimit() {
return BugleGservices.get().getInt(
BugleGservicesKeys.MMS_ATTACHMENT_LIMIT,
BugleGservicesKeys.MMS_ATTACHMENT_LIMIT_DEFAULT);
return BugleGservicesKeys.MMS_ATTACHMENT_LIMIT_DEFAULT;
}
public void removeAttachment(final MessagePartData attachment) {
@@ -32,10 +32,8 @@ import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
import com.android.messaging.datamodel.DatabaseWrapper;
import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.Dates;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.OsUtil;
import java.util.ArrayList;
@@ -564,17 +562,13 @@ public class MessageData implements Parcelable {
}
public final boolean getInResendWindow(final long now) {
final long maxAgeToResend = BugleGservices.get().getLong(
BugleGservicesKeys.MESSAGE_RESEND_TIMEOUT_MS,
BugleGservicesKeys.MESSAGE_RESEND_TIMEOUT_MS_DEFAULT);
final long maxAgeToResend = BugleGservicesKeys.MESSAGE_RESEND_TIMEOUT_MS_DEFAULT;
final long age = now - mRetryStartTimestamp;
return age < maxAgeToResend;
}
public final boolean getInDownloadWindow(final long now) {
final long maxAgeToRedownload = BugleGservices.get().getLong(
BugleGservicesKeys.MESSAGE_DOWNLOAD_TIMEOUT_MS,
BugleGservicesKeys.MESSAGE_DOWNLOAD_TIMEOUT_MS_DEFAULT);
final long maxAgeToRedownload = BugleGservicesKeys.MESSAGE_DOWNLOAD_TIMEOUT_MS_DEFAULT;
final long age = now - mRetryStartTimestamp;
return age < maxAgeToRedownload;
}
@@ -586,11 +580,9 @@ public class MessageData implements Parcelable {
return false;
}
// Should show option for manual download if status is manual download or failed
// If debug is enabled, allow to download an expired or unavailable message.
return (status == BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED ||
status == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD ||
// If debug is enabled, allow to download an expired or unavailable message.
(DebugUtils.isDebugEnabled()
&& status == BUGLE_STATUS_INCOMING_EXPIRED_OR_NOT_AVAILABLE));
status == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD);
}
public boolean canDownloadMessage() {
@@ -611,11 +603,9 @@ public class MessageData implements Parcelable {
return false;
}
// Can redownload if status is manual download not started or download failed
// If debug is enabled, allow to download an expired or unavailable message.
return (mStatus == BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED ||
mStatus == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD ||
// If debug is enabled, allow to download an expired or unavailable message.
(DebugUtils.isDebugEnabled()
&& mStatus == BUGLE_STATUS_INCOMING_EXPIRED_OR_NOT_AVAILABLE));
mStatus == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD);
}
static boolean getShowResendMessage(final int status) {
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +35,6 @@ import androidx.core.app.NotificationManagerCompat;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import com.android.messaging.Factory;
import com.android.messaging.R;
@@ -44,9 +44,6 @@ import com.android.messaging.datamodel.NoConfirmationSmsSendService;
import com.android.messaging.datamodel.action.ReceiveSmsMessageAction;
import com.android.messaging.sms.MmsUtils;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.NotificationsUtil;
import com.android.messaging.util.OsUtil;
@@ -125,10 +122,6 @@ public final class SmsReceiver extends BroadcastReceiver {
int subId = PhoneUtils.getDefault().getEffectiveIncomingSubIdFromSystem(
intent, EXTRA_SUB_ID);
deliverSmsMessages(context, subId, errorCode, messages);
if (MmsUtils.isDumpSmsEnabled()) {
final String format = intent.getStringExtra("format");
DebugUtils.dumpSms(messages[0].getTimestampMillis(), messages, format);
}
}
public static void deliverSmsMessages(final Context context, final int subId,
@@ -148,8 +141,7 @@ public final class SmsReceiver extends BroadcastReceiver {
messageValues.put(Sms.Inbox.SEEN, 0);
messageValues.put(Sms.SUBSCRIPTION_ID, subId);
if (messages[0].getMessageClass() == android.telephony.SmsMessage.MessageClass.CLASS_0 ||
DebugUtils.debugClassZeroSmsEnabled()) {
if (messages[0].getMessageClass() == android.telephony.SmsMessage.MessageClass.CLASS_0) {
Factory.get().getUIIntents().launchClassZeroActivity(context, messageValues);
} else {
final ReceiveSmsMessageAction action = new ReceiveSmsMessageAction(messageValues);
@@ -228,30 +220,6 @@ public final class SmsReceiver extends BroadcastReceiver {
return Factory.get().getApplicationContext().getPackageName() + ":secondaryuser";
}
/**
* Compile all of the patterns we check for to ignore system SMS messages.
*/
private static void compileIgnoreSmsPatterns() {
// Get the pattern set from GServices
final String smsIgnoreRegex = BugleGservices.get().getString(
BugleGservicesKeys.SMS_IGNORE_MESSAGE_REGEX,
BugleGservicesKeys.SMS_IGNORE_MESSAGE_REGEX_DEFAULT);
if (smsIgnoreRegex != null) {
final String[] ignoreSmsExpressions = smsIgnoreRegex.split("\n");
if (ignoreSmsExpressions.length != 0) {
sIgnoreSmsPatterns = new ArrayList<Pattern>();
for (int i = 0; i < ignoreSmsExpressions.length; i++) {
try {
sIgnoreSmsPatterns.add(Pattern.compile(ignoreSmsExpressions[i]));
} catch (PatternSyntaxException e) {
LogUtil.e(TAG, "compileIgnoreSmsPatterns: Skipping bad expression: " +
ignoreSmsExpressions[i]);
}
}
}
}
}
/**
* Get the SMS messages from the specified SMS intent.
* @return the messages. If there is an error or the message should be ignored, return null.
@@ -263,27 +231,6 @@ public final class SmsReceiver extends BroadcastReceiver {
if (messages == null || messages.length < 1) {
return null;
}
// Sometimes, SmsMessage.mWrappedSmsMessage is null causing NPE when we access
// the methods on it although the SmsMessage itself is not null. So do this check
// before we do anything on the parsed SmsMessages.
try {
final String messageBody = messages[0].getDisplayMessageBody();
if (messageBody != null) {
// Compile patterns if necessary
if (sIgnoreSmsPatterns == null) {
compileIgnoreSmsPatterns();
}
// Check against filters
for (final Pattern pattern : sIgnoreSmsPatterns) {
if (pattern.matcher(messageBody).matches()) {
return null;
}
}
}
} catch (final NullPointerException e) {
LogUtil.e(TAG, "shouldIgnoreMessage: NPE inside SmsMessage");
return null;
}
return messages;
}
@@ -31,8 +31,6 @@ import android.util.SparseArray;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.mmslib.SqliteWrapper;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.PhoneUtils;
@@ -336,11 +334,6 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
}
private void loadLocked(final int subId, final String apnName, final List<Apn> apns) {
// Try Gservices first
loadFromGservices(apns);
if (apns.size() > 0) {
return;
}
// Try system APN table
loadFromSystem(subId, apnName, apns);
if (apns.size() > 0) {
@@ -353,26 +346,6 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
}
}
/**
* Load from Gservices if APN setting is set in Gservices
*
* @param apns the list used to return results
*/
private void loadFromGservices(final List<Apn> apns) {
final BugleGservices gservices = BugleGservices.get();
final String mmsc = gservices.getString(BugleGservicesKeys.MMS_MMSC, null);
if (TextUtils.isEmpty(mmsc)) {
return;
}
LogUtil.i(LogUtil.BUGLE_TAG, "Loading APNs from gservices");
final String proxy = gservices.getString(BugleGservicesKeys.MMS_PROXY_ADDRESS, null);
final int port = gservices.getInt(BugleGservicesKeys.MMS_PROXY_PORT, -1);
final Apn apn = BaseApn.from("mms", mmsc, proxy, Integer.toString(port));
if (apn != null) {
apns.add(apn);
}
}
/**
* Load matching APNs from telephony provider.
* We try different combinations of the query to work around some platform quirks.
@@ -22,7 +22,6 @@ import android.support.v7.mms.UserAgentInfoLoader;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.VersionUtil;
@@ -86,9 +85,7 @@ public class BugleUserAgentInfoLoader implements UserAgentInfoLoader {
}
// if the UAProfUrl isn't set, get it from Gservices
if (TextUtils.isEmpty(mUAProfUrl)) {
mUAProfUrl = BugleGservices.get().getString(
BugleGservicesKeys.MMS_UA_PROFILE_URL,
BugleGservicesKeys.MMS_UA_PROFILE_URL_DEFAULT);
mUAProfUrl = BugleGservicesKeys.MMS_UA_PROFILE_URL_DEFAULT;
}
}
}
+24 -79
View File
@@ -67,7 +67,6 @@ import com.android.messaging.mmslib.pdu.SendConf;
import com.android.messaging.mmslib.pdu.SendReq;
import com.android.messaging.sms.SmsSender.SendResult;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.ContentType;
@@ -1538,44 +1537,6 @@ public class MmsUtils {
sUseSystemApn = turnOn;
}
/**
* Checks if we should dump sms, based on both the setting and the global debug
* flag
*
* @return if dump sms is enabled
*/
public static boolean isDumpSmsEnabled() {
if (!DebugUtils.isDebugEnabled()) {
return false;
}
return getDumpSmsOrMmsPref(R.string.dump_sms_pref_key, R.bool.dump_sms_pref_default);
}
/**
* Checks if we should dump mms, based on both the setting and the global debug
* flag
*
* @return if dump mms is enabled
*/
public static boolean isDumpMmsEnabled() {
if (!DebugUtils.isDebugEnabled()) {
return false;
}
return getDumpSmsOrMmsPref(R.string.dump_mms_pref_key, R.bool.dump_mms_pref_default);
}
/**
* Load the value of dump sms or mms setting preference
*/
private static boolean getDumpSmsOrMmsPref(final int prefKeyRes, final int defaultKeyRes) {
final Context context = Factory.get().getApplicationContext();
final Resources resources = context.getResources();
final BuglePrefs prefs = BuglePrefs.getApplicationPrefs();
final String key = resources.getString(prefKeyRes);
final boolean defaultValue = resources.getBoolean(defaultKeyRes);
return prefs.getBoolean(key, defaultValue);
}
public static final Uri MMS_PART_CONTENT_URI = Uri.parse("content://mms/part");
/**
@@ -1731,9 +1692,7 @@ public class MmsUtils {
public static MessagePartData createMmsMessagePart(final DatabaseMessages.MmsPart part) {
MessagePartData messagePart = null;
if (part.isText()) {
final int mmsTextLengthLimit =
BugleGservices.get().getInt(BugleGservicesKeys.MMS_TEXT_LIMIT,
BugleGservicesKeys.MMS_TEXT_LIMIT_DEFAULT);
final int mmsTextLengthLimit = BugleGservicesKeys.MMS_TEXT_LIMIT_DEFAULT;
String text = part.mText;
if (text != null && text.length() > mmsTextLengthLimit) {
// Limit the text to a reasonable value. We ran into a situation where a vcard
@@ -1806,44 +1765,30 @@ public class MmsUtils {
int status = MMS_REQUEST_MANUAL_RETRY;
try {
RetrieveConf retrieveConf = null;
if (DebugUtils.isDebugEnabled() &&
MediaScratchFileProvider
.isMediaScratchSpaceUri(Uri.parse(contentLocation))) {
if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
LogUtil.d(TAG, "MmsUtils: Reading MMS from dump file: " + contentLocation);
}
final String fileName = Uri.parse(contentLocation).getPathSegments().get(1);
final byte[] data = DebugUtils.receiveFromDumpFile(fileName);
retrieveConf = receiveFromDumpFile(data);
} else {
if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
LogUtil.d(TAG, "MmsUtils: Downloading MMS via MMS lib API; notification "
+ "message: " + notificationUri);
}
if (subId < 0) {
LogUtil.e(TAG, "MmsUtils: Incoming MMS came from unknown SIM");
throw new MmsFailureException(MMS_REQUEST_NO_RETRY,
"Message from unknown SIM");
}
if (extras == null) {
extras = new Bundle();
}
extras.putParcelable(DownloadMmsAction.EXTRA_NOTIFICATION_URI, notificationUri);
extras.putInt(DownloadMmsAction.EXTRA_SUB_ID, subId);
extras.putString(DownloadMmsAction.EXTRA_SUB_PHONE_NUMBER, subPhoneNumber);
extras.putString(DownloadMmsAction.EXTRA_TRANSACTION_ID, transactionId);
extras.putString(DownloadMmsAction.EXTRA_CONTENT_LOCATION, contentLocation);
extras.putBoolean(DownloadMmsAction.EXTRA_AUTO_DOWNLOAD, autoDownload);
extras.putLong(DownloadMmsAction.EXTRA_RECEIVED_TIMESTAMP,
receivedTimestampInSeconds);
extras.putLong(DownloadMmsAction.EXTRA_EXPIRY, expiry);
MmsSender.downloadMms(context, subId, contentLocation, extras);
return STATUS_PENDING; // Download happens asynchronously; no status to return
if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
LogUtil.d(TAG, "MmsUtils: Downloading MMS via MMS lib API; notification "
+ "message: " + notificationUri);
}
return insertDownloadedMessageAndSendResponse(context, notificationUri, subId,
subPhoneNumber, transactionId, contentLocation, autoDownload,
receivedTimestampInSeconds, expiry, retrieveConf);
if (subId < 0) {
LogUtil.e(TAG, "MmsUtils: Incoming MMS came from unknown SIM");
throw new MmsFailureException(MMS_REQUEST_NO_RETRY,
"Message from unknown SIM");
}
if (extras == null) {
extras = new Bundle();
}
extras.putParcelable(DownloadMmsAction.EXTRA_NOTIFICATION_URI, notificationUri);
extras.putInt(DownloadMmsAction.EXTRA_SUB_ID, subId);
extras.putString(DownloadMmsAction.EXTRA_SUB_PHONE_NUMBER, subPhoneNumber);
extras.putString(DownloadMmsAction.EXTRA_TRANSACTION_ID, transactionId);
extras.putString(DownloadMmsAction.EXTRA_CONTENT_LOCATION, contentLocation);
extras.putBoolean(DownloadMmsAction.EXTRA_AUTO_DOWNLOAD, autoDownload);
extras.putLong(DownloadMmsAction.EXTRA_RECEIVED_TIMESTAMP,
receivedTimestampInSeconds);
extras.putLong(DownloadMmsAction.EXTRA_EXPIRY, expiry);
MmsSender.downloadMms(context, subId, contentLocation, extras);
return STATUS_PENDING; // Download happens asynchronously; no status to return
} catch (final MmsFailureException e) {
LogUtil.e(TAG, "MmsUtils: failed to download message " + notificationUri, e);
+2 -4
View File
@@ -33,7 +33,6 @@ import com.android.messaging.Factory;
import com.android.messaging.R;
import com.android.messaging.receiver.SendStatusReceiver;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.PhoneUtils;
@@ -225,9 +224,8 @@ public class SmsSender {
context, subId, dest, messages, serviceCenter, requireDeliveryReport, messageUri);
// Wait for pending intent to come back
synchronized (pendingResult) {
final long smsSendTimeoutInMillis = BugleGservices.get().getLong(
BugleGservicesKeys.SMS_SEND_TIMEOUT_IN_MILLIS,
BugleGservicesKeys.SMS_SEND_TIMEOUT_IN_MILLIS_DEFAULT);
final long smsSendTimeoutInMillis =
BugleGservicesKeys.SMS_SEND_TIMEOUT_IN_MILLIS_DEFAULT;
final long beginTime = SystemClock.elapsedRealtime();
long waitTime = smsSendTimeoutInMillis;
// We could possibly be woken up while still pending
@@ -154,11 +154,6 @@ public abstract class UIIntents {
public abstract void launchCreateNewConversationActivity(final Context context,
final MessageData draft);
/**
* Launch debug activity to set MMS config options.
*/
public abstract void launchDebugMmsConfigActivity(final Context context);
/**
* Launch an activity to change settings.
*/
@@ -325,16 +320,6 @@ public abstract class UIIntents {
public abstract PendingIntent getPendingIntentForSecondaryUserNewMessageNotification(
final Context context);
/**
* Get an intent for showing the APN editor.
*/
public abstract Intent getApnEditorIntent(final Context context, final String rowId, int subId);
/**
* Get an intent for showing the APN settings.
*/
public abstract Intent getApnSettingsIntent(final Context context, final int subId);
/**
* Get an intent for showing advanced settings.
*/
@@ -23,7 +23,6 @@ import android.app.role.RoleManager;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -34,7 +33,7 @@ import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
import android.provider.MediaStore;
import android.provider.Telephony;
import androidx.annotation.Nullable;
import androidx.core.app.TaskStackBuilder;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@@ -50,8 +49,6 @@ import com.android.messaging.datamodel.data.MessagePartData;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.receiver.NotificationReceiver;
import com.android.messaging.sms.MmsSmsUtils;
import com.android.messaging.ui.appsettings.ApnEditorActivity;
import com.android.messaging.ui.appsettings.ApnSettingsActivity;
import com.android.messaging.ui.appsettings.ApplicationSettingsActivity;
import com.android.messaging.ui.appsettings.PerSubscriptionSettingsActivity;
import com.android.messaging.ui.appsettings.SettingsActivity;
@@ -62,7 +59,6 @@ import com.android.messaging.ui.conversationlist.ArchivedConversationListActivit
import com.android.messaging.ui.conversationlist.ConversationListActivity;
import com.android.messaging.ui.conversationlist.ForwardMessageActivity;
import com.android.messaging.ui.conversationsettings.PeopleAndOptionsActivity;
import com.android.messaging.ui.debug.DebugMmsConfigActivity;
import com.android.messaging.ui.photoviewer.BuglePhotoViewActivity;
import com.android.messaging.util.Assert;
import com.android.messaging.util.ContentType;
@@ -192,11 +188,6 @@ public class UIIntentsImpl extends UIIntents {
context.startActivity(intent);
}
@Override
public void launchDebugMmsConfigActivity(final Context context) {
context.startActivity(new Intent(context, DebugMmsConfigActivity.class));
}
@Override
public void launchAddContactActivity(final Context context, final String destination) {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
@@ -449,21 +440,6 @@ public class UIIntentsImpl extends UIIntents {
return getPendingIntentForConversationListActivity(context);
}
@Override
public Intent getApnEditorIntent(final Context context, final String rowId, final int subId) {
final Intent intent = new Intent(context, ApnEditorActivity.class);
intent.putExtra(UI_INTENT_EXTRA_APN_ROW_ID, rowId);
intent.putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
return intent;
}
@Override
public Intent getApnSettingsIntent(final Context context, final int subId) {
final Intent intent = new Intent(context, ApnSettingsActivity.class)
.putExtra(UI_INTENT_EXTRA_SUB_ID, subId);
return intent;
}
@Override
public Intent getAdvancedSettingsIntent(final Context context) {
return getPerSubscriptionSettingsIntent(context, ParticipantData.DEFAULT_SELF_SUB_ID, null);
@@ -1,466 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.appsettings;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.provider.Telephony;
import androidx.annotation.NonNull;
import androidx.core.app.NavUtils;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.sms.ApnDatabase;
import com.android.messaging.sms.BugleApnSettingsLoader;
import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.PhoneUtils;
public class ApnEditorActivity extends BugleActionBarActivity {
private static final int ERROR_DIALOG_ID = 0;
private static final String ERROR_MESSAGE_KEY = "error_msg";
private ApnEditorFragment mApnEditorFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Display the fragment as the main content.
mApnEditorFragment = new ApnEditorFragment();
mApnEditorFragment.setSubId(getIntent().getIntExtra(UIIntents.UI_INTENT_EXTRA_SUB_ID,
ParticipantData.DEFAULT_SELF_SUB_ID));
getFragmentManager().beginTransaction()
.replace(android.R.id.content, mApnEditorFragment)
.commit();
}
@Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected Dialog onCreateDialog(int id, Bundle args) {
if (id == ERROR_DIALOG_ID) {
String msg = args.getString(ERROR_MESSAGE_KEY);
return new AlertDialog.Builder(this)
.setPositiveButton(android.R.string.ok, null)
.setMessage(msg)
.create();
}
return super.onCreateDialog(id);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
if (mApnEditorFragment.validateAndSave(false)) {
finish();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
super.onPrepareDialog(id, dialog);
if (id == ERROR_DIALOG_ID) {
final String msg = args.getString(ERROR_MESSAGE_KEY);
if (msg != null) {
((AlertDialog) dialog).setMessage(msg);
}
}
}
public static class ApnEditorFragment extends PreferenceFragment implements
SharedPreferences.OnSharedPreferenceChangeListener {
private static final String SAVED_POS = "pos";
private static final int MENU_DELETE = Menu.FIRST;
private static final int MENU_SAVE = Menu.FIRST + 1;
private static final int MENU_CANCEL = Menu.FIRST + 2;
private EditTextPreference mMmsProxy;
private EditTextPreference mMmsPort;
private EditTextPreference mName;
private EditTextPreference mMmsc;
private EditTextPreference mMcc;
private EditTextPreference mMnc;
private static String sNotSet;
private String mCurMnc;
private String mCurMcc;
private Cursor mCursor;
private boolean mNewApn;
private boolean mFirstTime;
private String mCurrentId;
private int mSubId;
/**
* Standard projection for the interesting columns of a normal note.
*/
private static final String[] sProjection = new String[] {
Telephony.Carriers._ID, // 0
Telephony.Carriers.NAME, // 1
Telephony.Carriers.MMSC, // 2
Telephony.Carriers.MCC, // 3
Telephony.Carriers.MNC, // 4
Telephony.Carriers.NUMERIC, // 5
Telephony.Carriers.MMSPROXY, // 6
Telephony.Carriers.MMSPORT, // 7
Telephony.Carriers.TYPE, // 8
};
private static final int ID_INDEX = 0;
private static final int NAME_INDEX = 1;
private static final int MMSC_INDEX = 2;
private static final int MCC_INDEX = 3;
private static final int MNC_INDEX = 4;
private static final int NUMERIC_INDEX = 5;
private static final int MMSPROXY_INDEX = 6;
private static final int MMSPORT_INDEX = 7;
private static final int TYPE_INDEX = 8;
private SQLiteDatabase mDatabase;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
addPreferencesFromResource(R.xml.apn_editor);
setHasOptionsMenu(true);
sNotSet = getResources().getString(R.string.apn_not_set);
mName = (EditTextPreference) findPreference("apn_name");
mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
mMmsc = (EditTextPreference) findPreference("apn_mmsc");
mMcc = (EditTextPreference) findPreference("apn_mcc");
mMnc = (EditTextPreference) findPreference("apn_mnc");
final Intent intent = getActivity().getIntent();
mFirstTime = savedInstanceState == null;
mCurrentId = intent.getStringExtra(UIIntents.UI_INTENT_EXTRA_APN_ROW_ID);
mNewApn = mCurrentId == null;
mDatabase = ApnDatabase.getApnDatabase().getWritableDatabase();
if (mNewApn) {
fillUi();
} else {
// Do initial query not on the UI thread
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
if (mCurrentId != null) {
String selection = Telephony.Carriers._ID + " =?";
String[] selectionArgs = new String[]{ mCurrentId };
mCursor = mDatabase.query(ApnDatabase.APN_TABLE, sProjection, selection,
selectionArgs, null, null, null, null);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (mCursor == null) {
getActivity().finish();
return;
}
mCursor.moveToFirst();
fillUi();
}
}.execute((Void) null);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mCursor != null) {
mCursor.close();
mCursor = null;
}
}
@Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
super.onPause();
}
public void setSubId(final int subId) {
mSubId = subId;
}
private void fillUi() {
if (mNewApn) {
mMcc.setText(null);
mMnc.setText(null);
String numeric = PhoneUtils.get(mSubId).getSimOperatorNumeric();
// MCC is first 3 chars and then in 2 - 3 chars of MNC
if (numeric != null && numeric.length() > 4) {
// Country code
String mcc = numeric.substring(0, 3);
// Network code
String mnc = numeric.substring(3);
// Auto populate MNC and MCC for new entries, based on what SIM reports
mMcc.setText(mcc);
mMnc.setText(mnc);
mCurMnc = mnc;
mCurMcc = mcc;
}
mName.setText(null);
mMmsProxy.setText(null);
mMmsPort.setText(null);
mMmsc.setText(null);
} else if (mFirstTime) {
mFirstTime = false;
// Fill in all the values from the db in both text editor and summary
mName.setText(mCursor.getString(NAME_INDEX));
mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX));
mMmsPort.setText(mCursor.getString(MMSPORT_INDEX));
mMmsc.setText(mCursor.getString(MMSC_INDEX));
mMcc.setText(mCursor.getString(MCC_INDEX));
mMnc.setText(mCursor.getString(MNC_INDEX));
}
mName.setSummary(checkNull(mName.getText()));
mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
mMmsPort.setSummary(checkNull(mMmsPort.getText()));
mMmsc.setSummary(checkNull(mMmsc.getText()));
mMcc.setSummary(checkNull(mMcc.getText()));
mMnc.setSummary(checkNull(mMnc.getText()));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// If it's a new APN, then cancel will delete the new entry in onPause
if (!mNewApn) {
menu.add(0, MENU_DELETE, 0, R.string.menu_delete_apn)
.setIcon(R.drawable.ic_delete_small_dark);
}
menu.add(0, MENU_SAVE, 0, R.string.menu_save_apn)
.setIcon(android.R.drawable.ic_menu_save);
menu.add(0, MENU_CANCEL, 0, R.string.menu_discard_apn_change)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_DELETE:
deleteApn();
return true;
case MENU_SAVE:
if (validateAndSave(false)) {
getActivity().finish();
}
return true;
case MENU_CANCEL:
getActivity().finish();
return true;
case android.R.id.home:
getActivity().onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
if (validateAndSave(true) && mCursor != null) {
icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX));
}
}
/**
* Check the key fields' validity and save if valid.
* @param force save even if the fields are not valid, if the app is
* being suspended
* @return true if the data was saved
*/
private boolean validateAndSave(boolean force) {
final String name = checkNotSet(mName.getText());
final String mcc = checkNotSet(mMcc.getText());
final String mnc = checkNotSet(mMnc.getText());
if (getErrorMsg() != null && !force) {
final Bundle bundle = new Bundle();
bundle.putString(ERROR_MESSAGE_KEY, getErrorMsg());
getActivity().showDialog(ERROR_DIALOG_ID, bundle);
return false;
}
// Make database changes not on the UI thread
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
ContentValues values = new ContentValues();
// Add a placeholder name "Untitled", if the user exits the screen without
// adding a name but entered other information worth keeping.
values.put(Telephony.Carriers.NAME, name.length() < 1 ?
getResources().getString(R.string.untitled_apn) : name);
values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText()));
values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText()));
values.put(Telephony.Carriers.TYPE, BugleApnSettingsLoader.APN_TYPE_MMS);
values.put(Telephony.Carriers.MCC, mcc);
values.put(Telephony.Carriers.MNC, mnc);
values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
if (mCurMnc != null && mCurMcc != null) {
if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
values.put(Telephony.Carriers.CURRENT, 1);
}
}
if (mNewApn) {
mDatabase.insert(ApnDatabase.APN_TABLE, null, values);
} else {
// update the APN
String selection = Telephony.Carriers._ID + " =?";
String[] selectionArgs = new String[]{ mCurrentId };
int updated = mDatabase.update(ApnDatabase.APN_TABLE, values,
selection, selectionArgs);
}
return null;
}
}.execute((Void) null);
return true;
}
private void deleteApn() {
// Make database changes not on the UI thread
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// delete the APN
String where = Telephony.Carriers._ID + " =?";
String[] whereArgs = new String[]{ mCurrentId };
mDatabase.delete(ApnDatabase.APN_TABLE, where, whereArgs);
return null;
}
}.execute((Void) null);
getActivity().finish();
}
private String checkNull(String value) {
if (value == null || value.length() == 0) {
return sNotSet;
} else {
return value;
}
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preference pref = findPreference(key);
if (pref != null) {
pref.setSummary(checkNull(sharedPreferences.getString(key, "")));
}
}
private String getErrorMsg() {
String errorMsg = null;
String name = checkNotSet(mName.getText());
String mcc = checkNotSet(mMcc.getText());
String mnc = checkNotSet(mMnc.getText());
if (name.length() < 1) {
errorMsg = getString(R.string.error_apn_name_empty);
} else if (mcc.length() != 3) {
errorMsg = getString(R.string.error_mcc_not3);
} else if ((mnc.length() & 0xFFFE) != 2) {
errorMsg = getString(R.string.error_mnc_not23);
}
return errorMsg;
}
private String checkNotSet(String value) {
if (value == null || value.equals(sNotSet)) {
return "";
} else {
return value;
}
}
}
}
@@ -1,143 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.appsettings;
import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.ui.UIIntents;
/**
* ApnPreference implements a pref, typically used as a list item, that has a title/summary on
* the left and a radio button on the right.
*
*/
public class ApnPreference extends Preference implements
CompoundButton.OnCheckedChangeListener, OnClickListener {
static final String TAG = "ApnPreference";
public ApnPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ApnPreference(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.apnPreferenceStyle);
}
public ApnPreference(Context context) {
this(context, null);
}
private static String mSelectedKey = null;
private static CompoundButton mCurrentChecked = null;
private boolean mProtectFromCheckedChange = false;
private boolean mSelectable = true;
private int mSubId = ParticipantData.DEFAULT_SELF_SUB_ID;
@Override
public View getView(View convertView, ViewGroup parent) {
View view = super.getView(convertView, parent);
View widget = view.findViewById(R.id.apn_radiobutton);
if ((widget != null) && widget instanceof RadioButton) {
RadioButton rb = (RadioButton) widget;
if (mSelectable) {
rb.setOnCheckedChangeListener(this);
boolean isChecked = getKey().equals(mSelectedKey);
if (isChecked) {
mCurrentChecked = rb;
mSelectedKey = getKey();
}
mProtectFromCheckedChange = true;
rb.setChecked(isChecked);
mProtectFromCheckedChange = false;
} else {
rb.setVisibility(View.GONE);
}
rb.setContentDescription(getTitle());
}
View textLayout = view.findViewById(R.id.text_layout);
if ((textLayout != null) && textLayout instanceof RelativeLayout) {
textLayout.setOnClickListener(this);
}
return view;
}
public boolean isChecked() {
return getKey().equals(mSelectedKey);
}
public void setChecked() {
mSelectedKey = getKey();
}
public void setSubId(final int subId) {
mSubId = subId;
}
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.i(TAG, "ID: " + getKey() + " :" + isChecked);
if (mProtectFromCheckedChange) {
return;
}
if (isChecked) {
if (mCurrentChecked != null) {
mCurrentChecked.setChecked(false);
}
mCurrentChecked = buttonView;
mSelectedKey = getKey();
callChangeListener(mSelectedKey);
} else {
mCurrentChecked = null;
mSelectedKey = null;
}
buttonView.setContentDescription(getTitle());
}
public void onClick(android.view.View v) {
if ((v != null) && (R.id.text_layout == v.getId())) {
Context context = getContext();
if (context != null) {
context.startActivity(
UIIntents.get().getApnEditorIntent(context, getKey(), mSubId));
}
}
}
public void setSelectable(boolean selectable) {
mSelectable = selectable;
}
public boolean getSelectable() {
return mSelectable;
}
}
@@ -1,402 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.appsettings;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.UserManager;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Telephony;
import androidx.annotation.NonNull;
import androidx.core.app.NavUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.sms.ApnDatabase;
import com.android.messaging.sms.BugleApnSettingsLoader;
import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.PhoneUtils;
public class ApnSettingsActivity extends BugleActionBarActivity {
private static final int DIALOG_RESTORE_DEFAULTAPN = 1001;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Display the fragment as the main content.
final ApnSettingsFragment fragment = new ApnSettingsFragment();
fragment.setSubId(getIntent().getIntExtra(UIIntents.UI_INTENT_EXTRA_SUB_ID,
ParticipantData.DEFAULT_SELF_SUB_ID));
getFragmentManager().beginTransaction()
.replace(android.R.id.content, fragment)
.commit();
}
@Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == DIALOG_RESTORE_DEFAULTAPN) {
ProgressDialog dialog = new ProgressDialog(this);
dialog.setMessage(getResources().getString(R.string.restore_default_apn));
dialog.setCancelable(false);
return dialog;
}
return null;
}
public static class ApnSettingsFragment extends PreferenceFragment implements
Preference.OnPreferenceChangeListener {
public static final String EXTRA_POSITION = "position";
public static final String APN_ID = "apn_id";
private static final String[] APN_PROJECTION = {
Telephony.Carriers._ID, // 0
Telephony.Carriers.NAME, // 1
Telephony.Carriers.APN, // 2
Telephony.Carriers.TYPE // 3
};
private static final int ID_INDEX = 0;
private static final int NAME_INDEX = 1;
private static final int APN_INDEX = 2;
private static final int TYPES_INDEX = 3;
private static final int MENU_NEW = Menu.FIRST;
private static final int MENU_RESTORE = Menu.FIRST + 1;
private static final int EVENT_RESTORE_DEFAULTAPN_START = 1;
private static final int EVENT_RESTORE_DEFAULTAPN_COMPLETE = 2;
private static boolean mRestoreDefaultApnMode;
private RestoreApnUiHandler mRestoreApnUiHandler;
private RestoreApnProcessHandler mRestoreApnProcessHandler;
private HandlerThread mRestoreDefaultApnThread;
private String mSelectedKey;
private static final ContentValues sCurrentNullMap;
private static final ContentValues sCurrentSetMap;
private UserManager mUm;
private boolean mUnavailable;
private int mSubId;
static {
sCurrentNullMap = new ContentValues(1);
sCurrentNullMap.putNull(Telephony.Carriers.CURRENT);
sCurrentSetMap = new ContentValues(1);
sCurrentSetMap.put(Telephony.Carriers.CURRENT, "2"); // 2 for user-selected APN,
// 1 for Bugle-selected APN
}
private SQLiteDatabase mDatabase;
public void setSubId(final int subId) {
mSubId = subId;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mDatabase = ApnDatabase.getApnDatabase().getWritableDatabase();
mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
if (!mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
setHasOptionsMenu(true);
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final ListView lv = (ListView) getView().findViewById(android.R.id.list);
TextView empty = (TextView) getView().findViewById(android.R.id.empty);
if (empty != null) {
empty.setText(R.string.apn_settings_not_available);
lv.setEmptyView(empty);
}
if (mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
mUnavailable = true;
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getActivity()));
return;
}
addPreferencesFromResource(R.xml.apn_settings);
lv.setItemsCanFocus(true);
}
@Override
public void onResume() {
super.onResume();
if (mUnavailable) {
return;
}
if (!mRestoreDefaultApnMode) {
fillList();
}
}
@Override
public void onPause() {
super.onPause();
if (mUnavailable) {
return;
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mRestoreDefaultApnThread != null) {
mRestoreDefaultApnThread.quit();
}
}
private void fillList() {
final String mccMnc = PhoneUtils.getMccMncString(PhoneUtils.get(mSubId).getMccMnc());
new AsyncTask<Void, Void, Cursor>() {
@Override
protected Cursor doInBackground(Void... params) {
String selection = Telephony.Carriers.NUMERIC + " =?";
String[] selectionArgs = new String[]{ mccMnc };
final Cursor cursor = mDatabase.query(ApnDatabase.APN_TABLE, APN_PROJECTION,
selection, selectionArgs, null, null, null, null);
return cursor;
}
@Override
protected void onPostExecute(Cursor cursor) {
if (cursor != null) {
try {
PreferenceGroup apnList = (PreferenceGroup)
findPreference(getString(R.string.apn_list_pref_key));
apnList.removeAll();
mSelectedKey = BugleApnSettingsLoader.getFirstTryApn(mDatabase, mccMnc);
while (cursor.moveToNext()) {
String name = cursor.getString(NAME_INDEX);
String apn = cursor.getString(APN_INDEX);
String key = cursor.getString(ID_INDEX);
String type = cursor.getString(TYPES_INDEX);
if (BugleApnSettingsLoader.isValidApnType(type,
BugleApnSettingsLoader.APN_TYPE_MMS)) {
ApnPreference pref = new ApnPreference(getActivity());
pref.setKey(key);
pref.setTitle(name);
pref.setSummary(apn);
pref.setPersistent(false);
pref.setOnPreferenceChangeListener(ApnSettingsFragment.this);
pref.setSelectable(true);
// Turn on the radio button for the currently selected APN. If
// there is no selected APN, don't select an APN.
if ((mSelectedKey != null && mSelectedKey.equals(key))) {
pref.setChecked();
}
apnList.addPreference(pref);
}
}
} finally {
cursor.close();
}
}
}
}.execute((Void) null);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (!mUnavailable) {
menu.add(0, MENU_NEW, 0,
getResources().getString(R.string.menu_new_apn))
.setIcon(R.drawable.ic_add_white)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(0, MENU_RESTORE, 0,
getResources().getString(R.string.menu_restore_default_apn))
.setIcon(android.R.drawable.ic_menu_upload);
}
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_NEW:
addNewApn();
return true;
case MENU_RESTORE:
restoreDefaultApn();
return true;
}
return super.onOptionsItemSelected(item);
}
private void addNewApn() {
startActivity(UIIntents.get().getApnEditorIntent(getActivity(), null, mSubId));
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
startActivity(
UIIntents.get().getApnEditorIntent(getActivity(), preference.getKey(), mSubId));
return true;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue instanceof String) {
setSelectedApnKey((String) newValue);
}
return true;
}
// current=2 means user selected APN
private static final String UPDATE_SELECTION = Telephony.Carriers.CURRENT + " =?";
private static final String[] UPDATE_SELECTION_ARGS = new String[] { "2" };
private void setSelectedApnKey(final String key) {
mSelectedKey = key;
// Make database changes not on the UI thread
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// null out the previous "current=2" APN
mDatabase.update(ApnDatabase.APN_TABLE, sCurrentNullMap,
UPDATE_SELECTION, UPDATE_SELECTION_ARGS);
// set the new "current" APN (2)
String selection = Telephony.Carriers._ID + " =?";
String[] selectionArgs = new String[]{ key };
mDatabase.update(ApnDatabase.APN_TABLE, sCurrentSetMap,
selection, selectionArgs);
return null;
}
}.execute((Void) null);
}
private boolean restoreDefaultApn() {
getActivity().showDialog(DIALOG_RESTORE_DEFAULTAPN);
mRestoreDefaultApnMode = true;
if (mRestoreApnUiHandler == null) {
mRestoreApnUiHandler = new RestoreApnUiHandler();
}
if (mRestoreApnProcessHandler == null ||
mRestoreDefaultApnThread == null) {
mRestoreDefaultApnThread = new HandlerThread(
"Restore default APN Handler: Process Thread");
mRestoreDefaultApnThread.start();
mRestoreApnProcessHandler = new RestoreApnProcessHandler(
mRestoreDefaultApnThread.getLooper(), mRestoreApnUiHandler);
}
mRestoreApnProcessHandler.sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START);
return true;
}
private class RestoreApnUiHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_RESTORE_DEFAULTAPN_COMPLETE:
fillList();
getPreferenceScreen().setEnabled(true);
mRestoreDefaultApnMode = false;
final Activity activity = getActivity();
activity.dismissDialog(DIALOG_RESTORE_DEFAULTAPN);
Toast.makeText(activity, getResources().getString(
R.string.restore_default_apn_completed), Toast.LENGTH_LONG)
.show();
break;
}
}
}
private static class RestoreApnProcessHandler extends Handler {
private final Handler mCachedRestoreApnUiHandler;
public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
super(looper);
this.mCachedRestoreApnUiHandler = restoreApnUiHandler;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_RESTORE_DEFAULTAPN_START:
ApnDatabase.forceBuildAndLoadApnTables();
mCachedRestoreApnUiHandler.sendEmptyMessage(
EVENT_RESTORE_DEFAULTAPN_COMPLETE);
break;
}
}
}
}
}
@@ -38,7 +38,6 @@ import com.android.messaging.ui.BugleActionBarActivity;
import com.android.messaging.ui.LicenseActivity;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.BuglePrefs;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.PhoneUtils;
public class ApplicationSettingsActivity extends BugleActionBarActivity {
@@ -117,12 +116,6 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
(SwitchPreference) findPreference(mSwipeRightToDeleteConversationkey);
mIsSmsPreferenceClicked = false;
if (!DebugUtils.isDebugEnabled()) {
final Preference debugCategory = findPreference(getString(
R.string.debug_pref_key));
getPreferenceScreen().removePreference(debugCategory);
}
final PreferenceScreen advancedScreen = (PreferenceScreen) findPreference(
getString(R.string.advanced_pref_key));
final boolean topLevel = getActivity().getIntent().getBooleanExtra(
@@ -138,23 +138,6 @@ public class PerSubscriptionSettingsActivity extends BugleActionBarActivity {
advancedCategory.removePreference(deliveryReportsPref);
}
// Access Point Names (APNs)
final PreferenceScreen apnsScreen =
(PreferenceScreen) findPreference(getString(R.string.sms_apns_key));
if (!MmsManager.shouldUseLegacyMms()
|| (MmsUtils.useSystemApnTable() && !ApnDatabase.doesDatabaseExist())) {
// 1) Remove the ability to edit the local APN prefs if it doesn't use legacy APIs.
// 2) Don't remove the ability to edit the local APN prefs if this device lets us
// access the system APN, but we can't find the MCC/MNC in the APN table and we
// created the local APN table in case the MCC/MNC was in there. In other words,
// if the local APN table exists, let the user edit it.
advancedCategory.removePreference((Preference) apnsScreen);
} else {
apnsScreen.setIntent(UIIntents.get()
.getApnSettingsIntent(getPreferenceScreen().getContext(), mSubId));
}
// We want to disable preferences if we are not the default app, but we do all of the
// above first so that the user sees the correct information on the screen
if (!PhoneUtils.getDefault().isDefaultSmsApp()) {
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +18,6 @@ package com.android.messaging.ui.contact;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import androidx.core.util.Pair;
import android.text.TextUtils;
import android.text.util.Rfc822Token;
@@ -35,8 +35,6 @@ import com.android.ex.chips.RecipientEntry;
import com.android.messaging.R;
import com.android.messaging.util.Assert;
import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.ContactRecipientEntryUtils;
import com.android.messaging.util.ContactUtil;
import com.android.messaging.util.PhoneUtils;
@@ -116,40 +114,16 @@ public final class ContactRecipientAdapter extends BaseRecipientAdapter {
@DoesNotRunOnMainThread
private CursorResult getFilteredResultsCursor(final String searchText) {
Assert.isNotMainThread();
if (BugleGservices.get().getBoolean(
BugleGservicesKeys.ALWAYS_AUTOCOMPLETE_EMAIL_ADDRESS,
BugleGservicesKeys.ALWAYS_AUTOCOMPLETE_EMAIL_ADDRESS_DEFAULT)) {
final Cursor personalFilterPhonesCursor = ContactUtil
.filterPhones(getContext(), searchText).performSynchronousQuery();
final Cursor personalFilterEmailsCursor = ContactUtil
.filterEmails(getContext(), searchText).performSynchronousQuery();
final Cursor personalCursor = new MergeCursor(
new Cursor[]{personalFilterEmailsCursor, personalFilterPhonesCursor});
final CursorResult cursorResult =
new CursorResult(personalCursor, false /* sorted */);
// Including enterprise result starting from N.
final Cursor enterpriseFilterPhonesCursor = ContactUtil.filterPhonesEnterprise(
getContext(), searchText).performSynchronousQuery();
final Cursor enterpriseFilterEmailsCursor = ContactUtil.filterEmailsEnterprise(
getContext(), searchText).performSynchronousQuery();
final Cursor enterpriseCursor = new MergeCursor(
new Cursor[]{enterpriseFilterEmailsCursor,
enterpriseFilterPhonesCursor});
cursorResult.enterpriseCursor = enterpriseCursor;
return cursorResult;
} else {
final Cursor personalFilterDestinationCursor = ContactUtil
.filterDestination(getContext(), searchText).performSynchronousQuery();
final CursorResult cursorResult = new CursorResult(personalFilterDestinationCursor,
true);
// Including enterprise result starting from N.
final Cursor enterpriseFilterDestinationCursor = ContactUtil
.filterDestinationEnterprise(getContext(), searchText)
.performSynchronousQuery();
cursorResult.enterpriseCursor = enterpriseFilterDestinationCursor;
return cursorResult;
}
final Cursor personalFilterDestinationCursor = ContactUtil
.filterDestination(getContext(), searchText).performSynchronousQuery();
final CursorResult cursorResult = new CursorResult(personalFilterDestinationCursor,
true);
// Including enterprise result starting from N.
final Cursor enterpriseFilterDestinationCursor = ContactUtil
.filterDestinationEnterprise(getContext(), searchText)
.performSynchronousQuery();
cursorResult.enterpriseCursor = enterpriseFilterDestinationCursor;
return cursorResult;
}
@Override
@@ -37,9 +37,7 @@ import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.Assert;
import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
import com.android.messaging.util.Dates;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.PhoneUtils;
import com.android.messaging.util.SafeAsyncTask;
import java.util.List;
@@ -52,22 +50,8 @@ public class MessageDetailsDialog {
public static void show(final Context context, final ConversationMessageData data,
final ConversationParticipantsData participants, final ParticipantData self) {
if (DebugUtils.isDebugEnabled()) {
new SafeAsyncTask<Void, Void, String>() {
@Override
protected String doInBackgroundTimed(Void... params) {
return getMessageDetails(context, data, participants, self);
}
@Override
protected void onPostExecute(String messageDetails) {
showDialog(context, messageDetails);
}
}.executeOnThreadPool(null, null, null);
} else {
String messageDetails = getMessageDetails(context, data, participants, self);
showDialog(context, messageDetails);
}
String messageDetails = getMessageDetails(context, data, participants, self);
showDialog(context, messageDetails);
}
private static String getMessageDetails(final Context context,
@@ -140,10 +124,6 @@ public class MessageDetailsDialog {
appendSimInfo(res, self, details);
if (DebugUtils.isDebugEnabled()) {
appendDebugInfo(details, data);
}
return details.toString();
}
@@ -206,10 +186,6 @@ public class MessageDetailsDialog {
appendSimInfo(res, self, details);
if (DebugUtils.isDebugEnabled()) {
appendDebugInfo(details, data);
}
return details.toString();
}
@@ -40,9 +40,6 @@ import com.android.messaging.ui.UIIntents;
import com.android.messaging.ui.contact.AddContactsConfirmationDialog;
import com.android.messaging.ui.conversationlist.ConversationListFragment.ConversationListFragmentHost;
import com.android.messaging.ui.conversationlist.MultiSelectActionModeCallback.SelectedConversation;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.PhoneUtils;
import com.android.messaging.util.Trace;
import com.android.messaging.util.UiUtils;
@@ -268,10 +265,6 @@ public abstract class AbstractConversationListActivity extends BugleActionBarAc
conversationId);
}
public void onActionBarDebug() {
DebugUtils.showDebugOptions(this);
}
private static class UpdateDestinationBlockedActionSnackBar
implements UpdateDestinationBlockedAction.UpdateDestinationBlockedActionListener {
private final Context mContext;
@@ -21,11 +21,9 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import android.view.Menu;
import android.view.MenuItem;
import com.android.messaging.R;
import com.android.messaging.util.DebugUtils;
public class ArchivedConversationListActivity extends AbstractConversationListActivity {
@@ -60,25 +58,8 @@ public class ArchivedConversationListActivity extends AbstractConversationListAc
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (super.onCreateOptionsMenu(menu)) {
return true;
}
getMenuInflater().inflate(R.menu.archived_conversation_list_menu, menu);
final MenuItem item = menu.findItem(R.id.action_debug_options);
if (item != null) {
final boolean enableDebugItems = DebugUtils.isDebugEnabled();
item.setVisible(enableDebugItems).setEnabled(enableDebugItems);
}
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem menuItem) {
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch(menuItem.getItemId()) {
case R.id.action_debug_options:
onActionBarDebug();
return true;
case android.R.id.home:
onActionBarHome();
return true;
@@ -27,7 +27,6 @@ import android.view.MenuItem;
import com.android.messaging.R;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.Trace;
public class ConversationListActivity extends AbstractConversationListActivity {
@@ -77,11 +76,6 @@ public class ConversationListActivity extends AbstractConversationListActivity {
return true;
}
getMenuInflater().inflate(R.menu.conversation_list_fragment_menu, menu);
final MenuItem item = menu.findItem(R.id.action_debug_options);
if (item != null) {
final boolean enableDebugItems = DebugUtils.isDebugEnabled();
item.setVisible(enableDebugItems).setEnabled(enableDebugItems);
}
return true;
}
@@ -94,9 +88,6 @@ public class ConversationListActivity extends AbstractConversationListActivity {
case R.id.action_settings:
onActionBarSettings();
return true;
case R.id.action_debug_options:
onActionBarDebug();
return true;
case R.id.action_show_archived:
onActionBarArchived();
return true;
@@ -1,34 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.debug;
import android.os.Bundle;
import com.android.messaging.R;
import com.android.messaging.ui.BaseBugleActivity;
/**
* Show list of all MmsConfig key/value pairs and allow editing.
*/
public class DebugMmsConfigActivity extends BaseBugleActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.debug_mmsconfig_activity);
}
}
@@ -1,150 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.debug;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import com.android.messaging.R;
import com.android.messaging.sms.MmsConfig;
import com.android.messaging.ui.debug.DebugMmsConfigItemView.MmsConfigItemListener;
import com.android.messaging.util.PhoneUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Show list of all MmsConfig key/value pairs and allow editing.
*/
public class DebugMmsConfigFragment extends Fragment {
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
final View fragmentView = inflater.inflate(R.layout.mms_config_debug_fragment, container,
false);
final ListView listView = (ListView) fragmentView.findViewById(android.R.id.list);
final Spinner spinner = (Spinner) fragmentView.findViewById(R.id.sim_selector);
final Integer[] subIdArray = getActiveSubIds();
ArrayAdapter<Integer> spinnerAdapter = new ArrayAdapter<Integer>(getActivity(),
android.R.layout.simple_spinner_item, subIdArray);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerAdapter);
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
listView.setAdapter(new MmsConfigAdapter(getActivity(), subIdArray[position]));
final int[] mccmnc = PhoneUtils.get(subIdArray[position]).getMccMnc();
// Set the title with the mcc/mnc
final TextView title = (TextView) fragmentView.findViewById(R.id.sim_title);
title.setText("(" + mccmnc[0] + "/" + mccmnc[1] + ") " +
getActivity().getString(R.string.debug_sub_id_spinner_text));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
return fragmentView;
}
public static Integer[] getActiveSubIds() {
final List<SubscriptionInfo> subRecords =
PhoneUtils.getDefault().getActiveSubscriptionInfoList();
if (subRecords == null) {
return new Integer[0];
}
final Integer[] retArray = new Integer[subRecords.size()];
for (int i = 0; i < subRecords.size(); i++) {
retArray[i] = subRecords.get(i).getSubscriptionId();
}
return retArray;
}
private class MmsConfigAdapter extends BaseAdapter implements
DebugMmsConfigItemView.MmsConfigItemListener {
private final LayoutInflater mInflater;
private final List<String> mKeys;
private final MmsConfig mMmsConfig;
public MmsConfigAdapter(Context context, int subId) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mMmsConfig = MmsConfig.get(subId);
mKeys = new ArrayList<>(mMmsConfig.keySet());
Iterator<String> it = mKeys.iterator();
while (it.hasNext()) {
// Remove a config if the MmsConfig.sKeyTypeMap doesn't have it.
if (MmsConfig.getKeyType(it.next()) == null) {
it.remove();
}
}
Collections.sort(mKeys);
}
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
final DebugMmsConfigItemView view;
if (convertView != null && convertView instanceof DebugMmsConfigItemView) {
view = (DebugMmsConfigItemView) convertView;
} else {
view = (DebugMmsConfigItemView) mInflater.inflate(
R.layout.debug_mmsconfig_item_view, parent, false);
}
final String key = mKeys.get(position);
view.bind(key,
MmsConfig.getKeyType(key),
String.valueOf(mMmsConfig.getValue(key)),
this);
return view;
}
@Override
public void onValueChanged(String key, String keyType, String value) {
mMmsConfig.update(keyType, key, value);
notifyDataSetChanged();
}
@Override
public int getCount() {
return mKeys.size();
}
@Override
public Object getItem(int position) {
return mKeys.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
}
}
@@ -1,131 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.debug;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
import com.android.messaging.R;
import com.android.messaging.sms.MmsConfig;
import com.android.messaging.util.LogUtil;
public class DebugMmsConfigItemView extends LinearLayout implements OnClickListener,
OnCheckedChangeListener, DialogInterface.OnClickListener {
public interface MmsConfigItemListener {
void onValueChanged(String key, String keyType, String value);
}
private TextView mTitle;
private TextView mTextValue;
private Switch mSwitch;
private String mKey;
private String mKeyType;
private MmsConfigItemListener mListener;
private EditText mEditText;
public DebugMmsConfigItemView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
@Override
protected void onFinishInflate () {
mTitle = (TextView) findViewById(R.id.title);
mTextValue = (TextView) findViewById(R.id.text_value);
mSwitch = (Switch) findViewById(R.id.switch_button);
setOnClickListener(this);
mSwitch.setOnCheckedChangeListener(this);
}
public void bind(final String key, final String keyType, final String value,
final MmsConfigItemListener listener) {
mListener = listener;
mKey = key;
mKeyType = keyType;
mTitle.setText(key);
switch (keyType) {
case MmsConfig.KEY_TYPE_BOOL:
mSwitch.setVisibility(View.VISIBLE);
mTextValue.setVisibility(View.GONE);
mSwitch.setChecked(Boolean.valueOf(value));
break;
case MmsConfig.KEY_TYPE_STRING:
case MmsConfig.KEY_TYPE_INT:
mTextValue.setVisibility(View.VISIBLE);
mSwitch.setVisibility(View.GONE);
mTextValue.setText(value);
break;
default:
mTextValue.setVisibility(View.GONE);
mSwitch.setVisibility(View.GONE);
LogUtil.e(LogUtil.BUGLE_TAG, "Unexpected keytype: " + keyType);
break;
}
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mListener.onValueChanged(mKey, mKeyType, String.valueOf(isChecked));
}
@Override
public void onClick(View v) {
if (MmsConfig.KEY_TYPE_BOOL.equals(mKeyType)) {
return;
}
final Context context = getContext();
mEditText = new EditText(context);
mEditText.setText(mTextValue.getText());
mEditText.setFocusable(true);
if (MmsConfig.KEY_TYPE_INT.equals(mKeyType)) {
mEditText.setInputType(InputType.TYPE_CLASS_PHONE);
} else {
mEditText.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
}
final AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle(mKey)
.setView(mEditText)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setOnShowListener(dialog1 -> {
mEditText.requestFocus();
mEditText.selectAll();
((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE))
.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
});
dialog.show();
}
@Override
public void onClick(DialogInterface dialog, int which) {
mListener.onValueChanged(mKey, mKeyType, mEditText.getText().toString());
}
}
@@ -1,171 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui.debug;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.telephony.SmsMessage;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.android.messaging.R;
import com.android.messaging.datamodel.action.ReceiveMmsMessageAction;
import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.receiver.SmsReceiver;
import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.DebugUtils;
import com.android.messaging.util.LogUtil;
/**
* Class that displays UI for choosing SMS/MMS dump files for debugging
*/
public class DebugSmsMmsFromDumpFileDialogFragment extends DialogFragment {
public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
public static final String KEY_DUMP_FILES = "dump_files";
public static final String KEY_ACTION = "action";
public static final String ACTION_LOAD = "load";
public static final String ACTION_EMAIL = "email";
private String[] mDumpFiles;
private String mAction;
public static DebugSmsMmsFromDumpFileDialogFragment newInstance(final String[] dumpFiles,
final String action) {
final DebugSmsMmsFromDumpFileDialogFragment frag =
new DebugSmsMmsFromDumpFileDialogFragment();
final Bundle args = new Bundle();
args.putSerializable(KEY_DUMP_FILES, dumpFiles);
args.putString(KEY_ACTION, action);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Bundle args = getArguments();
mDumpFiles = (String[]) args.getSerializable(KEY_DUMP_FILES);
mAction = args.getString(KEY_ACTION);
final LayoutInflater inflater = getActivity().getLayoutInflater();
final View layout = inflater.inflate(
R.layout.debug_sms_mms_from_dump_file_dialog, null/*root*/);
final ListView list = (ListView) layout.findViewById(R.id.dump_file_list);
list.setAdapter(new DumpFileListAdapter(getActivity(), mDumpFiles));
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
final Resources resources = getResources();
if (ACTION_LOAD.equals(mAction)) {
builder.setTitle(resources.getString(
R.string.load_sms_mms_from_dump_file_dialog_title));
} else if (ACTION_EMAIL.equals(mAction)) {
builder.setTitle(resources.getString(
R.string.email_sms_mms_from_dump_file_dialog_title));
}
builder.setView(layout);
return builder.create();
}
private class DumpFileListAdapter extends ArrayAdapter<String> {
public DumpFileListAdapter(final Context context, final String[] dumpFiles) {
super(context, R.layout.sms_mms_dump_file_list_item, dumpFiles);
}
@NonNull
@Override
public View getView(final int position, final View view, @NonNull final ViewGroup parent) {
TextView actionItemView;
if (view == null || !(view instanceof TextView)) {
final LayoutInflater inflater = LayoutInflater.from(getContext());
actionItemView = (TextView) inflater.inflate(
R.layout.sms_mms_dump_file_list_item, parent, false);
} else {
actionItemView = (TextView) view;
}
final String file = getItem(position);
actionItemView.setText(file);
actionItemView.setOnClickListener(view1 -> {
dismiss();
if (ACTION_LOAD.equals(mAction)) {
receiveFromDumpFile(file);
} else if (ACTION_EMAIL.equals(mAction)) {
emailDumpFile(file);
}
});
return actionItemView;
}
}
/**
* Load MMS/SMS from the dump file
*/
private void receiveFromDumpFile(final String dumpFileName) {
if (dumpFileName.startsWith(MmsUtils.SMS_DUMP_PREFIX)) {
final SmsMessage[] messages = DebugUtils.retreiveSmsFromDumpFile(dumpFileName);
if (messages != null) {
SmsReceiver.deliverSmsMessages(getActivity(), ParticipantData.DEFAULT_SELF_SUB_ID,
0, messages);
} else {
LogUtil.e(LogUtil.BUGLE_TAG,
"receiveFromDumpFile: invalid sms dump file " + dumpFileName);
}
} else if (dumpFileName.startsWith(MmsUtils.MMS_DUMP_PREFIX)) {
final byte[] data = MmsUtils.createDebugNotificationInd(dumpFileName);
if (data != null) {
final ReceiveMmsMessageAction action = new ReceiveMmsMessageAction(
ParticipantData.DEFAULT_SELF_SUB_ID, data);
action.start();
} else {
LogUtil.e(LogUtil.BUGLE_TAG,
"receiveFromDumpFile: invalid mms dump file " + dumpFileName);
}
} else {
LogUtil.e(LogUtil.BUGLE_TAG,
"receiveFromDumpFile: invalid dump file name " + dumpFileName);
}
}
/**
* Launch email app to send the dump file
*/
private void emailDumpFile(final String file) {
final Resources resources = getResources();
final String fileLocation = "file://"
+ Environment.getExternalStorageDirectory() + "/" + file;
final Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.setType(APPLICATION_OCTET_STREAM);
sharingIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(fileLocation));
sharingIntent.putExtra(Intent.EXTRA_SUBJECT,
resources.getString(R.string.email_sms_mms_dump_file_subject));
getActivity().startActivity(Intent.createChooser(sharingIntent,
resources.getString(R.string.email_sms_mms_dump_file_chooser_title)));
}
}
@@ -45,8 +45,6 @@ import com.android.messaging.sms.MmsConfig;
import com.android.messaging.ui.mediapicker.camerafocus.FocusOverlayManager;
import com.android.messaging.ui.mediapicker.camerafocus.RenderOverlay;
import com.android.messaging.util.Assert;
import com.android.messaging.util.BugleGservices;
import com.android.messaging.util.BugleGservicesKeys;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.OsUtil;
import com.android.messaging.util.UiUtils;
@@ -975,9 +973,7 @@ class CameraManager implements FocusOverlayManager.Listener {
screenWidth *= scaleFactor;
screenHeight *= scaleFactor;
final float aspectRatio = BugleGservices.get().getFloat(
BugleGservicesKeys.CAMERA_ASPECT_RATIO,
screenWidth / (float) screenHeight);
final float aspectRatio = screenWidth / (float) screenHeight;
final List<Camera.Size> sizes = new ArrayList<Camera.Size>(
mCamera.getParameters().getSupportedPictureSizes());
final int maxPixels = maxWidth * maxHeight;
@@ -40,27 +40,12 @@ public final class Assert {
sShouldCrash = sIsEngBuild = true;
}
private static void refreshGservices(final BugleGservices gservices) {
sShouldCrash = sIsEngBuild;
if (!sShouldCrash) {
sShouldCrash = gservices.getBoolean(
BugleGservicesKeys.ASSERTS_FATAL,
BugleGservicesKeys.ASSERTS_FATAL_DEFAULT);
}
}
// Static initializer block to find out if we're running an eng or
// release build.
static {
setIfEngBuild();
}
// This is called from FactoryImpl once the Gservices class is initialized.
public static void initializeGservices (final BugleGservices gservices) {
gservices.registerForChanges(() -> refreshGservices(gservices));
refreshGservices(gservices);
}
/**
* Halt execution if this is not an eng build.
* <p>Intended for use in code paths that should be run only for tests and never on
@@ -1,72 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.util;
import com.android.messaging.Factory;
/**
* A thin wrapper for getting GServices value. During constructor time a one time background thread
* will cache all GServices key with the prefix of "bugle_". All get calls will wait for Gservices
* to finish caching the first time. In practice, the background thread will finish before any get
* request.
*/
public abstract class BugleGservices {
static final String BUGLE_GSERVICES_PREFIX = "bugle_";
public static BugleGservices get() {
return Factory.get().getBugleGservices();
}
public abstract void registerForChanges(final Runnable r);
/**
* @param key The key to look up in GServices
* @param defaultValue The default value if value in GServices is null or if
* NumberFormatException is caught.
* @return The corresponding value, or the default value.
*/
public abstract long getLong(final String key, final long defaultValue);
/**
* @param key The key to look up in GServices
* @param defaultValue The default value if value in GServices is null or if
* NumberFormatException is caught.
* @return The corresponding value, or the default value.
*/
public abstract int getInt(final String key, final int defaultValue);
/**
* @param key The key to look up in GServices
* @param defaultValue The default value if value in GServices is null.
* @return The corresponding value, or the default value.
*/
public abstract boolean getBoolean(final String key, final boolean defaultValue);
/**
* @param key The key to look up in GServices
* @param defaultValue The default value if value in GServices is null.
* @return The corresponding value, or the default value.
*/
public abstract String getString(final String key, final String defaultValue);
/**
* @param key The key to look up in GServices
* @param defaultValue The default value if value in GServices is null.
* @return The corresponding value, or the default value.
*/
public abstract float getFloat(final String key, final float defaultValue);
}
@@ -1,68 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.util;
import android.content.Context;
/**
* A thin wrapper for getting GServices value.
*/
public class BugleGservicesImpl extends BugleGservices {
public BugleGservicesImpl(final Context context) {
}
@Override
public void registerForChanges(final Runnable r) {
}
/**
* Asserts that the key has the expected prefix.
*/
private void assertKeyAndWaitForGservices(final String key) {
Assert.isTrue(key.startsWith(BUGLE_GSERVICES_PREFIX));
}
@Override
public long getLong(final String key, final long defaultValue) {
assertKeyAndWaitForGservices(key);
return defaultValue;
}
@Override
public int getInt(final String key, final int defaultValue) {
assertKeyAndWaitForGservices(key);
return defaultValue;
}
@Override
public boolean getBoolean(final String key, final boolean defaultValue) {
assertKeyAndWaitForGservices(key);
return defaultValue;
}
@Override
public String getString(final String key, final String defaultValue) {
assertKeyAndWaitForGservices(key);
return defaultValue;
}
@Override
public float getFloat(final String key, final float defaultValue) {
assertKeyAndWaitForGservices(key);
return defaultValue;
}
}
@@ -24,49 +24,29 @@ package com.android.messaging.util;
public final class BugleGservicesKeys {
private BugleGservicesKeys() {} // do not instantiate
/**
* Whether to enable extra debugging features on the client. Default is
* {@value #ENABLE_DEBUGGING_FEATURES_DEFAULT}.
*/
public static final String ENABLE_DEBUGGING_FEATURES
= "bugle_debugging";
public static final boolean ENABLE_DEBUGGING_FEATURES_DEFAULT
= false;
/**
* Whether to enable saving extra logs. Default is {@value #ENABLE_LOG_SAVER_DEFAULT}.
*/
public static final String ENABLE_LOG_SAVER = "bugle_logsaver";
public static final boolean ENABLE_LOG_SAVER_DEFAULT = false;
/**
* Time in milliseconds of initial (attempt 1) resend backoff for failing messages
*/
public static final String INITIAL_MESSAGE_RESEND_DELAY_MS = "bugle_resend_delay_in_millis";
public static final long INITIAL_MESSAGE_RESEND_DELAY_MS_DEFAULT = 5 * 1000L;
/**
* Time in milliseconds of max resend backoff for failing messages
*/
public static final String MAX_MESSAGE_RESEND_DELAY_MS = "bugle_max_resend_delay_in_millis";
public static final long MAX_MESSAGE_RESEND_DELAY_MS_DEFAULT = 2 * 60 * 60 * 1000L;
/**
* Time in milliseconds of resend window for unsent messages
*/
public static final String MESSAGE_RESEND_TIMEOUT_MS = "bugle_resend_timeout_in_millis";
public static final long MESSAGE_RESEND_TIMEOUT_MS_DEFAULT = 20 * 60 * 1000L;
/**
* Time in milliseconds of download window for new mms notifications
*/
public static final String MESSAGE_DOWNLOAD_TIMEOUT_MS = "bugle_download_timeout_in_millis";
public static final long MESSAGE_DOWNLOAD_TIMEOUT_MS_DEFAULT = 20 * 60 * 1000L;
/**
* Time in milliseconds for SMS send timeout
*/
public static final String SMS_SEND_TIMEOUT_IN_MILLIS = "bugle_sms_send_timeout";
public static final long SMS_SEND_TIMEOUT_IN_MILLIS_DEFAULT = 5 * 60 * 1000L;
/**
@@ -87,17 +67,9 @@ public final class BugleGservicesKeys {
* whatever reasons. Keeping this low ensures responsiveness of the application.
* 4. The limit on number of total messages to scan in one batch.
*/
public static final String SMS_SYNC_BATCH_SIZE_MIN =
"bugle_sms_sync_batch_size_min";
public static final int SMS_SYNC_BATCH_SIZE_MIN_DEFAULT = 80;
public static final String SMS_SYNC_BATCH_SIZE_MAX =
"bugle_sms_sync_batch_size_max";
public static final int SMS_SYNC_BATCH_SIZE_MAX_DEFAULT = 1000;
public static final String SMS_SYNC_BATCH_TIME_LIMIT_MILLIS =
"bugle_sms_sync_batch_time_limit";
public static final long SMS_SYNC_BATCH_TIME_LIMIT_MILLIS_DEFAULT = 400;
public static final String SMS_SYNC_BATCH_MAX_MESSAGES_TO_SCAN =
"bugle_sms_sync_batch_max_messages_to_scan";
public static final int SMS_SYNC_BATCH_MAX_MESSAGES_TO_SCAN_DEFAULT =
SMS_SYNC_BATCH_SIZE_MAX_DEFAULT * 4;
@@ -108,18 +80,13 @@ public final class BugleGservicesKeys {
* when bringing in changes made outside the application. It also represents a buffer
* to ensure that sync doesn't trigger based on changes made within the application.
*/
public static final String SMS_SYNC_BACKOFF_TIME_MILLIS =
"bugle_sms_sync_backoff_time";
public static final long SMS_SYNC_BACKOFF_TIME_MILLIS_DEFAULT = 5000L;
/**
* Just in case if we fall into a loop of full sync -> still not synchronized -> full sync ...
* This forces a backoff time so that we at most do full sync once a while (an hour by default)
*/
public static final String SMS_FULL_SYNC_BACKOFF_TIME_MILLIS =
"bugle_sms_full_sync_backoff_time";
public static final long SMS_FULL_SYNC_BACKOFF_TIME_MILLIS_DEFAULT = 60 * 60 * 1000;
/**
* MMS UA profile url.
*
@@ -127,66 +94,30 @@ public final class BugleGservicesKeys {
* latest and greatest phones. However, if we're on KitKat or below we can't get the phone's
* UA profile and thus we need to send them the default url.
*/
public static final String MMS_UA_PROFILE_URL =
"bugle_mms_uaprofurl";
public static final String MMS_UA_PROFILE_URL_DEFAULT =
"http://www.gstatic.com/android/sms/mms_ua_profile.xml";
/**
* MMS apn mmsc
*/
public static final String MMS_MMSC =
"bugle_mms_mmsc";
/**
* MMS apn proxy ip address
*/
public static final String MMS_PROXY_ADDRESS =
"bugle_mms_proxy_address";
/**
* MMS apn proxy port
*/
public static final String MMS_PROXY_PORT =
"bugle_mms_proxy_port";
/**
* List of known SMS system messages that we will ignore (no deliver, no abort) so that the
* user doesn't see them and the appropriate app is able to handle them. We are delivering
* these as a \n delimited list of patterns, however we should eventually move to storing
* them with the per-carrier mms config xml file.
*/
public static final String SMS_IGNORE_MESSAGE_REGEX =
"bugle_sms_ignore_message_regex";
public static final String SMS_IGNORE_MESSAGE_REGEX_DEFAULT = "";
/**
* When receiving or importing an mms, limit the length of text to this limit. Huge blocks
* of text can cause the app to hang/ANR/or crash in native text code..
*/
public static final String MMS_TEXT_LIMIT = "bugle_mms_text_limit";
public static final int MMS_TEXT_LIMIT_DEFAULT = 2000;
/**
* Max number of attachments the user may add to a single message.
*/
public static final String MMS_ATTACHMENT_LIMIT = "bugle_mms_attachment_limit";
public static final int MMS_ATTACHMENT_LIMIT_DEFAULT = 10;
/**
* The max number of messages to show in a single conversation notification. We always show
* the most recent message. If this value is >1, we may also include prior messages as well.
*/
public static final String MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION =
"bugle_max_messages_in_conversation_notification";
public static final int MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_DEFAULT = 7;
/**
* Time (in seconds) between notification ringing for incoming messages of the same
* conversation. We won't ding more often than this value for messages coming in at a high rate.
*/
public static final String NOTIFICATION_TIME_BETWEEN_RINGS_SECONDS
= "bugle_notification_time_between_rings_seconds";
public static final int NOTIFICATION_TIME_BETWEEN_RINGS_SECONDS_DEFAULT = 10;
/**
@@ -195,91 +126,11 @@ public final class BugleGservicesKeys {
* less screen real estate, so we may want to optimize for that case. Note that if a wearable
* is paired, this value will apply to notifications as shown both on the watch and the phone.
*/
public static final String MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_WITH_WEARABLE =
"bugle_max_messages_in_conversation_notification_with_wearable";
public static final int MAX_MESSAGES_IN_CONVERSATION_NOTIFICATION_WITH_WEARABLE_DEFAULT = 1;
/**
* Regular expression to match against query. If it matches then display
* the query plan for this query.
*/
public static final String EXPLAIN_QUERY_PLAN_REGEXP = "bugle_query_plan_regexp";
/**
* Whether asserts are fatal on user/userdebug builds.
* Default is {@value #ASSERTS_FATAL_DEFAULT}.
*/
public static final String ASSERTS_FATAL = "bugle_asserts_fatal";
public static final boolean ASSERTS_FATAL_DEFAULT = false;
/**
* Whether to use API for sending/downloading MMS (if present, true for L).
* Default is {@value #USE_MMS_API_IF_PRESENT_DEFAULT}.
*/
public static final String USE_MMS_API_IF_PRESENT = "bugle_use_mms_api";
public static final boolean USE_MMS_API_IF_PRESENT_DEFAULT = true;
/**
* Whether to always auto-complete email addresses for sending MMS. By default, Bugle starts
* to auto-complete after the user has typed the "@" character.
* Default is (@value ALWAYS_AUTOCOMPLETE_EMAIL_ADDRESS_DEFAULT}.
*/
public static final String ALWAYS_AUTOCOMPLETE_EMAIL_ADDRESS =
"bugle_always_autocomplete_email_address";
public static final boolean ALWAYS_AUTOCOMPLETE_EMAIL_ADDRESS_DEFAULT = false;
// We typically request an aspect ratio close the the screen size, but some cameras can be
// flaky and not work well in certain aspect ratios. This allows us to guide the CameraManager
// to pick a more reliable aspect ratio. The value is a float like 1.333f or 1.777f. There is
// no hard coded default because the default is the screen aspect ratio.
public static final String CAMERA_ASPECT_RATIO = "bugle_camera_aspect_ratio";
/**
* The recent time range within which we should check MMS WAP Push duplication
* If the value is 0, it signals that we should use old dedup algorithm for wap push
*/
public static final String MMS_WAP_PUSH_DEDUP_TIME_LIMIT_SECS =
"bugle_mms_wap_push_dedup_time_limit_secs";
public static final long MMS_WAP_PUSH_DEDUP_TIME_LIMIT_SECS_DEFAULT = 7 * 24 * 3600; // 7 days
/**
* Whether to use persistent, on-disk LogSaver
*/
public static final String PERSISTENT_LOGSAVER = "bugle_persistent_logsaver";
public static final boolean PERSISTENT_LOGSAVER_DEFAULT = false;
/**
* For in-memory LogSaver, what's the size of memory buffer in number of records
*/
public static final String IN_MEMORY_LOGSAVER_RECORD_COUNT =
"bugle_in_memory_logsaver_record_count";
public static final int IN_MEMORY_LOGSAVER_RECORD_COUNT_DEFAULT = 500;
/**
* For on-disk LogSaver, what's the size of file rotation set
*/
public static final String PERSISTENT_LOGSAVER_ROTATION_SET_SIZE =
"bugle_persistent_logsaver_rotation_set_size";
public static final int PERSISTENT_LOGSAVER_ROTATION_SET_SIZE_DEFAULT = 8;
/**
* For on-disk LogSaver, what's the byte limit of a single log file
*/
public static final String PERSISTENT_LOGSAVER_FILE_LIMIT_BYTES =
"bugle_persistent_logsaver_file_limit";
public static final int PERSISTENT_LOGSAVER_FILE_LIMIT_BYTES_DEFAULT = 256 * 1024; // 256KB
/**
* We concatenate all text parts in an MMS to form the message text. This specifies
* the separator between the combinated text parts. Default is ' ' (space).
*/
public static final String MMS_TEXT_CONCAT_SEPARATOR = "bugle_mms_text_concat_separator";
public static final String MMS_TEXT_CONCAT_SEPARATOR_DEFAULT = " ";
/**
* Whether to enable transcoding GIFs. We sometimes need to compress GIFs to make them small
* enough to send via MMS (which often limits messages to 1 MB in size).
*/
public static final String ENABLE_GIF_TRANSCODING = "bugle_gif_transcoding";
public static final boolean ENABLE_GIF_TRANSCODING_DEFAULT = true;
}
@@ -17,304 +17,22 @@
package com.android.messaging.util;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Environment;
import android.telephony.SmsMessage;
import android.text.TextUtils;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import com.android.messaging.Factory;
import com.android.messaging.R;
import com.android.messaging.datamodel.SyncManager;
import com.android.messaging.datamodel.action.DumpDatabaseAction;
import com.android.messaging.datamodel.action.LogTelephonyDatabaseAction;
import com.android.messaging.sms.MmsUtils;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.ui.debug.DebugSmsMmsFromDumpFileDialogFragment;
import com.google.common.io.ByteStreams;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StreamCorruptedException;
public class DebugUtils {
private static final String TAG = "bugle.util.DebugUtils";
private static boolean sDebugNoise;
private static boolean sDebugClassZeroSms;
private static MediaPlayer [] sMediaPlayer;
private static final Object sLock = new Object();
public static final int DEBUG_SOUND_SERVER_REQUEST = 0;
public static final int DEBUG_SOUND_DB_OP = 1;
public static void maybePlayDebugNoise(final Context context, final int sound) {
if (sDebugNoise) {
synchronized (sLock) {
try {
if (sMediaPlayer == null) {
sMediaPlayer = new MediaPlayer[2];
sMediaPlayer[DEBUG_SOUND_SERVER_REQUEST] =
MediaPlayer.create(context, R.raw.server_request_debug);
sMediaPlayer[DEBUG_SOUND_DB_OP] =
MediaPlayer.create(context, R.raw.db_op_debug);
sMediaPlayer[DEBUG_SOUND_DB_OP].setVolume(1.0F, 1.0F);
sMediaPlayer[DEBUG_SOUND_SERVER_REQUEST].setVolume(0.3F, 0.3F);
}
if (sMediaPlayer[sound] != null) {
sMediaPlayer[sound].start();
}
} catch (final IllegalArgumentException e) {
LogUtil.e(TAG, "MediaPlayer exception", e);
} catch (final SecurityException e) {
LogUtil.e(TAG, "MediaPlayer exception", e);
} catch (final IllegalStateException e) {
LogUtil.e(TAG, "MediaPlayer exception", e);
}
}
}
}
public static boolean isDebugEnabled() {
return BugleGservices.get().getBoolean(BugleGservicesKeys.ENABLE_DEBUGGING_FEATURES,
BugleGservicesKeys.ENABLE_DEBUGGING_FEATURES_DEFAULT);
}
public abstract static class DebugAction {
final String mTitle;
public DebugAction(final String title) {
mTitle = title;
}
@NonNull
@Override
public String toString() {
return mTitle;
}
public abstract void run();
}
public static void showDebugOptions(final Activity host) {
final AlertDialog.Builder builder = new AlertDialog.Builder(host);
final ArrayAdapter<DebugAction> arrayAdapter = new ArrayAdapter<DebugAction>(
host, android.R.layout.simple_list_item_1);
arrayAdapter.add(new DebugAction("Dump Database") {
@Override
public void run() {
DumpDatabaseAction.dumpDatabase();
}
});
arrayAdapter.add(new DebugAction("Log Telephony Data") {
@Override
public void run() {
LogTelephonyDatabaseAction.dumpDatabase();
}
});
arrayAdapter.add(new DebugAction("Toggle Noise") {
@Override
public void run() {
sDebugNoise = !sDebugNoise;
}
});
arrayAdapter.add(new DebugAction("Force sync SMS") {
@Override
public void run() {
final BuglePrefs prefs = BuglePrefs.getApplicationPrefs();
prefs.putLong(BuglePrefsKeys.LAST_FULL_SYNC_TIME, -1);
SyncManager.forceSync();
}
});
arrayAdapter.add(new DebugAction("Sync SMS") {
@Override
public void run() {
SyncManager.sync();
}
});
arrayAdapter.add(new DebugAction("Load SMS/MMS from dump file") {
@Override
public void run() {
new DebugSmsMmsDumpTask(host,
DebugSmsMmsFromDumpFileDialogFragment.ACTION_LOAD).executeOnThreadPool();
}
});
arrayAdapter.add(new DebugAction("Email SMS/MMS dump file") {
@Override
public void run() {
new DebugSmsMmsDumpTask(host,
DebugSmsMmsFromDumpFileDialogFragment.ACTION_EMAIL).executeOnThreadPool();
}
});
arrayAdapter.add(new DebugAction("MMS Config...") {
@Override
public void run() {
UIIntents.get().launchDebugMmsConfigActivity(host);
}
});
arrayAdapter.add(new DebugAction(sDebugClassZeroSms ? "Turn off Class 0 sms test" :
"Turn on Class Zero test") {
@Override
public void run() {
sDebugClassZeroSms = !sDebugClassZeroSms;
}
});
arrayAdapter.add(new DebugAction("Test sharing a file URI") {
@Override
public void run() {
shareFileUri();
}
});
builder.setAdapter(arrayAdapter, (arg0, pos) -> arrayAdapter.getItem(pos).run());
builder.create().show();
}
/**
* Task to list all the dump files and perform an action on it
*/
private static class DebugSmsMmsDumpTask extends SafeAsyncTask<Void, Void, String[]> {
private final String mAction;
private final Activity mHost;
public DebugSmsMmsDumpTask(final Activity host, final String action) {
mHost = host;
mAction = action;
}
@Override
protected void onPostExecute(final String[] result) {
if (result == null || result.length < 1) {
return;
}
final FragmentManager fragmentManager = mHost.getFragmentManager();
final FragmentTransaction ft = fragmentManager.beginTransaction();
final DebugSmsMmsFromDumpFileDialogFragment dialog =
DebugSmsMmsFromDumpFileDialogFragment.newInstance(result, mAction);
dialog.show(fragmentManager, ""/*tag*/);
}
@Override
protected String[] doInBackgroundTimed(final Void... params) {
final File dir = DebugUtils.getDebugFilesDir();
return dir.list((dir1, filename) -> filename != null
&& ((mAction == DebugSmsMmsFromDumpFileDialogFragment.ACTION_EMAIL
&& filename.equals(DumpDatabaseAction.DUMP_NAME))
|| filename.startsWith(MmsUtils.MMS_DUMP_PREFIX)
|| filename.startsWith(MmsUtils.SMS_DUMP_PREFIX)));
}
}
/**
* Dump the received raw SMS data into a file on external storage
*
* @param id The ID to use as part of the dump file name
* @param messages The raw SMS data
*/
public static void dumpSms(final long id, final android.telephony.SmsMessage[] messages,
final String format) {
try {
final String dumpFileName = MmsUtils.SMS_DUMP_PREFIX + id;
final File dumpFile = DebugUtils.getDebugFile(dumpFileName, true);
if (dumpFile != null) {
final FileOutputStream fos = new FileOutputStream(dumpFile);
final DataOutputStream dos = new DataOutputStream(fos);
try {
final int chars = (TextUtils.isEmpty(format) ? 0 : format.length());
dos.writeInt(chars);
if (chars > 0) {
dos.writeUTF(format);
}
dos.writeInt(messages.length);
for (final android.telephony.SmsMessage message : messages) {
final byte[] pdu = message.getPdu();
dos.writeInt(pdu.length);
dos.write(pdu, 0, pdu.length);
}
dos.flush();
} finally {
dos.close();
ensureReadable(dumpFile);
}
}
} catch (final IOException e) {
LogUtil.e(LogUtil.BUGLE_TAG, "dumpSms: " + e, e);
}
}
/**
* Load MMS/SMS from the dump file
*/
public static SmsMessage[] retreiveSmsFromDumpFile(final String dumpFileName) {
SmsMessage[] messages = null;
final File inputFile = DebugUtils.getDebugFile(dumpFileName, false);
if (inputFile != null) {
FileInputStream fis = null;
DataInputStream dis = null;
try {
fis = new FileInputStream(inputFile);
dis = new DataInputStream(fis);
// SMS dump
String format = null;
final int chars = dis.readInt();
if (chars > 0) {
format = dis.readUTF();
}
final int count = dis.readInt();
final SmsMessage[] messagesTemp = new SmsMessage[count];
for (int i = 0; i < count; i++) {
final int length = dis.readInt();
final byte[] pdu = new byte[length];
dis.read(pdu, 0, length);
messagesTemp[i] = SmsMessage.createFromPdu(pdu, format);
}
messages = messagesTemp;
} catch (final FileNotFoundException e) {
// Nothing to do
} catch (final StreamCorruptedException e) {
// Nothing to do
} catch (final IOException e) {
// Nothing to do
} finally {
if (dis != null) {
try {
dis.close();
} catch (final IOException e) {
// Nothing to do
}
}
}
}
return messages;
}
public static File getDebugFile(final String fileName, final boolean create) {
final File dir = getDebugFilesDir();
final File file = new File(dir, fileName);
@@ -416,24 +134,4 @@ public class DebugUtils {
// Never found ourself in the stack?!
return null;
}
/**
* Returns a boolean indicating whether ClassZero debugging is enabled. If enabled, any received
* sms is treated as if it were a class zero message and displayed by the ClassZeroActivity.
*/
public static boolean debugClassZeroSmsEnabled() {
return sDebugClassZeroSms;
}
/** Shares a ringtone file via file URI. */
private static void shareFileUri() {
final String packageName = "com.android.messaging";
final String fileName = "/system/media/audio/ringtones/Andromeda.ogg";
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setPackage(packageName);
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + fileName));
intent.setType("image/*");
Factory.get().getApplicationContext().startActivity(intent);
}
}
@@ -42,9 +42,6 @@ public class GifTranscoder {
}
public static boolean transcode(Context context, String filePath, String outFilePath) {
if (!isEnabled()) {
return false;
}
final long inputSize = new File(filePath).length();
Stopwatch stopwatch = Stopwatch.createStarted();
final boolean success = transcodeInternal(filePath, outFilePath);
@@ -77,19 +74,6 @@ public class GifTranscoder {
}
public static boolean canBeTranscoded(int width, int height) {
if (!isEnabled()) {
return false;
}
return width >= MIN_WIDTH && height >= MIN_HEIGHT;
}
private static boolean isEnabled() {
final boolean enabled = BugleGservices.get().getBoolean(
BugleGservicesKeys.ENABLE_GIF_TRANSCODING,
BugleGservicesKeys.ENABLE_GIF_TRANSCODING_DEFAULT);
if (!enabled) {
LogUtil.w(TAG, "GIF transcoding is disabled");
}
return enabled;
}
}
@@ -1,293 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.util;
import android.os.Process;
import android.util.Log;
import com.android.messaging.Factory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Save the app's own log to dump along with adb bugreport
*/
public abstract class LogSaver {
/**
* Writes the accumulated log entries, from oldest to newest, to the specified PrintWriter.
* Log lines are emitted in much the same form as logcat -v threadtime -- specifically,
* lines will include a timestamp, pid, tid, level, and tag.
*
* @param writer The PrintWriter to output
*/
public abstract void dump(PrintWriter writer);
/**
* Log a line
*
* @param level The log level to use
* @param tag The log tag
* @param msg The message of the log line
*/
public abstract void log(int level, String tag, String msg);
/**
* Check if the LogSaver still matches the current Gservices settings
*
* @return true if matches, false otherwise
*/
public abstract boolean isCurrent();
private LogSaver() {
}
public static LogSaver newInstance() {
final boolean persistent = BugleGservices.get().getBoolean(
BugleGservicesKeys.PERSISTENT_LOGSAVER,
BugleGservicesKeys.PERSISTENT_LOGSAVER_DEFAULT);
if (persistent) {
final int setSize = BugleGservices.get().getInt(
BugleGservicesKeys.PERSISTENT_LOGSAVER_ROTATION_SET_SIZE,
BugleGservicesKeys.PERSISTENT_LOGSAVER_ROTATION_SET_SIZE_DEFAULT);
final int fileLimitBytes = BugleGservices.get().getInt(
BugleGservicesKeys.PERSISTENT_LOGSAVER_FILE_LIMIT_BYTES,
BugleGservicesKeys.PERSISTENT_LOGSAVER_FILE_LIMIT_BYTES_DEFAULT);
return new DiskLogSaver(setSize, fileLimitBytes);
} else {
final int size = BugleGservices.get().getInt(
BugleGservicesKeys.IN_MEMORY_LOGSAVER_RECORD_COUNT,
BugleGservicesKeys.IN_MEMORY_LOGSAVER_RECORD_COUNT_DEFAULT);
return new MemoryLogSaver(size);
}
}
/**
* A circular in-memory log to be used to log potentially verbose logs. The logs will be
* persisted in memory in the application and can be dumped by various dump() methods.
* For example, adb shell dumpsys activity provider com.android.messaging.
* The dump will also show up in bugreports.
*/
private static final class MemoryLogSaver extends LogSaver {
/**
* Record to store a single log entry. Stores timestamp, tid, level, tag, and message.
* It can be reused when the circular log rolls over. This avoids creating new objects.
*/
private static class LogRecord {
int mTid;
String mLevelString;
long mTimeMillis; // from System.currentTimeMillis
String mTag;
String mMessage;
LogRecord() {
}
void set(int tid, int level, long time, String tag, String message) {
this.mTid = tid;
this.mTimeMillis = time;
this.mTag = tag;
this.mMessage = message;
this.mLevelString = getLevelString(level);
}
}
private final int mSize;
private final CircularArray<LogRecord> mLogList;
private final Object mLock;
private final SimpleDateFormat mSdf = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
public MemoryLogSaver(final int size) {
mSize = size;
mLogList = new CircularArray<LogRecord>(size);
mLock = new Object();
}
@Override
public void dump(PrintWriter writer) {
int pid = Process.myPid();
synchronized (mLock) {
for (int i = 0; i < mLogList.count(); i++) {
LogRecord rec = mLogList.get(i);
writer.println(String.format("%s %5d %5d %s %s: %s",
mSdf.format(rec.mTimeMillis),
pid, rec.mTid, rec.mLevelString, rec.mTag, rec.mMessage));
}
}
}
@Override
public void log(int level, String tag, String msg) {
synchronized (mLock) {
LogRecord rec = mLogList.getFree();
if (rec == null) {
rec = new LogRecord();
}
rec.set(Process.myTid(), level, System.currentTimeMillis(), tag, msg);
mLogList.add(rec);
}
}
@Override
public boolean isCurrent() {
final boolean persistent = BugleGservices.get().getBoolean(
BugleGservicesKeys.PERSISTENT_LOGSAVER,
BugleGservicesKeys.PERSISTENT_LOGSAVER_DEFAULT);
if (persistent) {
return false;
}
final int size = BugleGservices.get().getInt(
BugleGservicesKeys.IN_MEMORY_LOGSAVER_RECORD_COUNT,
BugleGservicesKeys.IN_MEMORY_LOGSAVER_RECORD_COUNT_DEFAULT);
return size == mSize;
}
}
/**
* A persistent, on-disk log saver. It uses the standard Java util logger along with
* a rotation log file set to store the logs in app's local file directory "app_logs".
*/
private static final class DiskLogSaver extends LogSaver {
private static final String DISK_LOG_DIR_NAME = "logs";
private final int mSetSize;
private final int mFileLimitBytes;
private Logger mDiskLogger;
public DiskLogSaver(final int setSize, final int fileLimitBytes) {
Assert.isTrue(setSize > 0);
Assert.isTrue(fileLimitBytes > 0);
mSetSize = setSize;
mFileLimitBytes = fileLimitBytes;
initDiskLog();
}
private static void clearDefaultHandlers(Logger logger) {
Assert.notNull(logger);
for (Handler handler : logger.getHandlers()) {
logger.removeHandler(handler);
}
}
private void initDiskLog() {
mDiskLogger = Logger.getLogger(LogUtil.BUGLE_TAG);
// We don't want the default console handler
clearDefaultHandlers(mDiskLogger);
// Don't want duplicate print in system log
mDiskLogger.setUseParentHandlers(false);
// FileHandler manages the log files in a fixed rotation set
final File logDir = Factory.get().getApplicationContext().getDir(
DISK_LOG_DIR_NAME, 0/*mode*/);
FileHandler handler = null;
try {
handler = new FileHandler(
logDir + "/%g.log", mFileLimitBytes, mSetSize, true/*append*/);
} catch (Exception e) {
Log.e(LogUtil.BUGLE_TAG, "LogSaver: fail to init disk logger", e);
return;
}
final Formatter formatter = new Formatter() {
@Override
public String format(java.util.logging.LogRecord r) {
return r.getMessage();
}
};
handler.setFormatter(formatter);
handler.setLevel(Level.ALL);
mDiskLogger.addHandler(handler);
}
@Override
public void dump(PrintWriter writer) {
for (int i = mSetSize - 1; i >= 0; i--) {
final File logDir = Factory.get().getApplicationContext().getDir(
DISK_LOG_DIR_NAME, 0/*mode*/);
final String logFilePath = logDir + "/" + i + ".log";
try {
final File logFile = new File(logFilePath);
if (!logFile.exists()) {
continue;
}
final BufferedReader reader = new BufferedReader(new FileReader(logFile));
for (String line; (line = reader.readLine()) != null;) {
line = line.trim();
writer.println(line);
}
} catch (FileNotFoundException e) {
Log.w(LogUtil.BUGLE_TAG, "LogSaver: can not find log file " + logFilePath);
} catch (IOException e) {
Log.w(LogUtil.BUGLE_TAG, "LogSaver: can not read log file", e);
}
}
}
@Override
public void log(int level, String tag, String msg) {
final SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
mDiskLogger.info(String.format("%s %5d %5d %s %s: %s\n",
sdf.format(System.currentTimeMillis()),
Process.myPid(), Process.myTid(), getLevelString(level), tag, msg));
}
@Override
public boolean isCurrent() {
final boolean persistent = BugleGservices.get().getBoolean(
BugleGservicesKeys.PERSISTENT_LOGSAVER,
BugleGservicesKeys.PERSISTENT_LOGSAVER_DEFAULT);
if (!persistent) {
return false;
}
final int setSize = BugleGservices.get().getInt(
BugleGservicesKeys.PERSISTENT_LOGSAVER_ROTATION_SET_SIZE,
BugleGservicesKeys.PERSISTENT_LOGSAVER_ROTATION_SET_SIZE_DEFAULT);
final int fileLimitBytes = BugleGservices.get().getInt(
BugleGservicesKeys.PERSISTENT_LOGSAVER_FILE_LIMIT_BYTES,
BugleGservicesKeys.PERSISTENT_LOGSAVER_FILE_LIMIT_BYTES_DEFAULT);
return setSize == mSetSize && fileLimitBytes == mFileLimitBytes;
}
}
private static String getLevelString(final int level) {
switch (level) {
case android.util.Log.DEBUG:
return "D";
case android.util.Log.WARN:
return "W";
case android.util.Log.INFO:
return "I";
case android.util.Log.VERBOSE:
return "V";
case android.util.Log.ERROR:
return "E";
case android.util.Log.ASSERT:
return "A";
default:
return "?";
}
}
}
@@ -36,33 +36,6 @@ public class LogUtil {
public static final int INFO = android.util.Log.INFO;
public static final int ERROR = android.util.Log.ERROR;
// If this is non-null, DEBUG and higher logs will be tracked in-memory. It will not include
// VERBOSE logs.
private static LogSaver sDebugLogSaver;
private static volatile boolean sCaptureDebugLogs;
/**
* Read Gservices to see if logging should be enabled.
*/
public static void refreshGservices(final BugleGservices gservices) {
sCaptureDebugLogs = gservices.getBoolean(
BugleGservicesKeys.ENABLE_LOG_SAVER,
BugleGservicesKeys.ENABLE_LOG_SAVER_DEFAULT);
if (sCaptureDebugLogs && (sDebugLogSaver == null || !sDebugLogSaver.isCurrent())) {
// We were not capturing logs before. We are now.
sDebugLogSaver = LogSaver.newInstance();
} else if (!sCaptureDebugLogs && sDebugLogSaver != null) {
// We were capturing logs. We aren't anymore.
sDebugLogSaver = null;
}
}
// This is called from FactoryImpl once the Gservices class is initialized.
public static void initializeGservices (final BugleGservices gservices) {
gservices.registerForChanges(() -> refreshGservices(gservices));
refreshGservices(gservices);
}
/**
* Send a {@link #VERBOSE} log message.
* @param tag Used to identify the source of a log message. It usually identifies
@@ -214,26 +187,6 @@ public class LogUtil {
*/
private static void println(final int level, final String tag, final String msg) {
android.util.Log.println(level, tag, msg);
LogSaver serviceLog = sDebugLogSaver;
if (serviceLog != null && level >= android.util.Log.DEBUG) {
serviceLog.log(level, tag, msg);
}
}
/**
* Save logging into LogSaver only, for dumping to bug report
*
* @param level The priority/type of this log message
* @param tag Used to identify the source of a log message. It usually identifies
* the class or activity where the log call occurs.
* @param msg The message you would like logged.
*/
public static void save(final int level, final String tag, final String msg) {
LogSaver serviceLog = sDebugLogSaver;
if (serviceLog != null) {
serviceLog.log(level, tag, msg);
}
}
/**
@@ -260,11 +213,4 @@ public class LogUtil {
return "Redacted-" + text.length();
}
}
public static void dump(java.io.PrintWriter out) {
final LogSaver logsaver = sDebugLogSaver;
if (logsaver != null) {
logsaver.dump(out);
}
}
}
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,8 +60,6 @@ public class LoggingTimer {
final String logMessage = String.format("Used %dms for %s", elapsedMs, mName);
LogUtil.save(LogUtil.DEBUG, mTag, logMessage);
if (mWarnLimitMillis != NO_WARN_LIMIT && elapsedMs > mWarnLimitMillis) {
LogUtil.w(mTag, logMessage);
} else if (LogUtil.isLoggable(mTag, LogUtil.VERBOSE)) {