Skip to content

Commit

Permalink
[Chromecast] Added AIDL to get assistant device logs
Browse files Browse the repository at this point in the history
Bug: 71798364
Bug: 71646628
Test: Send feedback on home app
Change-Id: Iba23a4e07369a9557a8ef43c23a7f93f3184a9c6
Reviewed-on: https://chromium-review.googlesource.com/882493
Commit-Queue: Sandeep Vijayasekar <sandv@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Luke Halliwell <halliwell@chromium.org>
Reviewed-by: Stephen Lanham <slan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#531793}
  • Loading branch information
Sandeep Vijayasekar authored and Commit Bot committed Jan 25, 2018
1 parent a6edf46 commit 4ddbad7
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 130 deletions.
29 changes: 26 additions & 3 deletions chromecast/browser/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ java_cpp_template("cast_shell_build_config_gen") {

# Set the logcat extractor size in lines.
defines += [ "_LOGCAT_EXTRACTOR_SIZE=${logcat_extractor_size}" ]

if (use_remote_service_logcat) {
defines += [ "_USE_REMOTE_SERVICE_LOGCAT" ]
}

# Component name which will provide the device logs
defines +=
[ "_DEVICE_LOGS_PROVIDER_PACKAGE=\"${device_logs_provider_package}\"" ]
defines += [ "_DEVICE_LOGS_PROVIDER_CLASS=\"${device_logs_provider_class}\"" ]
}

