diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/CompleteLogLine.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/CompleteLogLine.java index bbdc61f654..b5a5e79b0a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/CompleteLogLine.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/CompleteLogLine.java @@ -29,4 +29,9 @@ public class CompleteLogLine implements LogLine { public @NonNull Style getStyle() { return line.getStyle(); } + + @Override + public @NonNull Placeholder getPlaceholderType() { + return line.getPlaceholderType(); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogLine.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogLine.java index 304071ab8d..2efd866d6b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogLine.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogLine.java @@ -12,15 +12,13 @@ public interface LogLine { long getId(); @NonNull String getText(); @NonNull Style getStyle(); - - static List fromText(@NonNull CharSequence text) { - return Stream.of(Pattern.compile("\\n").split(text)) - .map(s -> new SimpleLogLine(s, Style.NONE)) - .map(line -> (LogLine) line) - .toList(); - } + @NonNull Placeholder getPlaceholderType(); enum Style { NONE, VERBOSE, DEBUG, INFO, WARNING, ERROR } + + enum Placeholder { + NONE, TRACE + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionTrace.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionTrace.java index 4441b4fe32..3d140a5f8b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionTrace.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionTrace.java @@ -20,19 +20,6 @@ public class LogSectionTrace implements LogSection { @Override public @NonNull CharSequence getContent(@NonNull Context context) { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - GZIPOutputStream compressedStream = new GZIPOutputStream(outputStream)) - { - compressedStream.write(Tracer.getInstance().serialize()); - compressedStream.flush(); - compressedStream.close(); - - outputStream.flush(); - outputStream.close(); - - return Base64.encodeBytes(outputStream.toByteArray()); - } catch (IOException e) { - return ""; - } + return LogStyleParser.TRACE_PLACEHOLDER; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogStyleParser.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogStyleParser.java index 56ab1a96ab..c8605791bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogStyleParser.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogStyleParser.java @@ -7,6 +7,8 @@ import java.util.Map; public class LogStyleParser { + public static final String TRACE_PLACEHOLDER = ""; + private static final Map STYLE_MARKERS = new HashMap() {{ put(" V ", LogLine.Style.VERBOSE); put(" D ", LogLine.Style.DEBUG); @@ -15,7 +17,7 @@ public class LogStyleParser { put(" E ", LogLine.Style.ERROR); }}; - public static LogLine.Style parseStyle(@NonNull String text) { + public static @NonNull LogLine.Style parseStyle(@NonNull String text) { for (Map.Entry entry : STYLE_MARKERS.entrySet()) { if (text.contains(entry.getKey())) { return entry.getValue(); @@ -23,4 +25,12 @@ public class LogStyleParser { } return LogLine.Style.NONE; } + + public static @NonNull LogLine.Placeholder parsePlaceholderType(@NonNull String text) { + if (text.equals(TRACE_PLACEHOLDER)) { + return LogLine.Placeholder.TRACE; + } else { + return LogLine.Placeholder.NONE; + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SimpleLogLine.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SimpleLogLine.java index 90c6017fce..1e9226bb0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SimpleLogLine.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SimpleLogLine.java @@ -7,14 +7,16 @@ import androidx.annotation.NonNull; */ class SimpleLogLine implements LogLine { - static final SimpleLogLine EMPTY = new SimpleLogLine("", Style.NONE); + static final SimpleLogLine EMPTY = new SimpleLogLine("", Style.NONE, Placeholder.NONE); - private final String text; - private final Style style; + private final String text; + private final Style style; + private final Placeholder placeholder; - SimpleLogLine(@NonNull String text, @NonNull Style style) { - this.text = text; - this.style = style; + SimpleLogLine(@NonNull String text, @NonNull Style style, @NonNull Placeholder placeholder) { + this.text = text; + this.style = style; + this.placeholder = placeholder; } @Override @@ -22,11 +24,18 @@ class SimpleLogLine implements LogLine { return -1; } + @Override public @NonNull String getText() { return text; } + @Override public @NonNull Style getStyle() { return style; } + + @Override + public @NonNull Placeholder getPlaceholderType() { + return placeholder; + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java index 60c18dc594..49f25e7e24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogRepository.java @@ -4,6 +4,7 @@ import android.content.Context; import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import com.annimon.stream.Stream; @@ -82,16 +83,48 @@ public class SubmitDebugLogRepository { } public void submitLog(@NonNull List lines, Callback> callback) { - SignalExecutors.UNBOUNDED.execute(() -> callback.onResult(submitLogInternal(lines))); + SignalExecutors.UNBOUNDED.execute(() -> callback.onResult(submitLogInternal(lines, null))); + } + + public void submitLog(@NonNull List lines, @Nullable byte[] trace, Callback> callback) { + SignalExecutors.UNBOUNDED.execute(() -> callback.onResult(submitLogInternal(lines, trace))); } @WorkerThread - private @NonNull Optional submitLogInternal(@NonNull List lines) { - StringBuilder bodyBuilder = new StringBuilder(); - for (LogLine line : lines) { - bodyBuilder.append(line.getText()).append('\n'); + private @NonNull Optional submitLogInternal(@NonNull List lines, @Nullable byte[] trace) { + String traceUrl = null; + if (trace != null) { + try { + traceUrl = uploadContent("application/octet-stream", trace); + } catch (IOException e) { + Log.w(TAG, "Error during trace upload.", e); + return Optional.absent(); + } } + StringBuilder bodyBuilder = new StringBuilder(); + for (LogLine line : lines) { + switch (line.getPlaceholderType()) { + case NONE: + bodyBuilder.append(line.getText()).append('\n'); + break; + case TRACE: + bodyBuilder.append(traceUrl).append('\n'); + break; + } + } + + try { + String logUrl = uploadContent("text/plain", bodyBuilder.toString().getBytes()); + return Optional.of(logUrl); + } catch (IOException e) { + Log.w(TAG, "Error during log upload.", e); + return Optional.absent(); + } + } + + @WorkerThread + private @NonNull String uploadContent(@NonNull String contentType, @NonNull byte[] content) throws IOException { try { OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new StandardUserAgentInterceptor()).dns(SignalServiceNetworkAccess.DNS).build(); Response response = client.newCall(new Request.Builder().url(API_ENDPOINT).get().build()).execute(); @@ -108,14 +141,14 @@ public class SubmitDebugLogRepository { MultipartBody.Builder post = new MultipartBody.Builder(); Iterator keys = fields.keys(); - post.addFormDataPart("Content-Type", "text/plain"); + post.addFormDataPart("Content-Type", contentType); while (keys.hasNext()) { String key = keys.next(); post.addFormDataPart(key, fields.getString(key)); } - post.addFormDataPart("file", "file", RequestBody.create(MediaType.parse("text/plain"), bodyBuilder.toString())); + post.addFormDataPart("file", "file", RequestBody.create(MediaType.parse(contentType), content)); Response postResponse = client.newCall(new Request.Builder().url(url).post(post.build()).build()).execute(); @@ -123,10 +156,10 @@ public class SubmitDebugLogRepository { throw new IOException("Bad response: " + postResponse); } - return Optional.of(API_ENDPOINT + "/" + item); - } catch (IOException | JSONException e) { + return API_ENDPOINT + "/" + item; + } catch (JSONException e) { Log.w(TAG, "Error during upload.", e); - return Optional.absent(); + throw new IOException(e); } } @@ -166,12 +199,12 @@ public class SubmitDebugLogRepository { long startTime = System.currentTimeMillis(); List out = new ArrayList<>(); - out.add(new SimpleLogLine(formatTitle(section.getTitle(), maxTitleLength), LogLine.Style.NONE)); + out.add(new SimpleLogLine(formatTitle(section.getTitle(), maxTitleLength), LogLine.Style.NONE, LogLine.Placeholder.NONE)); CharSequence content = Scrubber.scrub(section.getContent(context)); List lines = Stream.of(Pattern.compile("\\n").split(content)) - .map(s -> new SimpleLogLine(s, LogStyleParser.parseStyle(s))) + .map(s -> new SimpleLogLine(s, LogStyleParser.parseStyle(s), LogStyleParser.parsePlaceholderType(s))) .map(line -> (LogLine) line) .toList(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java index 3d6ae39443..414d305a94 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/SubmitDebugLogViewModel.java @@ -10,6 +10,7 @@ import androidx.lifecycle.ViewModelProvider; import com.annimon.stream.Stream; +import org.thoughtcrime.securesms.tracing.Tracer; import org.thoughtcrime.securesms.util.DefaultValueLiveData; import org.whispersystems.libsignal.util.guava.Optional; @@ -23,12 +24,17 @@ public class SubmitDebugLogViewModel extends ViewModel { private final MutableLiveData mode; private List sourceLines; + private byte[] trace; private SubmitDebugLogViewModel() { this.repo = new SubmitDebugLogRepository(); this.lines = new DefaultValueLiveData<>(Collections.emptyList()); this.mode = new MutableLiveData<>(); + if (Tracer.getInstance().isEnabled()) { + this.trace = Tracer.getInstance().serialize(); + } + repo.getLogLines(result -> { sourceLines = result; mode.postValue(Mode.NORMAL); @@ -40,10 +46,6 @@ public class SubmitDebugLogViewModel extends ViewModel { return lines; } - boolean hasLines() { - return lines.getValue().size() > 0; - } - @NonNull LiveData getMode() { return mode; } @@ -53,7 +55,7 @@ public class SubmitDebugLogViewModel extends ViewModel { MutableLiveData> result = new MutableLiveData<>(); - repo.submitLog(lines.getValue(), value -> { + repo.submitLog(lines.getValue(), trace, value -> { mode.postValue(Mode.NORMAL); result.postValue(value); });