0

[Net] Add Java version of NetworkTrafficAnnotationTag

This class is very similar to the C++ NetworkTrafficAnnotationTag
class. It requires the same kind of documentation, in as a text
protobuf.

As a proof-of-concept, update EndpointFetcher methods to take a
NetworkTrafficAnnotationTag argument, and plumb it down to C++. For now,
use NO_TRAFFIC_ANNOTATION_YET in callers.

Since auditor.py doesn't scan Java files yet, the contents of the
annotations doesn't need to be valid for now.

Bug: 1231780, 995852
Change-Id: I798a99adfd9d2d2afee537b78d379e9f05b49a9a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3045770
Reviewed-by: David Trainor <dtrainor@chromium.org>
Reviewed-by: Ryan Sleevi <rsleevi@chromium.org>
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Reviewed-by: Wei-Yin Chen (陳威尹) <wychen@chromium.org>
Reviewed-by: Ramin Halavati <rhalavati@chromium.org>
Commit-Queue: Nicolas Ouellet-Payeur <nicolaso@chromium.org>
Cr-Commit-Position: refs/heads/main@{#917776}
This commit is contained in:
Nicolas Ouellet-Payeur
2021-09-02 19:10:36 +00:00
committed by Chromium LUCI CQ
parent b03a695762
commit e8bcd6f751
24 changed files with 242 additions and 36 deletions
chrome
android
features
tab_ui
BUILD.gn
java
src
org
chromium
chrome
browser
tasks
tab_management
junit
src
org
chromium
javatests
browser
commerce
subscriptions
android
java
src
org
chromium
chrome
test
android
java
src
org
chromium
endpoint_fetcher
BUILD.gnendpoint_fetcher.cc
java
src
org
chromium
chrome
browser
endpoint_fetcher
page_annotations
android
BUILD.gn
java
src
org
chromium
chrome
browser
test
android
BUILD.gn
java
src
org
chromium
chrome
tab
java
src
org
chromium
chrome
net
tools/traffic_annotation/auditor

@ -233,6 +233,7 @@ android_library("java") {
"//components/site_engagement/content/android:java",
"//content/public/android:content_java",
"//content/public/android:content_java_resources",
"//net/android:net_java",
"//third_party/android_deps:android_support_v7_appcompat_java",
"//third_party/android_deps:material_design_java",
"//third_party/androidx:androidx_annotation_annotation_java",

@ -21,6 +21,7 @@ import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.net.NetworkTrafficAnnotationTag;
import java.util.Collections;
import java.util.LinkedList;
@ -78,12 +79,14 @@ public class TabSuggestionsServerFetcher implements TabSuggestionsFetcher {
return;
}
}
// TODO(crbug.com/995852): Replace NO_TRAFFIC_ANNOTATION_YET with a real traffic
// annotation.
EndpointFetcher.fetchUsingChromeAPIKey(res
-> { fetchCallback(res, callback, tabContext); },
mProfileForTesting == null ? Profile.getLastUsedRegularProfile()
: mProfileForTesting,
ENDPOINT, METHOD, CONTENT_TYPE, getTabContextJson(tabContext), TIMEOUT_MS,
new String[] {});
new String[] {}, NetworkTrafficAnnotationTag.NO_TRAFFIC_ANNOTATION_YET);
} catch (JSONException e) {
// Soft failure for now so we don't crash the app and fall back on client side
// providers.

@ -3162,7 +3162,7 @@ public class TabListMediatorUnitTest {
.when(mEndpointFetcherJniMock)
.nativeFetchOAuth(any(Profile.class), anyString(), contains(entry.getKey()),
anyString(), anyString(), any(String[].class), anyString(), anyLong(),
any(Callback.class));
anyInt(), any(Callback.class));
}
}