android_resources("cast_shell_android_resources") {
Expand All @@ -57,10 +66,21 @@ android_library("cast_intents_java") {
java_files =
[ "$java_src_dir/org/chromium/chromecast/shell/CastIntents.java" ]
}
android_aidl("logs_provider_aidl") {
java_src_dir = "//chromecast/browser/android/apk/src"
interface_file =
"$java_src_dir/org/chromium/chromecast/shell/logs_provider_service.aidl"

sources = [
"$java_src_dir/org/chromium/chromecast/shell/IDeviceLogsProvider.aidl",
]
}

android_library("cast_shell_java") {
java_src_dir = "//chromecast/browser/android/apk/src"
java_files = [
"$java_src_dir/org/chromium/chromecast/shell/AndroidAppLogcatProvider.java",
"$java_src_dir/org/chromium/chromecast/shell/ExternalServiceDeviceLogcatProvider.java",
"$java_src_dir/org/chromium/chromecast/shell/CastApplication.java",
"$java_src_dir/org/chromium/chromecast/shell/CastBrowserHelper.java",
"$java_src_dir/org/chromium/chromecast/shell/CastContentWindowAndroid.java",
Expand All @@ -75,12 +95,15 @@ android_library("cast_shell_java") {
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsComponent.java",
"$java_src_dir/org/chromium/chromecast/shell/LogcatElision.java",
"$java_src_dir/org/chromium/chromecast/shell/LogcatExtractor.java",
"$java_src_dir/org/chromium/chromecast/shell/ElidedLogcatProvider.java",
]

android_manifest_for_lint = cast_shell_android_manifest

srcjar_deps = [ ":cast_shell_build_config_gen" ]
srcjar_deps = [
":cast_shell_build_config_gen",
":logs_provider_aidl",
]

deps = [
":cast_audio_manager_java",
Expand Down Expand Up @@ -109,7 +132,7 @@ junit_binary("cast_shell_junit_tests") {
java_files = [
"junit/src/org/chromium/chromecast/shell/CastWebContentsComponentTest.java",
"junit/src/org/chromium/chromecast/shell/LogcatElisionUnitTest.java",
"junit/src/org/chromium/chromecast/shell/LogcatExtractorUnitTest.java",
"junit/src/org/chromium/chromecast/shell/ElidedLogcatProviderUnitTest.java",
]

srcjar_deps = [ ":cast_shell_build_config_gen" ]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chromecast.shell;

import org.chromium.base.Log;
import org.chromium.chromecast.base.CircularBuffer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;

/**
* Extracts logcat out of Android devices and elide PII sensitive info from it.
*
* <p>Elided information includes: Emails, IP address, MAC address, URL/domains as well as
* Javascript console messages.
*/
class AndroidAppLogcatProvider extends ElidedLogcatProvider {
private static final String TAG = "LogcatProvider";
private static final long HALF_SECOND = 500;

@Override
protected void getRawLogcat(RawLogcatCallback callback) {
CircularBuffer<String> rawLogcat = new CircularBuffer<>(BuildConfig.LOGCAT_SIZE);
String logLn = null;
Integer exitValue = null;

try {
Process p = Runtime.getRuntime().exec("logcat -d");
try (BufferedReader bReader = new BufferedReader(
new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8")))) {
while (exitValue == null) {
while ((logLn = bReader.readLine()) != null) {
// Add each new string to the end of the buffer.
rawLogcat.add(logLn);
}
try {
exitValue = p.exitValue();
} catch (IllegalThreadStateException itse) {
Thread.sleep(HALF_SECOND);
}
}
if (exitValue != 0) {
String msg = "Logcat process exit value: " + exitValue;
Log.w(TAG, msg);
}
} catch (UnsupportedCharsetException e) {
// Should never happen; all Java implementations are required to support UTF-8.
Log.wtf(TAG, "UTF-8 not supported", e);
} catch (InterruptedException e) {
Log.e(TAG, "Logcat subprocess interrupted ", e);
}
} catch (IOException e) {
Log.e(TAG, "Error occurred trying to upload crash dump", e);
} finally {
callback.onLogsDone(rawLogcat);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
Expand All @@ -44,20 +42,21 @@ public final class CastCrashUploader {
private static final String DUMP_FILE_REGEX = ".*\\.dmp\\d*";

private final ScheduledExecutorService mExecutorService;
private final ElidedLogcatProvider mLogcatProvider;
private final String mCrashDumpPath;
private final String mCrashReportUploadUrl;
private final String mUuid;
private final String mApplicationFeedback;
private final Runnable mQueueAllCrashDumpUploadsRunnable =
() -> queueAllCrashDumpUploads(false);
private final Runnable mQueueAllCrashDumpUploadsRunnable = () -> queueAllCrashDumpUploads();

public CastCrashUploader(ScheduledExecutorService executorService, String crashDumpPath,
String uuid, String applicationFeedback, boolean uploadCrashToStaging) {
public CastCrashUploader(ScheduledExecutorService executorService,
ElidedLogcatProvider logcatProvider, String crashDumpPath, String uuid,
String applicationFeedback, boolean uploadCrashToStaging) {
mExecutorService = executorService;
mLogcatProvider = logcatProvider;
mCrashDumpPath = crashDumpPath;
mUuid = uuid;
mApplicationFeedback = applicationFeedback;

mCrashReportUploadUrl = uploadCrashToStaging
? "https://clients2.google.com/cr/staging_report"
: "https://clients2.google.com/cr/report";
Expand Down Expand Up @@ -93,47 +92,22 @@ public void removeCrashDumps() {
* @param synchronous Whether or not this function should block on queued uploads
* @param log Log to include, if any
*/
private void queueAllCrashDumpUploads(boolean synchronous) {
private void queueAllCrashDumpUploads() {
if (mCrashDumpPath == null) return;
Log.i(TAG, "Checking for crash dumps");

List<Future> tasks = new ArrayList<Future>();
File crashDumpDirectory = new File(mCrashDumpPath);
mLogcatProvider.getElidedLogcat((String logs) -> queueAllCrashDumpUploadsWithLogs(logs));
}

final String log = getLogs(crashDumpDirectory);
private void queueAllCrashDumpUploadsWithLogs(String logs) {
File crashDumpDirectory = new File(mCrashDumpPath);

for (final File potentialDump : crashDumpDirectory.listFiles()) {
String dumpName = potentialDump.getName();
if (dumpName.matches(DUMP_FILE_REGEX)) {
tasks.add(mExecutorService.submit(() -> uploadCrashDump(potentialDump, log)));
}
}

// Wait on tasks, if necessary.
if (synchronous) {
for (Future task : tasks) {
// Wait on task. If thread received interrupt and should stop waiting, return.
if (!waitOnTask(task)) {
return;
}
}
}
}

private String getLogs(File crashDumpDirectory) {
String log = null;
if (crashDumpDirectory.listFiles().length > 0) {
try {
Log.i(TAG, "Getting logcat");
log = LogcatExtractor.getElidedLogcat();
Log.d(TAG, "Log size" + log.length());
return log;

} catch (IOException | InterruptedException e) {
Log.e(TAG, "Getting logcat failed ", e);
mExecutorService.submit(() -> uploadCrashDump(potentialDump, logs));
}
}
return null;
}

/** Enqueues a background task to upload a single crash dump file. */
Expand All @@ -149,7 +123,7 @@ private void uploadCrashDump(final File dumpFile, final String log) {
String dumpFirstLine = getFirstLine(dumpFileStream);
String mimeBoundary = dumpFirstLine.substring(2);

if (log != null) {
if (!log.equals("")) {
Log.i(TAG, "Including log file");
StringBuilder logHeader = new StringBuilder();
logHeader.append(dumpFirstLine);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,21 @@
*/
public final class CastCrashUploaderFactory {
private static ScheduledExecutorService sExecutorService = null;

public static CastCrashUploader createCastCrashUploader(String crashDumpPath, String uuid,
String applicationFeedback, boolean uploadCrashToStaging) {
if (sExecutorService == null) {
sExecutorService = Executors.newScheduledThreadPool(1);
}
return new CastCrashUploader(
sExecutorService, crashDumpPath, uuid, applicationFeedback, uploadCrashToStaging);
ElidedLogcatProvider logcatProvider = shouldUseRemoteServiceLogs()
? new ExternalServiceDeviceLogcatProvider()
: new AndroidAppLogcatProvider();
return new CastCrashUploader(sExecutorService, logcatProvider, crashDumpPath, uuid,
applicationFeedback, uploadCrashToStaging);
}

private static boolean shouldUseRemoteServiceLogs() {
return BuildConfig.USE_REMOTE_SERVICE_LOGCAT
&& !BuildConfig.DEVICE_LOGS_PROVIDER_PACKAGE.equals("")
&& !BuildConfig.DEVICE_LOGS_PROVIDER_CLASS.equals("");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chromecast.shell;

import org.chromium.base.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;

/**
* Utility class for dump streams
*
*/
public final class DumpStreamUtils {
private static final String TAG = "cr_DumpStreamUtils";

/**
* Gets the first line from an input stream
* Mutates given input stream.
*
* @return First line of the input stream.
* @throws IOException
*/
public static String getFirstLineFromStream(InputStream inputStream) throws IOException {
try (InputStreamReader streamReader =
new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(streamReader)) {
return reader.readLine();
} catch (UnsupportedCharsetException e) {
// Should never happen; all Java implementations are required to support UTF-8.
Log.wtf(TAG, "UTF-8 not supported", e);
return "";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chromecast.shell;

import org.chromium.base.VisibleForTesting;

/**
* Extracts logcat out of Android devices and elide PII sensitive info from it.
*
* <p>Elided information includes: Emails, IP address, MAC address, URL/domains as well as
* Javascript console messages.
*/
abstract class ElidedLogcatProvider {
protected abstract void getRawLogcat(RawLogcatCallback rawLogcatCallback);

protected interface RawLogcatCallback { public void onLogsDone(Iterable<String> logs); }
public interface LogcatCallback { public void onLogsDone(String logs); }

public void getElidedLogcat(LogcatCallback callback) {
getRawLogcat((Iterable<String> rawLogs) -> callback.onLogsDone(elideLogcat(rawLogs)));
}

@VisibleForTesting
protected static String elideLogcat(Iterable<String> rawLogcat) {
StringBuilder builder = new StringBuilder();
for (String line : rawLogcat) {
builder.append(LogcatElision.elide(line));
builder.append("\n");
}
return builder.toString();
}
}
Loading

0 comments on commit 4ddbad7

Please sign in to comment.