Add additional info to support emails and debuglogs.

master
Greyson Parrelli 2020-05-06 15:02:26 -04:00 committed by Alex Hart
parent b156e4a79a
commit 3fef58057e
7 changed files with 187 additions and 79 deletions

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.help;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
@ -27,8 +28,10 @@ import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
import org.thoughtcrime.securesms.util.AppSignatureUtil;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.SupportEmailUtil;
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
import java.util.ArrayList;
@ -150,70 +153,36 @@ public class HelpFragment extends Fragment {
.findFirst().orElse(null);
CommunicationActions.openEmail(requireContext(),
getString(R.string.RegistrationActivity_support_email),
SupportEmailUtil.getSupportEmailAddress(requireContext()),
getEmailSubject(),
getEmailBody(debugLog, feeling).toString());
getEmailBody(debugLog, feeling));
}
private String getEmailSubject() {
return getString(R.string.HelpFragment__signal_android_support_request);
}
private Spanned getEmailBody(@Nullable String debugLog, @Nullable Feeling feeling) {
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(problem.getText().toString());
builder.append("\n\n");
builder.append("--- ");
builder.append(getString(R.string.HelpFragment__support_info));
builder.append(" ---\n");
builder.append(getString(R.string.HelpFragment__subject));
builder.append(" ");
builder.append(getString(R.string.HelpFragment__signal_android_support_request));
builder.append("\n");
builder.append(getString(R.string.HelpFragment__device_info));
builder.append(" ");
builder.append(getDeviceInfo());
builder.append("\n");
builder.append(getString(R.string.HelpFragment__android_version));
builder.append(" ");
builder.append(getAndroidVersion());
builder.append("\n");
builder.append(getString(R.string.HelpFragment__signal_version));
builder.append(" ");
builder.append(getSignalVersion());
builder.append("\n");
builder.append(getString(R.string.HelpFragment__locale));
builder.append(" ");
builder.append(Locale.getDefault().toString());
private String getEmailBody(@Nullable String debugLog, @Nullable Feeling feeling) {
StringBuilder suffix = new StringBuilder();
if (debugLog != null) {
builder.append("\n");
builder.append(getString(R.string.HelpFragment__debug_log));
builder.append(" ");
builder.append(debugLog);
suffix.append("\n");
suffix.append(getString(R.string.HelpFragment__debug_log));
suffix.append(" ");
suffix.append(debugLog);
}
if (feeling != null) {
builder.append("\n\n");
builder.append(feeling.getEmojiCode());
builder.append("\n");
builder.append(getString(feeling.getStringId()));
suffix.append("\n\n");
suffix.append(feeling.getEmojiCode());
suffix.append("\n");
suffix.append(getString(feeling.getStringId()));
}
return builder;
}
private static CharSequence getDeviceInfo() {
return String.format("%s %s (%s)", Build.MANUFACTURER, Build.MODEL, Build.PRODUCT);
}
private static CharSequence getAndroidVersion() {
return String.format("%s (%s, %s)", Build.VERSION.RELEASE, Build.VERSION.INCREMENTAL, Build.DISPLAY);
}
private static CharSequence getSignalVersion() {
return BuildConfig.VERSION_NAME;
return SupportEmailUtil.generateSupportEmailBody(requireContext(),
getString(R.string.HelpFragment__signal_android_support_request),
problem.getText().toString() + "\n\n",
suffix.toString());
}
private static void setSpinning(@Nullable CircularProgressButton button) {

View File

@ -10,6 +10,8 @@ import android.view.WindowManager;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.util.AppSignatureUtil;
import org.thoughtcrime.securesms.util.ByteUnit;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -60,6 +62,7 @@ public class LogSectionSystemInfo implements LogSection {
} catch (PackageManager.NameNotFoundException nnfe) {
builder.append("Unknown\n");
}
builder.append("Package : ").append(BuildConfig.APPLICATION_ID).append(" (").append(getSigningString(context)).append(")");
return builder;
}
@ -134,4 +137,8 @@ public class LogSectionSystemInfo implements LogSection {
private static @NonNull String getScreenRefreshRate(@NonNull Context context) {
return String.format(Locale.ENGLISH, "%.2f hz", ServiceUtil.getWindowManager(context).getDefaultDisplay().getRefreshRate());
}
private static String getSigningString(@NonNull Context context) {
return AppSignatureUtil.getAppSignature(context).or("Unknown");
}
}

View File

@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.registration.RegistrationUtil;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.SupportEmailUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Locale;
@ -195,14 +196,14 @@ public class PinRestoreEntryFragment extends Fragment {
.setMessage(getString(R.string.PinRestoreEntryFragment_your_pin_is_a_d_digit_code, KbsConstants.MINIMUM_PIN_LENGTH))
.setPositiveButton(R.string.PinRestoreEntryFragment_create_new_pin, null)
.setNeutralButton(R.string.PinRestoreEntryFragment_contact_support, (dialog, which) -> {
String body = SupportEmailUtil.generateSupportEmailBody(requireContext(),
getString(R.string.PinRestoreEntryFragment_signal_registration_need_help_with_pin),
null,
null);
CommunicationActions.openEmail(requireContext(),
getString(R.string.PinRestoreEntryFragment_support_email),
SupportEmailUtil.getSupportEmailAddress(requireContext()),
getString(R.string.PinRestoreEntryFragment_signal_registration_need_help_with_pin),
getString(R.string.PinRestoreEntryFragment_subject_signal_registration,
getDevice(),
getAndroidVersion(),
BuildConfig.VERSION_NAME,
Locale.getDefault()));
body);
})
.setNegativeButton(R.string.PinRestoreEntryFragment_cancel, null)
.show();

View File

@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.registration.service.RegistrationCodeRequest;
import org.thoughtcrime.securesms.registration.service.RegistrationService;
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
import org.thoughtcrime.securesms.util.CommunicationActions;
import org.thoughtcrime.securesms.util.SupportEmailUtil;
import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
@ -306,21 +307,13 @@ public final class EnterCodeFragment extends BaseRegistrationFragment {
}
private void sendEmailToSupport() {
String body = SupportEmailUtil.generateSupportEmailBody(requireContext(),
getString(R.string.RegistrationActivity_code_support_subject),
null,
null);
CommunicationActions.openEmail(requireContext(),
getString(R.string.RegistrationActivity_support_email),
SupportEmailUtil.getSupportEmailAddress(requireContext()),
getString(R.string.RegistrationActivity_code_support_subject),
getString(R.string.RegistrationActivity_code_support_body,
getDevice(),
getAndroidVersion(),
BuildConfig.VERSION_NAME,
Locale.getDefault()));
}
private static String getDevice() {
return String.format("%s %s (%s)", Build.MANUFACTURER, Build.MODEL, Build.PRODUCT);
}
private static String getAndroidVersion() {
return String.format("%s (%s, %s)", Build.VERSION.RELEASE, Build.VERSION.INCREMENTAL, Build.DISPLAY);
body);
}
}