@ -6,6 +6,7 @@ package org.chromium.chrome.browser.tasks.tab_management.suggestions;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@ -115,21 +116,21 @@ public class TabSuggestionsServerFetcherUnitTest {
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
Callback callback = (Callback) invocation.getArguments()[7];
Callback callback = (Callback) invocation.getArguments()[8];
callback.onResult(new EndpointResponse(response));
return null;
}
})
.when(mEndpointFetcherJniMock)
.nativeFetchChromeAPIKey(any(Profile.class), anyString(), anyString(), anyString(),
anyString(), anyLong(), any(String[].class), any(Callback.class));
anyString(), anyLong(), any(String[].class), anyInt(), any(Callback.class));
}
private void verifyEndpointArguments() {
verify(mEndpointFetcherJniMock)
.nativeFetchChromeAPIKey(eq(mProfile), eq(EXPECTED_ENDPOINT_URL),
eq(EXPECTED_METHOD), eq(EXPECTED_CONTENT_TYPE), any(String.class),
eq(EXPECTED_TIMEOUT), any(String[].class), any(Callback.class));
eq(EXPECTED_TIMEOUT), any(String[].class), anyInt(), any(Callback.class));
}
@Test

@ -324,7 +324,7 @@ public abstract class ShoppingPersistedTabDataTestUtils {
static void verifyEndpointFetcherCalled(EndpointFetcher.Natives endpointFetcher, int numTimes) {
verify(endpointFetcher, times(numTimes))
.nativeFetchChromeAPIKey(any(Profile.class), anyString(), anyString(), anyString(),
anyString(), anyLong(), any(String[].class), any(Callback.class));
anyString(), anyLong(), any(String[].class), anyInt(), any(Callback.class));
}
static void verifyPriceTrackingOptimizationTypeCalled(
@ -342,4 +342,4 @@ public abstract class ShoppingPersistedTabDataTestUtils {
eq(HintsProto.OptimizationType.PRICE_TRACKING.getNumber()),
any(OptimizationGuideCallback.class));
}
}
}

@ -5,6 +5,7 @@
package org.chromium.chrome.browser.tab.state;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@ -314,14 +315,14 @@ public class StorePersistedTabDataTest {
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
Callback callback = (Callback) invocation.getArguments()[8];
Callback callback = (Callback) invocation.getArguments()[9];
callback.onResult(new EndpointResponse(response));
return null;
}
})
.when(mEndpointFetcherJniMock)
.nativeFetchOAuth(any(Profile.class), anyString(), anyString(), anyString(),
anyString(), any(String[].class), anyString(), anyLong(),
anyString(), any(String[].class), anyString(), anyLong(), anyInt(),
any(Callback.class));
}
}

