blob: 265517647a3f684ac4761195c6c9f8ece2fd5967 [file] [log] [blame]
// 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.chrome.browser;
import android.support.annotation.IntDef;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.metrics.RecordHistogram;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Collects information about the HTTP headers passed into an Intent as Browser.EXTRA_HEADERS and
* records UMA. Call {@link #recordHeader} for each header in the Intent and {@link #report}
* afterwards.
*
* Lifecycle: Create an instance of this class for each Intent whose Headers you want to record.
* Thread safety: All methods on this class should be called on the UI thread.
*/
public class IntentHeadersRecorder {
/** Determines whether a header is CORS Safelisted or not. */
@JNINamespace("chrome::android")
/* package */ static class HeaderClassifier {
/* package */ boolean isCorsSafelistedHeader(String name, String value) {
return nativeIsCorsSafelistedHeader(name, value);
}
}
private static native boolean nativeIsCorsSafelistedHeader(String name, String value);
@Retention(RetentionPolicy.SOURCE)
@IntDef({IntentHeadersResult.FIRST_PARTY_NO_HEADERS,
IntentHeadersResult.FIRST_PARTY_ONLY_SAFE_HEADERS,
IntentHeadersResult.FIRST_PARTY_UNSAFE_HEADERS,
IntentHeadersResult.THIRD_PARTY_NO_HEADERS,
IntentHeadersResult.THIRD_PARTY_ONLY_SAFE_HEADERS,
IntentHeadersResult.THIRD_PARTY_UNSAFE_HEADERS})
@VisibleForTesting
@interface IntentHeadersResult {
// Don't reuse or reorder values. If you add something, update NUM_ENTRIES.
int FIRST_PARTY_NO_HEADERS = 0;
int FIRST_PARTY_ONLY_SAFE_HEADERS = 1;
int FIRST_PARTY_UNSAFE_HEADERS = 2;
int THIRD_PARTY_NO_HEADERS = 3;
int THIRD_PARTY_ONLY_SAFE_HEADERS = 4;
int THIRD_PARTY_UNSAFE_HEADERS = 5;
int NUM_ENTRIES = 6;
}
private final HeaderClassifier mClassifier;
private int mSafeHeaders;
private int mUnsafeHeaders;
/** Creates this class with a custom classifier (for testing). */
public IntentHeadersRecorder(HeaderClassifier classifier) {
mClassifier = classifier;
}
/** Creates this class with a classifier that checks Chrome native code. */
public IntentHeadersRecorder() {
this(new HeaderClassifier());
}
/* Records that a HTTP header has been used. */
public void recordHeader(String name, String value) {
if (mClassifier.isCorsSafelistedHeader(name, value)) mSafeHeaders++;
else mUnsafeHeaders++;
}
/**
* Logs the types of headers that have previously been {@link #recordHeader}ed.
* @param firstParty Whether the Intent is from a first or third party app. As it is just for
* logging, this is not security sensitive.
*/
public void report(boolean firstParty) {
if (firstParty) {
if (mSafeHeaders == 0 && mUnsafeHeaders == 0) {
record(IntentHeadersResult.FIRST_PARTY_NO_HEADERS);
} else if (mUnsafeHeaders == 0) {
record(IntentHeadersResult.FIRST_PARTY_ONLY_SAFE_HEADERS);
} else {
record(IntentHeadersResult.FIRST_PARTY_UNSAFE_HEADERS);
}
} else {
if (mSafeHeaders == 0 && mUnsafeHeaders == 0) {
record(IntentHeadersResult.THIRD_PARTY_NO_HEADERS);
} else if (mUnsafeHeaders == 0) {
record(IntentHeadersResult.THIRD_PARTY_ONLY_SAFE_HEADERS);
} else {
record(IntentHeadersResult.THIRD_PARTY_UNSAFE_HEADERS);
}
}
}
private static void record(@IntentHeadersResult int result) {
RecordHistogram.recordEnumeratedHistogram("Android.IntentHeaders", result,
IntentHeadersResult.NUM_ENTRIES);
}
}