View File

@ -0,0 +1,71 @@
package org.thoughtcrime.securesms.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.logging.Log;
import org.whispersystems.libsignal.util.guava.Optional;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public final class AppSignatureUtil {
private static final String TAG = Log.tag(AppSignatureUtil.class);
private static final String HASH_TYPE = "SHA-256";
private static final int HASH_LENGTH_BYTES = 9;
private static final int HASH_LENGTH_CHARS = 11;
private AppSignatureUtil() {}
/**
* Only intended to be used for logging.
*/
@SuppressLint("PackageManagerGetSignatures")
public static Optional<String> getAppSignature(@NonNull Context context) {
try {
String packageName = context.getPackageName();
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
Signature[] signatures = packageInfo.signatures;
if (signatures.length > 0) {
String hash = hash(packageName, signatures[0].toCharsString());
return Optional.fromNullable(hash);
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, e);
}
return Optional.absent();
}
private static String hash(String packageName, String signature) {
String appInfo = packageName + " " + signature;
try {
MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
byte[] hashSignature = messageDigest.digest();
hashSignature = Arrays.copyOfRange(hashSignature, 0, HASH_LENGTH_BYTES);
String base64Hash = Base64.encodeBytes(hashSignature);
base64Hash = base64Hash.substring(0, HASH_LENGTH_CHARS);
return base64Hash;
} catch (NoSuchAlgorithmException e) {
Log.w(TAG, e);
}
return null;
}
}

View File

@ -0,0 +1,66 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import java.util.Locale;
public final class SupportEmailUtil {
private SupportEmailUtil() { }
public static @NonNull String getSupportEmailAddress(@NonNull Context context) {
return context.getString(R.string.SupportEmailUtil_support_email);
}
/**
* Generates a support email body with system info near the top.
*/
public static @NonNull String generateSupportEmailBody(@NonNull Context context,
@NonNull String subject,
@Nullable String prefix,
@Nullable String suffix)
{
prefix = Util.firstNonNull(prefix, "");
suffix = Util.firstNonNull(suffix, "");
return String.format("%s\n%s\n%s", prefix, buildSystemInfo(context, subject), suffix);
}
private static @NonNull String buildSystemInfo(@NonNull Context context, @NonNull String subject) {
return "--- " + context.getString(R.string.HelpFragment__support_info) + " ---" +
"\n" +
context.getString(R.string.SupportEmailUtil_subject) + " " + subject +
"\n" +
context.getString(R.string.SupportEmailUtil_device_info) + " " + getDeviceInfo() +
"\n" +
context.getString(R.string.SupportEmailUtil_android_version) + " " + getAndroidVersion() +
"\n" +
context.getString(R.string.SupportEmailUtil_signal_version) + " " + getSignalVersion() +
"\n" +
context.getString(R.string.SupportEmailUtil_signal_package) + " " + getSignalPackage(context) +
"\n" +
context.getString(R.string.SupportEmailUtil_locale) + " " + Locale.getDefault().toString();
}
private static CharSequence getDeviceInfo() {
return String.format("%s %s (%s)", Build.MANUFACTURER, Build.MODEL, Build.PRODUCT);
}
private static CharSequence getAndroidVersion() {
return String.format("%s (%s, %s)", Build.VERSION.RELEASE, Build.VERSION.INCREMENTAL, Build.DISPLAY);
}
private static CharSequence getSignalVersion() {
return BuildConfig.VERSION_NAME;
}
private static CharSequence getSignalPackage(@NonNull Context context) {
return String.format("%s (%s)", BuildConfig.APPLICATION_ID, AppSignatureUtil.getAppSignature(context).or("Unknown"));
}
}