@ -12,6 +12,7 @@ import org.chromium.base.Callback;
import org.chromium.base.Log;
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.net.NetworkTrafficAnnotationTag;
import java.util.ArrayList;
import java.util.List;
@ -75,6 +76,8 @@ public final class CommerceSubscriptionsServiceProxy {
*/
public void get(@CommerceSubscription.CommerceSubscriptionType String type,
Callback<List<CommerceSubscription>> callback) {
// TODO(crbug.com/995852): Replace NO_TRAFFIC_ANNOTATION_YET with a real traffic
// annotation.
EndpointFetcher.fetchUsingOAuth(
(response)
-> {
@ -84,10 +87,12 @@ public final class CommerceSubscriptionsServiceProxy {
CommerceSubscriptionsServiceConfig.SUBSCRIPTIONS_SERVICE_BASE_URL.getValue()
+ String.format(GET_SUBSCRIPTIONS_QUERY_PARAMS_TEMPLATE, type),
GET_HTTPS_METHOD, CONTENT_TYPE, OAUTH_SCOPE, EMPTY_POST_DATA,
HTTPS_REQUEST_TIMEOUT_MS);
HTTPS_REQUEST_TIMEOUT_MS, NetworkTrafficAnnotationTag.NO_TRAFFIC_ANNOTATION_YET);
}
private void manageSubscriptions(JSONObject requestPayload, Callback<Boolean> callback) {
// TODO(crbug.com/995852): Replace NO_TRAFFIC_ANNOTATION_YET with a real traffic
// annotation.
EndpointFetcher.fetchUsingOAuth(
(response)
-> {
@ -97,7 +102,7 @@ public final class CommerceSubscriptionsServiceProxy {
mProfile, OAUTH_NAME,
CommerceSubscriptionsServiceConfig.SUBSCRIPTIONS_SERVICE_BASE_URL.getValue(),
POST_HTTPS_METHOD, CONTENT_TYPE, OAUTH_SCOPE, requestPayload.toString(),
HTTPS_REQUEST_TIMEOUT_MS);
HTTPS_REQUEST_TIMEOUT_MS, NetworkTrafficAnnotationTag.NO_TRAFFIC_ANNOTATION_YET);
}
private boolean didManageSubscriptionCallSucceed(String responseString) {

@ -6,6 +6,7 @@ package org.chromium.chrome.browser.subscriptions;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
@ -239,14 +240,14 @@ public class CommerceSubscriptionsServiceProxyUnitTest {
verify(mEndpointFetcherJniMock, times(numTimes))
.nativeFetchOAuth(any(Profile.class), any(String.class), eq(expectedUrl),
eq(expectedMethod), eq(EXPECTED_CONTENT_TYPE), eq(EXPECTED_OAUTH_SCOPES),
eq(expectedPayload), anyLong(), any(Callback.class));
eq(expectedPayload), anyLong(), anyInt(), any(Callback.class));
}
private void mockEndpointResponse(String response) {
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
Callback callback = (Callback) invocation.getArguments()[8];
Callback callback = (Callback) invocation.getArguments()[9];
callback.onResult(new EndpointResponse(response));
return null;
}
@ -254,6 +255,6 @@ public class CommerceSubscriptionsServiceProxyUnitTest {
.when(mEndpointFetcherJniMock)
.nativeFetchOAuth(any(Profile.class), any(String.class), any(String.class),
any(String.class), eq(EXPECTED_CONTENT_TYPE), eq(EXPECTED_OAUTH_SCOPES),
any(String.class), anyLong(), any(Callback.class));
any(String.class), anyLong(), anyInt(), any(Callback.class));
}
}

@ -9,6 +9,7 @@ android_library("java") {
":jni_headers",
"//base:base_java",
"//chrome/browser/profiles/android:java",
"//net/android:net_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
sources = [

@ -317,6 +317,7 @@ static void JNI_EndpointFetcher_NativeFetchOAuth(
const base::android::JavaParamRef<jobjectArray>& jscopes,
const base::android::JavaParamRef<jstring>& jpost_data,
jlong jtimeout,
jint jannotation_hash_code,
const base::android::JavaParamRef<jobject>& jcallback) {
std::vector<std::string> scopes;
base::android::AppendJavaStringArrayToStringVector(env, jscopes, &scopes);
@ -327,9 +328,8 @@ static void JNI_EndpointFetcher_NativeFetchOAuth(
base::android::ConvertJavaStringToUTF8(env, jhttps_method),
base::android::ConvertJavaStringToUTF8(env, jcontent_type), scopes,
jtimeout, base::android::ConvertJavaStringToUTF8(env, jpost_data),
// TODO(crbug.com/995852) Create a traffic annotation tag and configure it
// as part of the EndpointFetcher call over JNI.
NO_TRAFFIC_ANNOTATION_YET);
net::NetworkTrafficAnnotationTag::FromJavaAnnotation(
jannotation_hash_code));
auto* const endpoint_fetcher_ptr = endpoint_fetcher.get();
endpoint_fetcher_ptr->Fetch(
base::BindOnce(&OnEndpointFetcherComplete,
@ -348,6 +348,7 @@ static void JNI_EndpointFetcher_NativeFetchChromeAPIKey(
const base::android::JavaParamRef<jstring>& jpost_data,
jlong jtimeout,
const base::android::JavaParamRef<jobjectArray>& jheaders,
jint jannotation_hash_code,
const base::android::JavaParamRef<jobject>& jcallback) {
std::vector<std::string> headers;
base::android::AppendJavaStringArrayToStringVector(env, jheaders, &headers);
@ -357,7 +358,8 @@ static void JNI_EndpointFetcher_NativeFetchChromeAPIKey(
base::android::ConvertJavaStringToUTF8(env, jhttps_method),
base::android::ConvertJavaStringToUTF8(env, jcontent_type), jtimeout,
base::android::ConvertJavaStringToUTF8(env, jpost_data), headers,
NO_TRAFFIC_ANNOTATION_YET);
net::NetworkTrafficAnnotationTag::FromJavaAnnotation(
jannotation_hash_code));
auto* const endpoint_fetcher_ptr = endpoint_fetcher.get();
endpoint_fetcher_ptr->PerformRequest(
base::BindOnce(&OnEndpointFetcherComplete,
@ -372,11 +374,13 @@ static void JNI_EndpointFetcher_NativeFetchWithNoAuth(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jprofile,
const base::android::JavaParamRef<jstring>& jurl,
jint jannotation_hash_code,
const base::android::JavaParamRef<jobject>& jcallback) {
auto endpoint_fetcher = std::make_unique<EndpointFetcher>(
ProfileAndroid::FromProfileAndroid(jprofile),
GURL(base::android::ConvertJavaStringToUTF8(env, jurl)),
NO_TRAFFIC_ANNOTATION_YET);
net::NetworkTrafficAnnotationTag::FromJavaAnnotation(
jannotation_hash_code));
auto* const endpoint_fetcher_ptr = endpoint_fetcher.get();
endpoint_fetcher_ptr->PerformRequest(
base::BindOnce(&OnEndpointFetcherComplete,

@ -9,6 +9,7 @@ import androidx.annotation.MainThread;
import org.chromium.base.Callback;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.net.NetworkTrafficAnnotationTag;
/**
* EndpointFetcher uses native EndpointFetcher to call a HTTPS endpoint and return
@ -39,11 +40,12 @@ public final class EndpointFetcher {
@MainThread
public static void fetchUsingOAuth(Callback<EndpointResponse> callback, Profile profile,
String oathConsumerName, String url, String httpsMethod, String contentType,
String[] scopes, String postData, long timeout) {
String[] scopes, String postData, long timeout,
NetworkTrafficAnnotationTag annotation) {
// EndpointFetcher currently does not support incognito mode
assert !profile.isOffTheRecord();
EndpointFetcherJni.get().nativeFetchOAuth(profile, oathConsumerName, url, httpsMethod,
contentType, scopes, postData, timeout, callback);
contentType, scopes, postData, timeout, annotation.getHashCode(), callback);
}
/**
@ -61,22 +63,22 @@ public final class EndpointFetcher {
@MainThread
public static void fetchUsingChromeAPIKey(Callback<EndpointResponse> callback, Profile profile,
String url, String httpsMethod, String contentType, String postData, long timeout,
String[] headers) {
String[] headers, NetworkTrafficAnnotationTag annotation) {
// EndpointFetcher currently does not support incognito mode
// assert !profile.isOffTheRecord();
EndpointFetcherJni.get().nativeFetchChromeAPIKey(
profile, url, httpsMethod, contentType, postData, timeout, headers, callback);
EndpointFetcherJni.get().nativeFetchChromeAPIKey(profile, url, httpsMethod, contentType,
postData, timeout, headers, annotation.getHashCode(), callback);
}
@NativeMethods
public interface Natives {
void nativeFetchOAuth(Profile profile, String oathConsumerName, String url,
String httpsMethod, String contentType, String[] scopes, String postData,
long timeout, Callback<EndpointResponse> callback);
long timeout, int annotationHashCode, Callback<EndpointResponse> callback);
void nativeFetchChromeAPIKey(Profile profile, String url, String httpsMethod,
String contentType, String postData, long timeout, String[] headers,
int annotationHashCode, Callback<EndpointResponse> callback);
void nativeFetchWithNoAuth(Profile profile, String url, int annotationHashCode,
Callback<EndpointResponse> callback);
void nativeFetchWithNoAuth(
Profile profile, String url, Callback<EndpointResponse> callback);
}
}

@ -23,6 +23,7 @@ android_library("java") {
"//chrome/browser/flags:java",
"//chrome/browser/profiles/android:java",
"//components/embedder_support/android:util_java",
"//net/android:net_java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//url:gurl_java",
]

@ -15,6 +15,7 @@ import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.components.embedder_support.util.UrlUtilities;
import org.chromium.net.NetworkTrafficAnnotationTag;
import org.chromium.url.GURL;
import java.util.ArrayList;
@ -57,6 +58,8 @@ public class PageAnnotationsServiceProxy {
return;
}
// TODO(crbug.com/995852): Replace NO_TRAFFIC_ANNOTATION_YET with a real traffic
// annotation.
EndpointFetcher.fetchUsingChromeAPIKey(
(endpointResponse)
-> { fetchCallback(endpointResponse, callback); },
@ -65,7 +68,8 @@ public class PageAnnotationsServiceProxy {
+ GET_ANNOTATIONS_QUERY_PARAMS_TEMPLATE,
UrlUtilities.escapeQueryParamValue(url.getSpec(), false)),
HTTPS_METHOD, CONTENT_TYPE, EMPTY_POST_DATA, TIMEOUT_MS,
new String[] {ACCEPT_LANGUAGE_KEY, LocaleUtils.getDefaultLocaleListString()});
new String[] {ACCEPT_LANGUAGE_KEY, LocaleUtils.getDefaultLocaleListString()},
NetworkTrafficAnnotationTag.NO_TRAFFIC_ANNOTATION_YET);
}
private void fetchCallback(EndpointResponse response,
@ -90,4 +94,4 @@ public class PageAnnotationsServiceProxy {
resultCallback.onResult(new SinglePageAnnotationsServiceResponse(annotations));
}
}
}

@ -54,6 +54,7 @@ android_library("javatests") {
"//chrome/browser/page_annotations/android:java",
"//chrome/browser/profiles/android:java",
"//chrome/test/android:chrome_java_test_support",
"//net/android:net_java",
"//third_party/androidx:androidx_test_core_java",
"//third_party/androidx:androidx_test_runner_java",
"//third_party/junit",

@ -5,6 +5,7 @@
package org.chromium.chrome.browser.page_annotations;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@ -168,21 +169,21 @@ public class PageAnnotationsServiceProxyUnitTest {
verify(mEndpointFetcherJniMock, times(numTimes))
.nativeFetchChromeAPIKey(any(Profile.class), eq(expectedUrl), eq(EXPECTED_METHOD),
eq(EXPECTED_CONTENT_TYPE), anyString(), anyLong(), eq(expectedHeaders),
any(Callback.class));
anyInt(), any(Callback.class));
}
private void mockEndpointResponse(String response) {
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
Callback callback = (Callback) invocation.getArguments()[7];
Callback callback = (Callback) invocation.getArguments()[8];
callback.onResult(new EndpointResponse(response));
return null;
}
})
.when(mEndpointFetcherJniMock)
.nativeFetchChromeAPIKey(any(Profile.class), anyString(), anyString(), anyString(),
anyString(), anyLong(), any(String[].class), any(Callback.class));
anyString(), anyLong(), any(String[].class), anyInt(), any(Callback.class));
}
private void verifyAnnotations(List<PageAnnotation> annotations) {

@ -2,4 +2,5 @@ include_rules = [
"+chrome/browser/tabpersistence",
"+chrome/browser/page_annotations",
"+components/payments/content/android",
]
"+net/android",
]

@ -19,6 +19,7 @@ import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.proto.StorePersistedTabData.StorePersistedTabDataProto;
import org.chromium.net.NetworkTrafficAnnotationTag;
import java.nio.ByteBuffer;
import java.util.Locale;
@ -277,6 +278,8 @@ public class StorePersistedTabData extends PersistedTabData {
* @param callback {@link Callback} {@link StorePersistedTabData is passed back in}
*/
public static void from(Tab tab, Callback<StorePersistedTabData> callback) {
// TODO(crbug.com/995852): Replace NO_TRAFFIC_ANNOTATION_YET with a real traffic
// annotation.
PersistedTabData.from(tab,
(data, storage, id)
-> { return new StorePersistedTabData(tab, data, storage, id); },
@ -290,7 +293,8 @@ public class StorePersistedTabData extends PersistedTabData {
},
Profile.getLastUsedRegularProfile(), PERSISTED_TAB_DATA_ID,
String.format(Locale.US, ENDPOINT, tab.getUrl().getSpec()),
HTTPS_METHOD, CONTENT_TYPE, SCOPES, EMPTY_POST_DATA, TIMEOUT_MS);
HTTPS_METHOD, CONTENT_TYPE, SCOPES, EMPTY_POST_DATA, TIMEOUT_MS,
NetworkTrafficAnnotationTag.NO_TRAFFIC_ANNOTATION_YET);
},
StorePersistedTabData.class, callback);
}
@ -373,4 +377,4 @@ public class StorePersistedTabData extends PersistedTabData {
return Integer.parseInt(sb.toString());
}
}
}

@ -23,6 +23,7 @@ android_library("net_java") {
"java/src/org/chromium/net/NetworkActivationRequest.java",
"java/src/org/chromium/net/NetworkChangeNotifier.java",
"java/src/org/chromium/net/NetworkChangeNotifierAutoDetect.java",
"java/src/org/chromium/net/NetworkTrafficAnnotationTag.java",
"java/src/org/chromium/net/ProxyBroadcastReceiver.java",
"java/src/org/chromium/net/ProxyChangeListener.java",
"java/src/org/chromium/net/RegistrationPolicyAlwaysRegister.java",
@ -221,7 +222,10 @@ java_cpp_enum("net_android_java_enums_srcjar") {
}
junit_binary("net_junit_tests") {
sources = [ "junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java" ]
sources = [
"junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java",
"junit/src/org/chromium/net/NetworkTrafficAnnotationTagTest.java",
]
deps = [
":net_java",
"//base:base_java",

@ -0,0 +1,109 @@
// Copyright 2021 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.net;
import androidx.annotation.VisibleForTesting;
import java.nio.charset.StandardCharsets;
/**
* Network Traffic Annotations document the purpose of a particular network request, and its impact
* on privacy.
*
* This documentation is typically meant for system administrators in an enterprise setting. It
* should be easy for them to read and understand, and answer the following questions:
*
* 1. When and why does Chrome make this network request?
* 2. Does this network request send any sensitive data?
* 3. Where does the request go? (e.g. a Google server, a website the user is viewing...)
* 4. How can I disable it if I don't like it?
*/
public class NetworkTrafficAnnotationTag {
/**
* For network requests that aren't documented yet. These should be
* accompanied with a TODO with a bug/owner to write their documentation.
*/
public static final NetworkTrafficAnnotationTag NO_TRAFFIC_ANNOTATION_YET =
createComplete("undefined", "Nothing here yet.");
/**
* For network requests that don't need an annotation, because they're in an
* allowlisted file (see tools/traffic_annotation/auditor/safe_list.txt).
*/
public static final NetworkTrafficAnnotationTag MISSING_TRAFFIC_ANNOTATION =
createComplete("undefined", "Function called without traffic annotation.");
/** For network requests made in tests, don't bother writing documentation. */
public static final NetworkTrafficAnnotationTag TRAFFIC_ANNOTATION_FOR_TESTS =
createComplete("test", "Traffic annotation for unit, browser and other tests");
/**
* Create a self-contained tag describing a network request made by Chromium. This is the most
* common factory method.
*
* The C++ equivalent is DefineNetworkTrafficAnnotation().
*
* @param uniqueId a String that uniquely identifies this annotations across all of Chromium
* source code.
* @param proto a text-encoded NetworkTrafficAnnotation protobuf (see
* chrome/browser/privacy/traffic_annotation.proto).
*/
public static NetworkTrafficAnnotationTag createComplete(String uniqueId, String proto) {
return new NetworkTrafficAnnotationTag(uniqueId);
}
// TODO(crbug.com/1231780): Add Partial, Completing, Branched-Completing, and
// Mutable(?) factory methods.
/**
* At runtime, an annotation tag is just a hashCode. Most of the validation is done on CQ, so
* there's no point keeping track of everything at runtime.
*
* This field is referenced from C++, so don't change it without updating
* net/traffic_annotation/network_traffic_annotation.h.
*/
// TODO(crbug.com/1231780): Unlike the C++ version though, the string will still get compiled
// into the APK, and get loaded into memory when the constructor is called... Is there a way to
// tell Java, "No, I don't actually need this string at runtime"? We should investigate.
private final int mHashCode;
/**
* @return the hash code of uniqueId, which uniquely identifies this annotation.
*/
public int getHashCode() {
return mHashCode;
}
/**
* Constructor for NetworkTrafficAnnotationTag. Consumers of this API should use
* CreateComplete() instead.
*
* @param uniqueId a String that uniquely identifies this annotation across all of Chromium
* source code.
*/
private NetworkTrafficAnnotationTag(String uniqueId) {
mHashCode = iterativeHash(uniqueId);
}
/**
* Returns the hashcode of a string, as per the recursive_hash() function used in C++ code.
*
* This is NOT the same as Java's built-in hashCode() function, because we really want to
* produce the same hashcode that auditor.py produces.
*
* @param s the String to calculate the hash on.
*/
@VisibleForTesting
static int iterativeHash(String s) {
// Multiplying by 31 would cause an overflow if using `int', so use `long' instead.
long acc = 0;
// Encode the string as UTF-8.
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
for (byte b : bytes) {
acc = (acc * 31 + b) % 138003713;
}
// The final result always fits in an `int' thanks to the modulo.
return (int) acc;
}
}

@ -0,0 +1,32 @@
// Copyright 2021 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.net;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseRobolectricTestRunner;
/**
* Tests calculating the hash for Network Traffic Annotations.
*/
@RunWith(BaseRobolectricTestRunner.class)
public class NetworkTrafficAnnotationTagTest {
@Test
public void testIterativeHash() throws Exception {
// Reference values obtained from
// tools/traffic_annotation/scripts/auditor/auditor_tests.py.
assertEquals(3556498, NetworkTrafficAnnotationTag.iterativeHash("test"));
assertEquals(10236504, NetworkTrafficAnnotationTag.iterativeHash("unique_id"));
assertEquals(70581310, NetworkTrafficAnnotationTag.iterativeHash("123_id"));
assertEquals(69491511, NetworkTrafficAnnotationTag.iterativeHash("ID123"));
assertEquals(98652091,
NetworkTrafficAnnotationTag.iterativeHash("a_unique_"
+ "looooooooooooooooooooooooooooooooooooooooooooooooooooooong_id"));
assertEquals(124751853, NetworkTrafficAnnotationTag.iterativeHash("bébé"));
}
}

@ -4,6 +4,9 @@ source_set("traffic_annotation") {
"//base",
"//build:chromeos_buildflags",
]
if (is_android) {
sources += [ "network_traffic_annotation_android.cc" ]
}
}
source_set("test_support") {

@ -12,6 +12,10 @@
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#if defined(OS_ANDROID)
#include "base/android/scoped_java_ref.h"
#endif
namespace {
// Recursively compute hash code of the given string as a constant expression.
@ -73,6 +77,13 @@ struct NetworkTrafficAnnotationTag {
const PartialNetworkTrafficAnnotationTag& partial_annotation,
const char (&proto)[N3]);
#if defined(OS_ANDROID)
// Allows C++ methods to receive a Java NetworkTrafficAnnotationTag via JNI,
// and convert it to the C++ version.
static NetworkTrafficAnnotationTag FromJavaAnnotation(
int32_t unique_id_hash_code);
#endif
friend struct MutableNetworkTrafficAnnotationTag;
private:

@ -0,0 +1,15 @@
// Copyright 2021 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.
#include "net/traffic_annotation/network_traffic_annotation.h"
namespace net {
// static
NetworkTrafficAnnotationTag NetworkTrafficAnnotationTag::FromJavaAnnotation(
int32_t unique_id_hash_code) {
return NetworkTrafficAnnotationTag(unique_id_hash_code);
}
} // namespace net

@ -9,6 +9,7 @@ missing,net/url_request/url_fetcher.cc
missing,net/url_request/url_request_context.cc
mutable_tag,components/download/internal/background_service/proto_conversions.cc
mutable_tag,chrome/browser/media/router/providers/openscreen/platform/udp_socket.cc
mutable_tag,net/traffic_annotation/network_traffic_annotation.h
test_annotation,components/safe_search_api/stub_url_checker.cc
test_annotation,net/quic/quic_chromium_client_session_peer.cc
test_annotation,net/tools/quic/quic_http_proxy_backend_stream.cc