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
@@ -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)) {