View File

@ -856,9 +856,7 @@
<item quantity="one">You have %1$d attempt remaining. If you run out of attempts, you can create a new PIN. You can register and use your account but you\'ll lose some saved settings like your profile information.</item>
<item quantity="other">You have %1$d attempts remaining. If you run out of attempts, you can create a new PIN. You can register and use your account but you\'ll lose some saved settings like your profile information.</item>
</plurals>
<string name="PinRestoreEntryFragment_support_email" translatable="false">support@signal.org</string>
<string name="PinRestoreEntryFragment_signal_registration_need_help_with_pin">Signal Registration - Need Help with PIN for Android</string>
<string name="PinRestoreEntryFragment_subject_signal_registration">Subject: Signal Registration - Need Help with PIN for Android\nDevice info: %1$s\nAndroid version: %2$s\nSignal version: %3$s\nLocale: %4$s</string>
<string name="PinRestoreEntryFragment_enter_alphanumeric_pin">Enter alphanumeric PIN</string>
<string name="PinRestoreEntryFragment_enter_numeric_pin">Enter numeric PIN</string>
@ -1054,6 +1052,16 @@
<string name="SubmitDebugLogActivity_ok">Ok</string>
<string name="SubmitDebugLogActivity_share">Share</string>
<!-- SupportEmailUtil -->
<string name="SupportEmailUtil_support_email" translatable="false">support@signal.org</string>
<string name="SupportEmailUtil_subject">Subject:</string>
<string name="SupportEmailUtil_signal_android_support_request">Signal Android Support Request</string>
<string name="SupportEmailUtil_device_info">Device info:</string>
<string name="SupportEmailUtil_android_version">Android version:</string>
<string name="SupportEmailUtil_signal_version">Signal version:</string>
<string name="SupportEmailUtil_signal_package">Signal package:</string>
<string name="SupportEmailUtil_locale">Locale:</string>
<!-- ThreadRecord -->
<string name="ThreadRecord_group_updated">Group updated</string>
<string name="ThreadRecord_left_the_group">Left the group</string>
@ -1634,12 +1642,7 @@
<string name="HelpFragment__link__debug_info" translatable="false">https://support.signal.org/hc/articles/360007318591</string>
<string name="HelpFragment__link__faq" translatable="false">https://support.signal.org</string>
<string name="HelpFragment__support_info">Support Info</string>
<string name="HelpFragment__subject">Subject:</string>
<string name="HelpFragment__signal_android_support_request">Signal Android Support Request</string>
<string name="HelpFragment__device_info">Device info:</string>
<string name="HelpFragment__android_version">Android version:</string>
<string name="HelpFragment__signal_version">Signal version:</string>
<string name="HelpFragment__locale">Locale:</string>
<string name="HelpFragment__debug_log">Debug Log:</string>
<string name="HelpFragment__na">n/a</string>
<string name="HelpFragment__could_not_upload_logs">Could not upload logs</string>
@ -2159,9 +2162,7 @@
<string name="RegistrationActivity_wrong_number">Wrong number</string>
<string name="RegistrationActivity_call_me_instead_available_in">Call me instead \n (Available in %1$02d:%2$02d)</string>
<string name="RegistrationActivity_contact_signal_support">Contact Signal Support</string>
<string name="RegistrationActivity_support_email" translatable="false">support@signal.org</string>
<string name="RegistrationActivity_code_support_subject">Signal Registration - Verification Code for Android</string>
<string name="RegistrationActivity_code_support_body">Subject: Signal Registration - Verification Code for Android\nDevice info: %1$s\nAndroid version: %2$s\nSignal version: %3$s\nLocale: %4$s</string>
<string name="BackupUtil_never">Never</string>
<string name="BackupUtil_unknown">Unknown</string>
<string name="preferences_app_protection__screen_lock">Screen lock</string>