Compare commits

..

No commits in common. "fa421a5c800e0b8db25fb94fd38f471fb4e180a7" and "e775d7b29de161c6228adf723655ec3608f44fbc" have entirely different histories.

338 changed files with 2557 additions and 42127 deletions

View File

@ -93,17 +93,9 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- arch: x64
- arch: x86
- arch: arm64
- arch: arm
- arch: mipsel
extra: chrome_pgo_phase=0
- arch: mips64el
extra: chrome_pgo_phase=0
arch: [x64, x86, arm64, arm, mipsel, mips64el]
env:
EXTRA_FLAGS: target_cpu="${{ matrix.arch }}" ${{ matrix.extra }}
EXTRA_FLAGS: 'target_cpu="${{ matrix.arch }}"'
BUNDLE: 'naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}'
steps:
- uses: actions/checkout@v2
@ -130,8 +122,8 @@ jobs:
uses: actions/cache@v2
with:
path: ~/.ccache
key: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-${{ steps.ccache-timestamp.outputs.date }}
restore-keys: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-
key: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
restore-keys: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- run: sudo apt update
- run: sudo apt install ninja-build pkg-config qemu-user ccache
# libc6-i386 interferes with x86 build
@ -142,7 +134,6 @@ jobs:
- run: ccache -s
- run: ../tests/basic.sh out/Release/naive
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
- run: cp out/Release/libcronet* components/cronet/native/generated/cronet.idl_c.h components/cronet/native/include/*.h components/grpc_support/include/*.h components/cronet/native/sample/bidi_example.cc ${{ env.BUNDLE }}
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
- uses: actions/upload-release-asset@v1
if: ${{ github.event_name == 'release' }}
@ -213,7 +204,6 @@ jobs:
# qemu-user segfaults with x64 or x86 android builds here.
if: ${{ matrix.arch != 'x64' && matrix.arch != 'x86' }}
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
- run: cp out/Release/libcronet* components/cronet/native/generated/cronet.idl_c.h components/cronet/native/include/*.h components/grpc_support/include/*.h components/cronet/native/sample/bidi_example.cc ${{ env.BUNDLE }}
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
- uses: actions/upload-release-asset@v1
if: ${{ github.event_name == 'release' }}
@ -279,7 +269,6 @@ jobs:
# No real or emulated environment is available to test this.
if: ${{ matrix.arch != 'arm64' }}
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive.exe config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
- run: cp out/Release/cronet* components/cronet/native/generated/cronet.idl_c.h components/cronet/native/include/*.h components/grpc_support/include/*.h components/cronet/native/sample/bidi_example.cc ${{ env.BUNDLE }}
- run: 7z a ../${{ env.BUNDLE }}.zip ${{ env.BUNDLE }}
- uses: actions/upload-release-asset@v1
if: ${{ github.event_name == 'release' }}
@ -334,7 +323,6 @@ jobs:
# No real or emulated environment is available to test this.
if: ${{ matrix.arch != 'arm64' }}
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
- run: cp -r out/Release/libcronet* components/cronet/native/generated/cronet.idl_c.h components/cronet/native/include/*.h components/grpc_support/include/*.h components/cronet/native/sample/bidi_example.cc ${{ env.BUNDLE }}
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
- uses: actions/upload-release-asset@v1
if: ${{ github.event_name == 'release' }}
@ -476,8 +464,8 @@ jobs:
uses: actions/cache@v2
with:
path: ~/.ccache
key: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-${{ steps.ccache-timestamp.outputs.date }}
restore-keys: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-
key: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
restore-keys: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- run: sudo apt update
- run: sudo apt install ninja-build pkg-config qemu-user ccache
# libc6-i386 interferes with x86 build
@ -488,8 +476,6 @@ jobs:
- run: ccache -s
- run: ../tests/basic.sh out/Release/naive
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
- run: cp out/Release/libcronet* components/cronet/native/generated/cronet.idl_c.h components/cronet/native/include/*.h components/grpc_support/include/*.h components/cronet/native/sample/bidi_example.cc ${{ env.BUNDLE }}
if: ${{ ! contains(matrix.extra, 'build_static=true') }}
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
- uses: actions/upload-release-asset@v1
if: ${{ github.event_name == 'release' }}

View File

@ -53,7 +53,6 @@ group("gn_all") {
deps = [
":gn_visibility",
"//net",
"//components/cronet",
]
}

View File

@ -68,9 +68,4 @@ export DEPOT_TOOLS_WIN_TOOLCHAIN=0
./gn/out/gn gen "$out" --args="$flags $EXTRA_FLAGS" --script-executable=$PYTHON
cronet_targets="cronet cronet_static"
if echo "$EXTRA_FLAGS" | grep -q "build_static=true"; then
cronet_targets=
fi
ninja -C "$out" naive $cronet_targets
ninja -C "$out" naive

View File

@ -0,0 +1,167 @@
// Copyright 2017 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.bytecode;
import org.objectweb.asm.ClassReader;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Java application that takes in an input jar, performs a series of bytecode
* transformations, and generates an output jar.
*/
class ByteCodeProcessor {
private static final String CLASS_FILE_SUFFIX = ".class";
private static final int BUFFER_SIZE = 16384;
private static boolean sVerbose;
private static boolean sIsPrebuilt;
private static ClassLoader sDirectClassPathClassLoader;
private static ClassLoader sFullClassPathClassLoader;
private static Set<String> sFullClassPathJarPaths;
private static Set<String> sMissingClassesAllowlist;
private static Map<String, String> sJarToGnTarget;
private static ClassPathValidator sValidator;
private static Void processEntry(ZipEntry entry, byte[] data) {
ClassReader reader = new ClassReader(data);
if (sIsPrebuilt) {
sValidator.validateFullClassPath(
reader, sFullClassPathClassLoader, sMissingClassesAllowlist);
} else {
sValidator.validateDirectClassPath(reader, sDirectClassPathClassLoader,
sFullClassPathClassLoader, sFullClassPathJarPaths, sMissingClassesAllowlist,
sVerbose);
}
return null;
}
private static void process(String gnTarget, String inputJarPath)
throws ExecutionException, InterruptedException {
ExecutorService executorService =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
try (ZipInputStream inputStream = new ZipInputStream(
new BufferedInputStream(new FileInputStream(inputJarPath)))) {
while (true) {
ZipEntry entry = inputStream.getNextEntry();
if (entry == null) {
break;
}
byte[] data = readAllBytes(inputStream);
executorService.submit(() -> processEntry(entry, data));
}
executorService.shutdown(); // This is essential in order to avoid waiting infinitely.
executorService.awaitTermination(1, TimeUnit.HOURS);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (sValidator.hasErrors()) {
sValidator.printAll(gnTarget, sJarToGnTarget);
System.exit(1);
}
}
private static byte[] readAllBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int numRead = 0;
byte[] data = new byte[BUFFER_SIZE];
while ((numRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, numRead);
}
return buffer.toByteArray();
}
/**
* Loads a list of jars and returns a ClassLoader capable of loading all classes found in the
* given jars.
*/
static ClassLoader loadJars(Collection<String> paths) {
URL[] jarUrls = new URL[paths.size()];
int i = 0;
for (String path : paths) {
try {
jarUrls[i++] = new File(path).toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
return new URLClassLoader(jarUrls);
}
/**
* Extracts a length-encoded list of strings from the arguments, and adds them to |out|. Returns
* the new "next index" to be processed.
*/
private static int parseListArgument(String[] args, int index, Collection<String> out) {
int argLength = Integer.parseInt(args[index++]);
out.addAll(Arrays.asList(Arrays.copyOfRange(args, index, index + argLength)));
return index + argLength;
}
public static void main(String[] args) throws ClassPathValidator.ClassNotLoadedException,
ExecutionException, InterruptedException {
// Invoke this script using //build/android/gyp/bytecode_processor.py
int currIndex = 0;
String gnTarget = args[currIndex++];
String inputJarPath = args[currIndex++];
sVerbose = args[currIndex++].equals("--verbose");
sIsPrebuilt = args[currIndex++].equals("--is-prebuilt");
sMissingClassesAllowlist = new HashSet<>();
currIndex = parseListArgument(args, currIndex, sMissingClassesAllowlist);
ArrayList<String> sdkJarPaths = new ArrayList<>();
currIndex = parseListArgument(args, currIndex, sdkJarPaths);
ArrayList<String> directClassPathJarPaths = new ArrayList<>();
directClassPathJarPaths.add(inputJarPath);
directClassPathJarPaths.addAll(sdkJarPaths);
currIndex = parseListArgument(args, currIndex, directClassPathJarPaths);
sDirectClassPathClassLoader = loadJars(directClassPathJarPaths);
ArrayList<String> fullClassPathJarPaths = new ArrayList<>();
currIndex = parseListArgument(args, currIndex, fullClassPathJarPaths);
ArrayList<String> gnTargets = new ArrayList<>();
parseListArgument(args, currIndex, gnTargets);
sJarToGnTarget = new HashMap<>();
assert fullClassPathJarPaths.size() == gnTargets.size();
for (int i = 0; i < fullClassPathJarPaths.size(); ++i) {
sJarToGnTarget.put(fullClassPathJarPaths.get(i), gnTargets.get(i));
}
// Load all jars that are on the classpath for the input jar for analyzing class
// hierarchy.
sFullClassPathJarPaths = new HashSet<>();
sFullClassPathJarPaths.add(inputJarPath);
sFullClassPathJarPaths.addAll(sdkJarPaths);
sFullClassPathJarPaths.addAll(fullClassPathJarPaths);
sFullClassPathClassLoader = loadJars(sFullClassPathJarPaths);
sFullClassPathJarPaths.removeAll(directClassPathJarPaths);
sValidator = new ClassPathValidator();
process(gnTarget, inputJarPath);
}
}

View File

@ -0,0 +1,117 @@
// Copyright 2020 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.bytecode;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* Base class for scripts that perform bytecode modifications on a jar file.
*/
public abstract class ByteCodeRewriter {
private static final String CLASS_FILE_SUFFIX = ".class";
public void rewrite(File inputJar, File outputJar) throws IOException {
if (!inputJar.exists()) {
throw new FileNotFoundException("Input jar not found: " + inputJar.getPath());
}
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(inputJar));
OutputStream outputStream = new FileOutputStream(outputJar)) {
processZip(inputStream, outputStream);
}
}
/** Returns true if the class at the given path in the archive should be rewritten. */
protected abstract boolean shouldRewriteClass(String classPath);
/**
* Returns true if the class at the given {@link ClassReader} should be rewritten.
*/
protected boolean shouldRewriteClass(ClassReader classReader) {
return true;
}
/**
* Returns the ClassVisitor that should be used to modify the bytecode of class at the given
* path in the archive.
*/
protected abstract ClassVisitor getClassVisitorForClass(
String classPath, ClassVisitor delegate);
private void processZip(InputStream inputStream, OutputStream outputStream) {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
// Get the uncompressed contents of the current zip entry and wrap in an input
// stream. This is done because ZipInputStreams can't be reset so they can only be
// read once, and classes that don't need rewriting need to be read twice, first to
// parse and then to copy.
byte[] currentEntryBytes = zipInputStream.readAllBytes();
ByteArrayInputStream currentEntryInputStream =
new ByteArrayInputStream(currentEntryBytes);
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
boolean handled = processClassEntry(entry, currentEntryInputStream, outputBuffer);
if (handled) {
ZipEntry newEntry = new ZipEntry(entry.getName());
zipOutputStream.putNextEntry(newEntry);
zipOutputStream.write(outputBuffer.toByteArray(), 0, outputBuffer.size());
zipOutputStream.closeEntry();
} else {
ZipEntry newEntry = new ZipEntry(entry.getName());
zipOutputStream.putNextEntry(newEntry);
// processClassEntry may have advanced currentEntryInputStream, so reset it to
// copy zip entry contents unmodified.
currentEntryInputStream.reset();
currentEntryInputStream.transferTo(zipOutputStream);
zipOutputStream.closeEntry();
}
}
zipOutputStream.finish();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private boolean processClassEntry(
ZipEntry entry, InputStream inputStream, OutputStream outputStream) {
if (!entry.getName().endsWith(CLASS_FILE_SUFFIX) || !shouldRewriteClass(entry.getName())) {
return false;
}
try {
ClassReader reader = new ClassReader(inputStream);
if (!shouldRewriteClass(reader)) {
return false;
}
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
ClassVisitor classVisitor = getClassVisitorForClass(entry.getName(), writer);
reader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
writer.visitEnd();
byte[] classData = writer.toByteArray();
outputStream.write(classData, 0, classData.length);
return true;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,233 @@
// 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.bytecode;
import org.objectweb.asm.ClassReader;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Consumer;
/**
* Checks classpaths (given as ClassLoaders) by reading the constant pool of the class file and
* attempting to load every referenced class. If there are some that are unable to be found, it
* stores a helpful error message if it knows where it might find them, and exits the program if it
* can't find the class with any given classpath.
*/
public class ClassPathValidator {
// Number of warnings to print.
private static final int MAX_MISSING_CLASS_WARNINGS = 10;
// Number of missing classes to show per missing jar.
private static final int MAX_ERRORS_PER_JAR = 2;
// Map of missing .jar -> Missing class -> Classes that failed.
// TreeMap so that error messages have sorted list of jars.
private final Map<String, Map<String, Set<String>>> mDirectErrors =
Collections.synchronizedMap(new TreeMap<>());
// Missing classes we only track the first one for each jar.
// Map of missingClass -> srcClass.
private final Map<String, String> mMissingClasses =
Collections.synchronizedMap(new TreeMap<>());
static class ClassNotLoadedException extends ClassNotFoundException {
private final String mClassName;
ClassNotLoadedException(String className, Throwable ex) {
super("Couldn't load " + className, ex);
mClassName = className;
}
public String getClassName() {
return mClassName;
}
}
private static void validateClass(ClassLoader classLoader, String className)
throws ClassNotLoadedException {
if (className.startsWith("[")) {
// Dealing with an array type which isn't encoded nicely in the constant pool.
// For example, [[Lorg/chromium/Class$1;
className = className.substring(className.lastIndexOf('[') + 1);
if (className.charAt(0) == 'L' && className.endsWith(";")) {
className = className.substring(1, className.length() - 1);
} else {
// Bailing out if we have an non-class array type.
// This could be something like [B
return;
}
}
if (className.matches(".*\\bR(\\$\\w+)?$")) {
// Resources in R.java files are not expected to be valid at this stage in the build.
return;
}
if (className.matches("^libcore\\b.*")) {
// libcore exists on devices, but is not included in the Android sdk as it is a private
// API.
return;
}
try {
classLoader.loadClass(className.replace('/', '.'));
} catch (ClassNotFoundException e) {
throw new ClassNotLoadedException(className, e);
} catch (NoClassDefFoundError e) {
// We assume that this is caused by another class that is not going to able to be
// loaded, so we will skip this and let that class fail with ClassNotFoundException.
}
}
/**
* Given a .class file, see if every class referenced in the main class' constant pool can be
* loaded by the given ClassLoader.
*
* @param classReader .class file interface for reading the constant pool.
* @param classLoader classpath you wish to validate.
* @param errorConsumer Called for each missing class.
*/
private static void validateClassPath(ClassReader classReader, ClassLoader classLoader,
Consumer<ClassNotLoadedException> errorConsumer) {
char[] charBuffer = new char[classReader.getMaxStringLength()];
// According to the Java spec, the constant pool is indexed from 1 to constant_pool_count -
// 1. See https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
for (int i = 1; i < classReader.getItemCount(); i++) {
int offset = classReader.getItem(i);
// Class entries correspond to 7 in the constant pool
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
if (offset > 0 && classReader.readByte(offset - 1) == 7) {
try {
validateClass(classLoader, classReader.readUTF8(offset, charBuffer));
} catch (ClassNotLoadedException e) {
errorConsumer.accept(e);
}
}
}
}
public void validateFullClassPath(ClassReader classReader, ClassLoader fullClassLoader,
Set<String> missingClassAllowlist) {
// Prebuilts only need transitive dependencies checked, not direct dependencies.
validateClassPath(classReader, fullClassLoader, (e) -> {
if (!missingClassAllowlist.contains(e.getClassName())) {
addMissingError(classReader.getClassName(), e.getClassName());
}
});
}
public void validateDirectClassPath(ClassReader classReader, ClassLoader directClassLoader,
ClassLoader fullClassLoader, Collection<String> jarsOnlyInFullClassPath,
Set<String> missingClassAllowlist, boolean verbose) {
validateClassPath(classReader, directClassLoader, (e) -> {
try {
validateClass(fullClassLoader, e.getClassName());
} catch (ClassNotLoadedException d) {
if (!missingClassAllowlist.contains(e.getClassName())) {
addMissingError(classReader.getClassName(), e.getClassName());
}
return;
}
if (verbose) {
System.err.println("Class \"" + e.getClassName()
+ "\" not found in direct dependencies,"
+ " but found in indirect dependiences.");
}
// Iterating through all jars that are in the full classpath but not the direct
// classpath to find which one provides the class we are looking for.
for (String jarPath : jarsOnlyInFullClassPath) {
try {
ClassLoader smallLoader =
ByteCodeProcessor.loadJars(Collections.singletonList(jarPath));
validateClass(smallLoader, e.getClassName());
addDirectError(jarPath, classReader.getClassName(), e.getClassName());
break;
} catch (ClassNotLoadedException f) {
}
}
});
}
private void addMissingError(String srcClass, String missingClass) {
mMissingClasses.put(missingClass, srcClass);
}
private void addDirectError(String jarPath, String srcClass, String missingClass) {
synchronized (mDirectErrors) {
Map<String, Set<String>> failedClassesByMissingClass = mDirectErrors.get(jarPath);
if (failedClassesByMissingClass == null) {
// TreeMap so that error messages have sorted list of classes.
failedClassesByMissingClass = new TreeMap<>();
mDirectErrors.put(jarPath, failedClassesByMissingClass);
}
Set<String> failedClasses = failedClassesByMissingClass.get(missingClass);
if (failedClasses == null) {
failedClasses = new TreeSet<>();
failedClassesByMissingClass.put(missingClass, failedClasses);
}
failedClasses.add(srcClass);
}
}
public boolean hasErrors() {
return !mDirectErrors.isEmpty() || !mMissingClasses.isEmpty();
}
private static void printValidationError(
PrintStream out, String gnTarget, Map<String, Set<String>> missingClasses) {
out.print(" * ");
out.println(gnTarget);
int i = 0;
// The list of missing classes is non-exhaustive because each class that fails to validate
// reports only the first missing class.
for (Map.Entry<String, Set<String>> entry : missingClasses.entrySet()) {
String missingClass = entry.getKey();
Set<String> filesThatNeededIt = entry.getValue();
out.print(" * ");
if (i == MAX_ERRORS_PER_JAR) {
out.print(String.format(
"And %d more...", missingClasses.size() - MAX_ERRORS_PER_JAR));
break;
}
out.print(missingClass.replace('/', '.'));
out.print(" (needed by ");
out.print(filesThatNeededIt.iterator().next().replace('/', '.'));
if (filesThatNeededIt.size() > 1) {
out.print(String.format(" and %d more", filesThatNeededIt.size() - 1));
}
out.println(")");
i++;
}
}
public void printAll(String gnTarget, Map<String, String> jarToGnTarget) {
String streamer = "=============================";
System.err.println();
System.err.println(streamer + " Dependency Checks Failed " + streamer);
System.err.println("Target: " + gnTarget);
if (!mMissingClasses.isEmpty()) {
int i = 0;
for (Map.Entry<String, String> entry : mMissingClasses.entrySet()) {
if (++i > MAX_MISSING_CLASS_WARNINGS) {
System.err.println(String.format("... and %d more.",
mMissingClasses.size() - MAX_MISSING_CLASS_WARNINGS));
break;
}
System.err.println(String.format(
"Class \"%s\" not found on any classpath. Used by class \"%s\"",
entry.getKey(), entry.getValue()));
}
System.err.println();
}
if (!mDirectErrors.isEmpty()) {
System.err.println("Direct classpath is incomplete. To fix, add deps on:");
for (Map.Entry<String, Map<String, Set<String>>> entry : mDirectErrors.entrySet()) {
printValidationError(
System.err, jarToGnTarget.get(entry.getKey()), entry.getValue());
}
System.err.println();
}
}
}

View File

@ -0,0 +1,103 @@
// 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.bytecode;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ASM7;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.util.ArrayList;
class EmptyOverrideGeneratorClassAdapter extends ClassVisitor {
private final ArrayList<MethodDescription> mMethodsToGenerate;
private String mSuperClassName;
private boolean mIsAbstract;
private boolean mIsInterface;
public EmptyOverrideGeneratorClassAdapter(
ClassVisitor cv, ArrayList<MethodDescription> methodsToGenerate) {
super(ASM7, cv);
mMethodsToGenerate = methodsToGenerate;
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
mSuperClassName = superName;
mIsAbstract = (access & ACC_ABSTRACT) == ACC_ABSTRACT;
mIsInterface = (access & ACC_INTERFACE) == ACC_INTERFACE;
}
@Override
public void visitEnd() {
if (mIsAbstract || mIsInterface || mMethodsToGenerate.isEmpty()) {
super.visitEnd();
return;
}
for (MethodDescription method : mMethodsToGenerate) {
if (!method.shouldCreateOverride) {
continue;
}
MethodVisitor mv = super.visitMethod(
method.access, method.methodName, method.description, null, null);
writeOverrideCode(mv, method.access, method.methodName, method.description);
}
super.visitEnd();
}
/**
* Writes code to a method to call that method's parent implementation.
* <pre>
* {@code
* // Calling writeOverrideCode(mv, ACC_PUBLIC, "doFoo", "(Ljava/lang/String;)I") writes the
* following method body: public int doFoo(String arg){ return super.doFoo(arg);
* }
* }
* </pre>
*
* This will be rewritten later by TraceEventAdderClassAdapter to wrap the body in a trace
* event.
*/
private void writeOverrideCode(
MethodVisitor mv, final int access, final String name, final String descriptor) {
Type[] argTypes = Type.getArgumentTypes(descriptor);
Type returnType = Type.getReturnType(descriptor);
mv.visitCode();
// Variable 0 contains `this`, load it into the operand stack.
mv.visitVarInsn(ALOAD, 0);
// Variables 1..n contain all arguments, load them all into the operand stack.
int i = 1;
for (Type arg : argTypes) {
// getOpcode(ILOAD) returns the ILOAD equivalent to the current argument's type.
mv.visitVarInsn(arg.getOpcode(ILOAD), i);
i += arg.getSize();
}
// Call the parent class method with the same arguments.
mv.visitMethodInsn(INVOKESPECIAL, mSuperClassName, name, descriptor, false);
// Return the result.
mv.visitInsn(returnType.getOpcode(IRETURN));
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}

View File

@ -0,0 +1,303 @@
// Copyright 2020 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.bytecode;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.MethodRemapper;
import org.objectweb.asm.commons.Remapper;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* Java application that modifies Fragment.getActivity() to return an Activity instead of a
* FragmentActivity, and updates any existing getActivity() calls to reference the updated method.
*
* See crbug.com/1144345 for more context.
*/
public class FragmentActivityReplacer extends ByteCodeRewriter {
private static final String GET_ACTIVITY_METHOD_NAME = "getActivity";
private static final String GET_LIFECYCLE_ACTIVITY_METHOD_NAME = "getLifecycleActivity";
private static final String NEW_METHOD_DESCRIPTOR = "()Landroid/app/Activity;";
private static final String OLD_METHOD_DESCRIPTOR =
"()Landroidx/fragment/app/FragmentActivity;";
private static final String REQUIRE_ACTIVITY_METHOD_NAME = "requireActivity";
private static final String SUPPORT_LIFECYCLE_FRAGMENT_IMPL_BINARY_NAME =
"com.google.android.gms.common.api.internal.SupportLifecycleFragmentImpl";
public static void main(String[] args) throws IOException {
// Invoke this script using //build/android/gyp/bytecode_rewriter.py
if (!(args.length == 2 || args.length == 3 && args[0].equals("--single-androidx"))) {
System.err.println("Expected arguments: [--single-androidx] <input.jar> <output.jar>");
System.exit(1);
}
if (args.length == 2) {
FragmentActivityReplacer rewriter = new FragmentActivityReplacer(false);
rewriter.rewrite(new File(args[0]), new File(args[1]));
} else {
FragmentActivityReplacer rewriter = new FragmentActivityReplacer(true);
rewriter.rewrite(new File(args[1]), new File(args[2]));
}
}
private final boolean mSingleAndroidX;
public FragmentActivityReplacer(boolean singleAndroidX) {
mSingleAndroidX = singleAndroidX;
}
@Override
protected boolean shouldRewriteClass(String classPath) {
return true;
}
@Override
protected ClassVisitor getClassVisitorForClass(String classPath, ClassVisitor delegate) {
ClassVisitor invocationVisitor = new InvocationReplacer(delegate, mSingleAndroidX);
switch (classPath) {
case "androidx/fragment/app/Fragment.class":
return new FragmentClassVisitor(invocationVisitor);
case "com/google/android/gms/common/api/internal/SupportLifecycleFragmentImpl.class":
return new SupportLifecycleFragmentImplClassVisitor(invocationVisitor);
default:
return invocationVisitor;
}
}
/**
* Updates any Fragment.getActivity/requireActivity() or getLifecycleActivity() calls to call
* the replaced method.
*/
private static class InvocationReplacer extends ClassVisitor {
/**
* A ClassLoader that will resolve R classes to Object.
*
* R won't be in our classpath, and we don't access any information about them, so resolving
* it to a dummy value is fine.
*/
private static class ResourceStubbingClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.matches(".*\\.R(\\$.+)?")) {
return Object.class;
}
return super.findClass(name);
}
}
private final boolean mSingleAndroidX;
private final ClassLoader mClassLoader;
private InvocationReplacer(ClassVisitor baseVisitor, boolean singleAndroidX) {
super(Opcodes.ASM7, baseVisitor);
mSingleAndroidX = singleAndroidX;
mClassLoader = new ResourceStubbingClassLoader();
}
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor base = super.visitMethod(access, name, descriptor, signature, exceptions);
return new MethodVisitor(Opcodes.ASM7, base) {
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String descriptor, boolean isInterface) {
// Change the return type of getActivity and replaceActivity.
if (isActivityGetterInvocation(opcode, owner, name, descriptor)) {
super.visitMethodInsn(
opcode, owner, name, NEW_METHOD_DESCRIPTOR, isInterface);
if (mSingleAndroidX) {
super.visitTypeInsn(
Opcodes.CHECKCAST, "androidx/fragment/app/FragmentActivity");
}
} else if (isDowncastableFragmentActivityMethodInvocation(
opcode, owner, name, descriptor)) {
// Replace FragmentActivity.foo() with Activity.foo() to fix cases where the
// above code changed the getActivity return type. See the
// isDowncastableFragmentActivityMethodInvocation documentation for details.
super.visitMethodInsn(
opcode, "android/app/Activity", name, descriptor, isInterface);
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
private boolean isActivityGetterInvocation(
int opcode, String owner, String name, String descriptor) {
boolean isFragmentGetActivity = name.equals(GET_ACTIVITY_METHOD_NAME)
&& descriptor.equals(OLD_METHOD_DESCRIPTOR)
&& isFragmentSubclass(owner);
boolean isFragmentRequireActivity = name.equals(REQUIRE_ACTIVITY_METHOD_NAME)
&& descriptor.equals(OLD_METHOD_DESCRIPTOR)
&& isFragmentSubclass(owner);
boolean isSupportLifecycleFragmentImplGetLifecycleActivity =
name.equals(GET_LIFECYCLE_ACTIVITY_METHOD_NAME)
&& descriptor.equals(OLD_METHOD_DESCRIPTOR)
&& owner.equals(SUPPORT_LIFECYCLE_FRAGMENT_IMPL_BINARY_NAME);
return (opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKESPECIAL)
&& (isFragmentGetActivity || isFragmentRequireActivity
|| isSupportLifecycleFragmentImplGetLifecycleActivity);
}
/**
* Returns true if the given method belongs to FragmentActivity, and also exists on
* Activity.
*
* The Java code `requireActivity().getClassLoader()` will compile to the following
* bytecode:
* aload_0
* // Method requireActivity:()Landroid/app/Activity;
* invokevirtual #n
* // Method androidx/fragment/app/FragmentActivity.getClassLoader:()LClassLoader;
* invokevirtual #m
*
* The second invokevirtual instruction doesn't typecheck because the
* requireActivity() return type was changed from FragmentActivity to Activity. Note
* that this is only an issue when validating the bytecode on the JVM, not in
* Dalvik, so while the above code works on device, it fails in robolectric tests.
*
* To fix the example above, we'd replace the second invokevirtual call with a call
* to android/app/Activity.getClassLoader:()Ljava/lang/ClassLoader. In general, any
* call to FragmentActivity.foo, where foo also exists on Activity, will be replaced
* with a call to Activity.foo. Activity.foo will still resolve to
* FragmentActivity.foo at runtime, while typechecking in robolectric tests.
*/
private boolean isDowncastableFragmentActivityMethodInvocation(
int opcode, String owner, String name, String descriptor) {
// Return if this isn't an invoke instruction on a FragmentActivity.
if (!(opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKESPECIAL)
|| !owner.equals("androidx/fragment/app/FragmentActivity")) {
return false;
}
try {
// Check if the method exists in Activity.
Class<?> activity = mClassLoader.loadClass("android.app.Activity");
for (Method activityMethod : activity.getMethods()) {
if (activityMethod.getName().equals(name)
&& Type.getMethodDescriptor(activityMethod)
.equals(descriptor)) {
return true;
}
}
return false;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
private boolean isFragmentSubclass(String internalType) {
// This doesn't use Class#isAssignableFrom to avoid us needing to load
// AndroidX's Fragment class, which may not be on the classpath.
try {
String binaryName = Type.getObjectType(internalType).getClassName();
Class<?> clazz = mClassLoader.loadClass(binaryName);
while (clazz != null) {
if (clazz.getName().equals("androidx.fragment.app.Fragment")) {
return true;
}
clazz = clazz.getSuperclass();
}
return false;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
};
}
}
/**
* Updates the implementation of Fragment.getActivity() and Fragment.requireActivity().
*/
private static class FragmentClassVisitor extends ClassVisitor {
private FragmentClassVisitor(ClassVisitor baseVisitor) {
super(Opcodes.ASM7, baseVisitor);
}
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
// Update the descriptor of getActivity() and requireActivity().
MethodVisitor baseVisitor;
if (descriptor.equals(OLD_METHOD_DESCRIPTOR)
&& (name.equals(GET_ACTIVITY_METHOD_NAME)
|| name.equals(REQUIRE_ACTIVITY_METHOD_NAME))) {
// Some Fragments in a Clank library implement an interface that defines an
// `Activity getActivity()` method. Fragment.getActivity() is considered its
// implementation from a typechecking perspective, but javac still generates a
// getActivity() method in these Fragments that call Fragment.getActivity(). This
// isn't an issue when the methods return different types, but after changing
// Fragment.getActivity() to return an Activity, this generated implementation is
// now overriding Fragment's, which it can't do because Fragment.getActivity() is
// final. We make it non-final here to avoid this issue.
baseVisitor = super.visitMethod(
access & ~Opcodes.ACC_FINAL, name, NEW_METHOD_DESCRIPTOR, null, exceptions);
} else {
baseVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
}
// Replace getActivity() with `return ContextUtils.activityFromContext(getContext());`
if (name.equals(GET_ACTIVITY_METHOD_NAME) && descriptor.equals(OLD_METHOD_DESCRIPTOR)) {
baseVisitor.visitVarInsn(Opcodes.ALOAD, 0);
baseVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "androidx/fragment/app/Fragment",
"getContext", "()Landroid/content/Context;", false);
baseVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "org/chromium/utils/ContextUtils",
"activityFromContext", "(Landroid/content/Context;)Landroid/app/Activity;",
false);
baseVisitor.visitInsn(Opcodes.ARETURN);
// Since we set COMPUTE_FRAMES, the arguments of visitMaxs are ignored, but calling
// it forces ClassWriter to actually recompute the correct stack/local values.
// Without this call ClassWriter keeps the original stack=0,locals=1 which is wrong.
baseVisitor.visitMaxs(0, 0);
return null;
}
return new MethodRemapper(baseVisitor, new Remapper() {
@Override
public String mapType(String internalName) {
if (internalName.equals("androidx/fragment/app/FragmentActivity")) {
return "android/app/Activity";
}
return internalName;
}
});
}
}
/**
* Update SupportLifecycleFragmentImpl.getLifecycleActivity().
*/
private static class SupportLifecycleFragmentImplClassVisitor extends ClassVisitor {
private SupportLifecycleFragmentImplClassVisitor(ClassVisitor baseVisitor) {
super(Opcodes.ASM7, baseVisitor);
}
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
// SupportLifecycleFragmentImpl has two getActivity methods:
// 1. public FragmentActivity getLifecycleActivity():
// This is what you'll see in the source. This delegates to Fragment.getActivity().
// 2. public Activity getLifecycleActivity():
// This is generated because the class implements LifecycleFragment, which
// declares this method, and delegates to #1.
//
// Here we change the return type of #1 and delete #2.
if (name.equals(GET_LIFECYCLE_ACTIVITY_METHOD_NAME)) {
if (descriptor.equals(OLD_METHOD_DESCRIPTOR)) {
return super.visitMethod(
access, name, NEW_METHOD_DESCRIPTOR, signature, exceptions);
}
return null;
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}
}

View File

@ -0,0 +1,136 @@
// 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.bytecode;
import static org.objectweb.asm.ClassReader.EXPAND_FRAMES;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
import static org.objectweb.asm.Opcodes.ASM7;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
/**
* This ClassVisitor verifies that a class and its methods are suitable for rewriting.
* Given a class and a list of methods it performs the following checks:
* 1. Class is subclass of {@link android.view.View}.
* 2. Class is not abstract or an interface.
*
* For each method provided in {@code methodsToCheck}:
* If the class overrides the method then we can rewrite it directly.
* If the class doesn't override the method then we can generate an override with {@link
* EmptyOverrideGeneratorClassAdapter}, but first we must check if the parent method is private or
* final using {@link ParentMethodCheckerClassAdapter}.
*
* This adapter modifies the provided method list to indicate which methods should be overridden or
* skipped.
*/
class MethodCheckerClassAdapter extends ClassVisitor {
private static final String VIEW_CLASS_DESCRIPTOR = "android/view/View";
private final ArrayList<MethodDescription> mMethodsToCheck;
private final ClassLoader mJarClassLoader;
private String mSuperName;
public MethodCheckerClassAdapter(
ArrayList<MethodDescription> methodsToCheck, ClassLoader jarClassLoader) {
super(ASM7);
mMethodsToCheck = methodsToCheck;
mJarClassLoader = jarClassLoader;
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
mSuperName = superName;
boolean isAbstract = (access & ACC_ABSTRACT) == ACC_ABSTRACT;
boolean isInterface = (access & ACC_INTERFACE) == ACC_INTERFACE;
if (isAbstract || isInterface || !isClassView(name)) {
mMethodsToCheck.clear();
return;
}
}
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
if (mMethodsToCheck.isEmpty()) {
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
for (MethodDescription method : mMethodsToCheck) {
if (method.methodName.equals(name) && method.description.equals(descriptor)) {
method.shouldCreateOverride = false;
}
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
@Override
public void visitEnd() {
if (mMethodsToCheck.isEmpty()) {
super.visitEnd();
return;
}
boolean areAnyUncheckedMethods = false;
for (MethodDescription method : mMethodsToCheck) {
if (method.shouldCreateOverride == null) {
areAnyUncheckedMethods = true;
break;
}
}
if (areAnyUncheckedMethods) {
checkParentClass(mSuperName, mMethodsToCheck, mJarClassLoader);
}
super.visitEnd();
}
private boolean isClassView(String desc) {
Class currentClass = getClass(desc);
Class viewClass = getClass(VIEW_CLASS_DESCRIPTOR);
if (currentClass != null && viewClass != null) {
return viewClass.isAssignableFrom(currentClass);
}
return false;
}
private Class getClass(String desc) {
try {
return mJarClassLoader.loadClass(desc.replace('/', '.'));
} catch (ClassNotFoundException | NoClassDefFoundError | IllegalAccessError e) {
return null;
}
}
static void checkParentClass(String superClassName, ArrayList<MethodDescription> methodsToCheck,
ClassLoader jarClassLoader) {
try {
ClassReader cr = new ClassReader(getClassAsStream(jarClassLoader, superClassName));
ParentMethodCheckerClassAdapter parentChecker =
new ParentMethodCheckerClassAdapter(methodsToCheck, jarClassLoader);
cr.accept(parentChecker, EXPAND_FRAMES);
} catch (IOException ex) {
// Ignore errors in case class can't be loaded.
}
}
private static InputStream getClassAsStream(ClassLoader jarClassLoader, String desc) {
return jarClassLoader.getResourceAsStream(desc.replace('.', '/') + ".class");
}
}

View File

@ -0,0 +1,20 @@
// 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.bytecode;
class MethodDescription {
public final String methodName;
public final String description;
public final int access;
public Boolean shouldCreateOverride;
public MethodDescription(String methodName, String description, int access) {
this.methodName = methodName;
this.description = description;
this.access = access;
// A null value means we haven't checked the method.
this.shouldCreateOverride = null;
}
}

View File

@ -0,0 +1,94 @@
// 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.bytecode;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ASM7;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import java.util.ArrayList;
/**
* This ClassVisitor checks if the given class overrides methods on {@code methodsToCheck}, and if
* so it determines whether they can be overridden by a child class. If at the end any unchecked
* methods remain then we recurse on the class's superclass.
*/
class ParentMethodCheckerClassAdapter extends ClassVisitor {
private static final String OBJECT_CLASS_DESCRIPTOR = "java.lang.Object";
private final ArrayList<MethodDescription> mMethodsToCheck;
private final ClassLoader mJarClassLoader;
private String mSuperName;
private boolean mIsCheckingObjectClass;
public ParentMethodCheckerClassAdapter(
ArrayList<MethodDescription> methodsToCheck, ClassLoader jarClassLoader) {
super(ASM7);
mMethodsToCheck = methodsToCheck;
mJarClassLoader = jarClassLoader;
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
if (name.equals(OBJECT_CLASS_DESCRIPTOR)) {
mIsCheckingObjectClass = true;
return;
}
mSuperName = superName;
}
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
if (mIsCheckingObjectClass) {
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
for (MethodDescription methodToCheck : mMethodsToCheck) {
if (methodToCheck.shouldCreateOverride != null || !methodToCheck.methodName.equals(name)
|| !methodToCheck.description.equals(descriptor)) {
continue;
}
// This class contains methodToCheck.
boolean isMethodPrivate = (access & ACC_PRIVATE) == ACC_PRIVATE;
boolean isMethodFinal = (access & ACC_FINAL) == ACC_FINAL;
// If the method is private or final then don't create an override.
methodToCheck.shouldCreateOverride = !isMethodPrivate && !isMethodFinal;
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
@Override
public void visitEnd() {
if (mIsCheckingObjectClass) {
return;
}
boolean areAnyUncheckedMethods = false;
for (MethodDescription method : mMethodsToCheck) {
if (method.shouldCreateOverride == null) {
areAnyUncheckedMethods = true;
break;
}
}
if (areAnyUncheckedMethods) {
MethodCheckerClassAdapter.checkParentClass(
mSuperName, mMethodsToCheck, mJarClassLoader);
}
super.visitEnd();
}
}

View File

@ -0,0 +1,96 @@
// 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.bytecode;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Java application that modifies all implementations of "draw", "onMeasure" and "onLayout" on all
* {@link android.view.View} subclasses to wrap them in trace events.
*/
public class TraceEventAdder extends ByteCodeRewriter {
private final ClassLoader mClassPathJarsClassLoader;
private ArrayList<MethodDescription> mMethodsToTrace;
public static void main(String[] args) throws IOException {
// Invoke this script using //build/android/gyp/trace_event_bytecode_rewriter.py
if (args.length < 2) {
System.err.println("Expected arguments: <':' separated list with N input jar paths> "
+ "<':' separated list with N output jar paths>");
System.exit(1);
}
String[] inputJars = args[0].split(":");
String[] outputJars = args[1].split(":");
assert inputJars.length
== outputJars.length : "Input and output lists are not the same length. Inputs: "
+ inputJars.length + " Outputs: " + outputJars.length;
// outputJars[n] must be the same as inputJars[n] but with a suffix, validate this.
for (int i = 0; i < inputJars.length; i++) {
File inputJarPath = new File(inputJars[i]);
String inputJarFilename = inputJarPath.getName();
File outputJarPath = new File(outputJars[i]);
String inputFilenameNoExtension =
inputJarFilename.substring(0, inputJarFilename.lastIndexOf(".jar"));
assert outputJarPath.getName().startsWith(inputFilenameNoExtension);
}
ArrayList<String> classPathJarsPaths = new ArrayList<>();
classPathJarsPaths.addAll(Arrays.asList(inputJars));
ClassLoader classPathJarsClassLoader = ByteCodeProcessor.loadJars(classPathJarsPaths);
TraceEventAdder adder = new TraceEventAdder(classPathJarsClassLoader);
for (int i = 0; i < inputJars.length; i++) {
adder.rewrite(new File(inputJars[i]), new File(outputJars[i]));
}
}
public TraceEventAdder(ClassLoader classPathJarsClassLoader) {
mClassPathJarsClassLoader = classPathJarsClassLoader;
}
@Override
protected boolean shouldRewriteClass(String classPath) {
return true;
}
@Override
protected boolean shouldRewriteClass(ClassReader classReader) {
mMethodsToTrace = new ArrayList<>(Arrays.asList(
new MethodDescription("draw", "(Landroid/graphics/Canvas;)V", Opcodes.ACC_PUBLIC),
new MethodDescription("onMeasure", "(II)V", Opcodes.ACC_PROTECTED),
new MethodDescription("onLayout", "(ZIIII)V", Opcodes.ACC_PROTECTED)));
// This adapter will modify mMethodsToTrace to indicate which methods already exist in the
// class and which ones need to be overridden. In case the class is not an Android view
// we'll clear the list and skip rewriting.
MethodCheckerClassAdapter methodChecker =
new MethodCheckerClassAdapter(mMethodsToTrace, mClassPathJarsClassLoader);
classReader.accept(methodChecker, ClassReader.EXPAND_FRAMES);
return !mMethodsToTrace.isEmpty();
}
@Override
protected ClassVisitor getClassVisitorForClass(String classPath, ClassVisitor delegate) {
ClassVisitor chain = new TraceEventAdderClassAdapter(delegate, mMethodsToTrace);
chain = new EmptyOverrideGeneratorClassAdapter(chain, mMethodsToTrace);
return chain;
}
}

View File

@ -0,0 +1,47 @@
// 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.bytecode;
import static org.objectweb.asm.Opcodes.ASM7;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import java.util.ArrayList;
/**
* A ClassVisitor for adding TraceEvent.begin and TraceEvent.end methods to any methods specified in
* a list.
*/
class TraceEventAdderClassAdapter extends ClassVisitor {
private final ArrayList<MethodDescription> mMethodsToTrace;
private String mShortClassName;
TraceEventAdderClassAdapter(ClassVisitor visitor, ArrayList<MethodDescription> methodsToTrace) {
super(ASM7, visitor);
mMethodsToTrace = methodsToTrace;
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
mShortClassName = name.substring(name.lastIndexOf('/') + 1);
}
@Override
public MethodVisitor visitMethod(final int access, final String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
for (MethodDescription method : mMethodsToTrace) {
if (method.methodName.equals(name) && method.description.equals(desc)) {
return new TraceEventAdderMethodAdapter(mv, mShortClassName, name);
}
}
return mv;
}
}

View File

@ -0,0 +1,83 @@
// 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.bytecode;
import static org.objectweb.asm.Opcodes.ASM7;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.chromium.bytecode.TypeUtils.STRING;
import static org.chromium.bytecode.TypeUtils.VOID;
import org.objectweb.asm.MethodVisitor;
/**
* MethodVisitor that wraps all code in TraceEvent.begin and TraceEvent.end calls. TraceEvent.end
* calls are added on all returns and thrown exceptions.
*
* Example:
* <pre>
* {@code
* int methodToTrace(String foo){
*
* //Line added by rewriter:
* TraceEvent.begin("ClassName.methodToTrace");
*
* if(foo == null){
* //Line added by rewriter:
* TraceEvent.end("ClassName.methodToTrace");
*
* throw new Exception();
* }
* else if(foo.equals("Two")){
* //Line added by rewriter:
* TraceEvent.end("ClassName.methodToTrace");
*
* return 2;
* }
*
* //Line added by rewriter:
* TraceEvent.end("ClassName.methodToTrace");
*
* return 0;
* }
* }
* </pre>
*
*/
class TraceEventAdderMethodAdapter extends MethodVisitor {
private static final String TRACE_EVENT_DESCRIPTOR = "org/chromium/base/TraceEvent";
private static final String TRACE_EVENT_SIGNATURE = TypeUtils.getMethodDescriptor(VOID, STRING);
private final String mEventName;
public TraceEventAdderMethodAdapter(
MethodVisitor methodVisitor, String shortClassName, String methodName) {
super(ASM7, methodVisitor);
mEventName = shortClassName + "." + methodName;
}
@Override
public void visitCode() {
super.visitCode();
mv.visitLdcInsn(mEventName);
mv.visitMethodInsn(
INVOKESTATIC, TRACE_EVENT_DESCRIPTOR, "begin", TRACE_EVENT_SIGNATURE, false);
}
@Override
public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
mv.visitLdcInsn(mEventName);
mv.visitMethodInsn(
INVOKESTATIC, TRACE_EVENT_DESCRIPTOR, "end", TRACE_EVENT_SIGNATURE, false);
}
mv.visitInsn(opcode);
}
}

View File

@ -0,0 +1,87 @@
// Copyright 2017 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.bytecode;
import org.objectweb.asm.Type;
import java.util.HashMap;
import java.util.Map;
/**
* Utility methods for accessing {@link Type}s Strings.
*
* Useful definitions to keep in mind when using this class:
* Internal name - The fully qualified name for a type with dots replaced by slashes. Not really
* relevant for primitive types.
* Type descriptor - Single letters for primitive types, "L" + internal name + ";" for class types.
*
* The methods in this class accept internal names or primitive type descriptors.
*/
class TypeUtils {
static final String ASSERTION_ERROR = "java/lang/AssertionError";
static final String ASSET_MANAGER = "android/content/res/AssetManager";
static final String BUILD_HOOKS = "org/chromium/build/BuildHooks";
static final String BUILD_HOOKS_ANDROID = "org/chromium/build/BuildHooksAndroid";
static final String CONFIGURATION = "android/content/res/Configuration";
static final String CONTEXT = "android/content/Context";
static final String CONTEXT_WRAPPER = "android/content/ContextWrapper";
static final String RESOURCES = "android/content/res/Resources";
static final String STRING = "java/lang/String";
static final String THEME = "android/content/res/Resources$Theme";
static final String BOOLEAN = "Z";
static final String INT = "I";
static final String VOID = "V";
private static final Map<String, Type> PRIMITIVE_DESCRIPTORS;
static {
PRIMITIVE_DESCRIPTORS = new HashMap<>();
PRIMITIVE_DESCRIPTORS.put(Type.BOOLEAN_TYPE.toString(), Type.BOOLEAN_TYPE);
PRIMITIVE_DESCRIPTORS.put(Type.INT_TYPE.toString(), Type.INT_TYPE);
PRIMITIVE_DESCRIPTORS.put(Type.VOID_TYPE.toString(), Type.VOID_TYPE);
}
/**
* Returns the full method signature with internal names.
*
* @param methodName Name of the method (ex. "getResources").
* @param returnType Internal name for the return type.
* @param argumentTypes List of internal names for argument types.
* @return String representation of the method signature.
*/
static String getMethodSignature(
String methodName, String returnType, String... argumentTypes) {
return methodName + getMethodDescriptor(returnType, argumentTypes);
}
/**
* Builds a method descriptor suitable for use with {@link org.objectweb.asm.MethodVisitor}.
*
* @param returnType Internal name for the return type of the method (primitive or class).
* @param argumentTypes Internal names for the argument types (primitive or class).
* @return The generated method descriptor.
*/
static String getMethodDescriptor(String returnType, String... argumentTypes) {
Type[] typedArguments = new Type[argumentTypes.length];
for (int i = 0; i < argumentTypes.length; ++i) {
// Argument list should be empty in this case, not V (void).
assert !Type.VOID_TYPE.toString().equals(argumentTypes[i]);
typedArguments[i] = convert(argumentTypes[i]);
}
return Type.getMethodDescriptor(convert(returnType), typedArguments);
}
/**
* Converts an internal name for a type to a {@link Type}.
*
* @param type Internal name for a type (primitive or class).
* @return The resulting Type.
*/
private static Type convert(String type) {
if (PRIMITIVE_DESCRIPTORS.containsKey(type)) {
return PRIMITIVE_DESCRIPTORS.get(type);
}
return Type.getObjectType(type);
}
}

View File

@ -0,0 +1,15 @@
// Copyright 2014 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.helloworld;
public class HelloWorldMain {
public static void main(String[] args) {
if (args.length > 0) {
System.exit(Integer.parseInt(args[0]));
}
HelloWorldPrinter.print();
}
}

View File

@ -0,0 +1,12 @@
// Copyright 2014 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.helloworld;
public class HelloWorldPrinter {
public static void print() {
System.out.println("Hello, world!");
}
}

View File

@ -0,0 +1,297 @@
// Copyright 2015 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.incrementalinstall;
import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.util.Log;
import dalvik.system.DexFile;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
/**
* An Application that replaces itself with another Application (as defined in
* an AndroidManifext.xml meta-data tag). It loads the other application only
* after side-loading its .so and .dex files from /data/local/tmp.
*
* This class is highly dependent on the private implementation details of
* Android's ActivityThread.java. However, it has been tested to work with
* JellyBean through Marshmallow.
*/
public final class BootstrapApplication extends Application {
private static final String TAG = "incrementalinstall";
private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incremental-app-";
private static final String REAL_APP_META_DATA_NAME = "incremental-install-real-app";
private static final String REAL_INSTRUMENTATION_META_DATA_NAME0 =
"incremental-install-real-instrumentation-0";
private static final String REAL_INSTRUMENTATION_META_DATA_NAME1 =
"incremental-install-real-instrumentation-1";
private ClassLoaderPatcher mClassLoaderPatcher;
private Application mRealApplication;
private Instrumentation mOrigInstrumentation;
private Instrumentation mRealInstrumentation;
private Object mStashedProviderList;
private Object mActivityThread;
public static DexFile[] sIncrementalDexFiles; // Needed by junit test runner.
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
try {
mActivityThread = Reflect.invokeMethod(Class.forName("android.app.ActivityThread"),
"currentActivityThread");
mClassLoaderPatcher = new ClassLoaderPatcher(context);
mOrigInstrumentation =
(Instrumentation) Reflect.getField(mActivityThread, "mInstrumentation");
Context instContext = mOrigInstrumentation.getContext();
if (instContext == null) {
instContext = context;
}
// When running with an instrumentation that lives in a different package from the
// application, we must load the dex files and native libraries from both pacakges.
// This logic likely won't work when the instrumentation is incremental, but the app is
// non-incremental. This configuration isn't used right now though.
String appPackageName = getPackageName();
String instPackageName = instContext.getPackageName();
boolean instPackageNameDiffers = !appPackageName.equals(instPackageName);
Log.i(TAG, "App PackageName: " + appPackageName);
if (instPackageNameDiffers) {
Log.i(TAG, "Inst PackageName: " + instPackageName);
}
File appIncrementalRootDir = new File(MANAGED_DIR_PREFIX + appPackageName);
File appLibDir = new File(appIncrementalRootDir, "lib");
File appDexDir = new File(appIncrementalRootDir, "dex");
File appInstallLockFile = new File(appIncrementalRootDir, "install.lock");
File appFirstRunLockFile = new File(appIncrementalRootDir, "firstrun.lock");
File instIncrementalRootDir = new File(MANAGED_DIR_PREFIX + instPackageName);
File instLibDir = new File(instIncrementalRootDir, "lib");
File instDexDir = new File(instIncrementalRootDir, "dex");
File instInstallLockFile = new File(instIncrementalRootDir, "install.lock");
File instFirstRunLockFile = new File(instIncrementalRootDir, "firstrun.lock");
boolean isFirstRun = LockFile.installerLockExists(appFirstRunLockFile)
|| (instPackageNameDiffers
&& LockFile.installerLockExists(instFirstRunLockFile));
if (isFirstRun) {
if (mClassLoaderPatcher.mIsPrimaryProcess) {
// Wait for incremental_install.py to finish.
LockFile.waitForInstallerLock(appInstallLockFile, 30 * 1000);
LockFile.waitForInstallerLock(instInstallLockFile, 30 * 1000);
} else {
// Wait for the browser process to create the optimized dex files
// and copy the library files.
LockFile.waitForInstallerLock(appFirstRunLockFile, 60 * 1000);
LockFile.waitForInstallerLock(instFirstRunLockFile, 60 * 1000);
}
}
mClassLoaderPatcher.importNativeLibs(instLibDir);
sIncrementalDexFiles = mClassLoaderPatcher.loadDexFiles(instDexDir, instPackageName);
if (instPackageNameDiffers) {
mClassLoaderPatcher.importNativeLibs(appLibDir);
mClassLoaderPatcher.loadDexFiles(appDexDir, appPackageName);
}
if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) {
LockFile.clearInstallerLock(appFirstRunLockFile);
if (instPackageNameDiffers) {
LockFile.clearInstallerLock(instFirstRunLockFile);
}
}
// mInstrumentationAppDir is one of a set of fields that is initialized only when
// instrumentation is active.
if (Reflect.getField(mActivityThread, "mInstrumentationAppDir") != null) {
String metaDataName = REAL_INSTRUMENTATION_META_DATA_NAME0;
if (mOrigInstrumentation instanceof SecondInstrumentation) {
metaDataName = REAL_INSTRUMENTATION_META_DATA_NAME1;
}
mRealInstrumentation =
initInstrumentation(getClassNameFromMetadata(metaDataName, instContext));
} else {
Log.i(TAG, "No instrumentation active.");
}
// Even when instrumentation is not enabled, ActivityThread uses a default
// Instrumentation instance internally. We hook it here in order to hook into the
// call to Instrumentation.onCreate().
BootstrapInstrumentation bootstrapInstrumentation = new BootstrapInstrumentation(this);
populateInstrumenationFields(bootstrapInstrumentation);
Reflect.setField(mActivityThread, "mInstrumentation", bootstrapInstrumentation);
// attachBaseContext() is called from ActivityThread#handleBindApplication() and
// Application#mApplication is changed right after we return. Thus, we cannot swap
// the Application instances until onCreate() is called.
String realApplicationName = getClassNameFromMetadata(REAL_APP_META_DATA_NAME, context);
Log.i(TAG, "Instantiating " + realApplicationName);
Instrumentation anyInstrumentation =
mRealInstrumentation != null ? mRealInstrumentation : mOrigInstrumentation;
mRealApplication = anyInstrumentation.newApplication(
getClassLoader(), realApplicationName, context);
// Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate
// all ContentProviders. The ContentProviders break without the correct Application
// class being installed, so temporarily pretend there are no providers, and then
// instantiate them explicitly within onCreate().
disableContentProviders();
Log.i(TAG, "Waiting for Instrumentation.onCreate");
} catch (Exception e) {
throw new RuntimeException("Incremental install failed.", e);
}
}
/**
* Returns the fully-qualified class name for the given key, stored in a
* &lt;meta&gt; witin the manifest.
*/
private static String getClassNameFromMetadata(String key, Context context)
throws NameNotFoundException {
String pkgName = context.getPackageName();
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(pkgName,
PackageManager.GET_META_DATA);
String value = appInfo.metaData.getString(key);
if (value != null && !value.contains(".")) {
value = pkgName + "." + value;
}
return value;
}
/**
* Instantiates and initializes mRealInstrumentation (the real Instrumentation class).
*/
private Instrumentation initInstrumentation(String realInstrumentationName)
throws ReflectiveOperationException {
if (realInstrumentationName == null) {
// This is the case when an incremental app is used as a target for an instrumentation
// test. In this case, ActivityThread can instantiate the proper class just fine since
// it exists within the test apk (as opposed to the incremental apk-under-test).
Log.i(TAG, "Running with external instrumentation");
return null;
}
// For unit tests, the instrumentation class is replaced in the manifest by a build step
// because ActivityThread tries to instantiate it before we get a chance to load the
// incremental dex files.
Log.i(TAG, "Instantiating instrumentation " + realInstrumentationName);
Instrumentation ret =
(Instrumentation) Reflect.newInstance(Class.forName(realInstrumentationName));
populateInstrumenationFields(ret);
return ret;
}
/**
* Sets important fields on a newly created Instrumentation object by copying them from the
* original Instrumentation instance.
*/
private void populateInstrumenationFields(Instrumentation target)
throws ReflectiveOperationException {
// Initialize the fields that are set by Instrumentation.init().
String[] initFields = {"mAppContext", "mComponent", "mInstrContext", "mMessageQueue",
"mThread", "mUiAutomationConnection", "mWatcher"};
for (String fieldName : initFields) {
Reflect.setField(target, fieldName, Reflect.getField(mOrigInstrumentation, fieldName));
}
}
/**
* Called by BootstrapInstrumentation from Instrumentation.onCreate().
* This happens regardless of whether or not instrumentation is enabled.
*/
void onInstrumentationCreate(Bundle arguments) {
Log.i(TAG, "Instrumentation.onCreate() called. Swapping references.");
try {
swapApplicationReferences();
enableContentProviders();
if (mRealInstrumentation != null) {
Reflect.setField(mActivityThread, "mInstrumentation", mRealInstrumentation);
mRealInstrumentation.onCreate(arguments);
}
} catch (Exception e) {
throw new RuntimeException("Incremental install failed.", e);
}
}
@Override
public void onCreate() {
super.onCreate();
try {
Log.i(TAG, "Application.onCreate() called.");
mRealApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException("Incremental install failed.", e);
}
}
/**
* Nulls out ActivityThread.mBoundApplication.providers.
*/
private void disableContentProviders() throws ReflectiveOperationException {
Object data = Reflect.getField(mActivityThread, "mBoundApplication");
mStashedProviderList = Reflect.getField(data, "providers");
Reflect.setField(data, "providers", null);
}
/**
* Restores the value of ActivityThread.mBoundApplication.providers, and invokes
* ActivityThread#installContentProviders().
*/
private void enableContentProviders() throws ReflectiveOperationException {
Object data = Reflect.getField(mActivityThread, "mBoundApplication");
Reflect.setField(data, "providers", mStashedProviderList);
if (mStashedProviderList != null && mClassLoaderPatcher.mIsPrimaryProcess) {
Log.i(TAG, "Instantiating content providers");
Reflect.invokeMethod(mActivityThread, "installContentProviders", mRealApplication,
mStashedProviderList);
}
mStashedProviderList = null;
}
/**
* Changes all fields within framework classes that have stored an reference to this
* BootstrapApplication to instead store references to mRealApplication.
*/
@SuppressWarnings("unchecked")
private void swapApplicationReferences() throws ReflectiveOperationException {
if (Reflect.getField(mActivityThread, "mInitialApplication") == this) {
Reflect.setField(mActivityThread, "mInitialApplication", mRealApplication);
}
List<Application> allApplications =
(List<Application>) Reflect.getField(mActivityThread, "mAllApplications");
for (int i = 0; i < allApplications.size(); i++) {
if (allApplications.get(i) == this) {
allApplications.set(i, mRealApplication);
}
}
// Contains a reference to BootstrapApplication and will cause BroadCastReceivers to fail
// if not replaced.
Context contextImpl = mRealApplication.getBaseContext();
Reflect.setField(contextImpl, "mOuterContext", mRealApplication);
for (String fieldName : new String[] {"mPackages", "mResourcePackages"}) {
Map<String, WeakReference<?>> packageMap =
(Map<String, WeakReference<?>>) Reflect.getField(mActivityThread, fieldName);
for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet()) {
Object loadedApk = entry.getValue().get();
if (loadedApk != null && Reflect.getField(loadedApk, "mApplication") == this) {
Reflect.setField(loadedApk, "mApplication", mRealApplication);
}
}
}
}
}

View File

@ -0,0 +1,25 @@
// Copyright 2015 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.incrementalinstall;
import android.app.Instrumentation;
import android.os.Bundle;
/**
* Notifies BootstrapApplication of the call to Instrumentation.onCreate().
*/
public final class BootstrapInstrumentation extends Instrumentation {
private final BootstrapApplication mApp;
BootstrapInstrumentation(BootstrapApplication app) {
mApp = app;
}
@Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);
mApp.onInstrumentationCreate(arguments);
}
}

View File

@ -0,0 +1,312 @@
// Copyright 2015 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.incrementalinstall;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import dalvik.system.DexFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
/**
* Provides the ability to add native libraries and .dex files to an existing class loader.
* Tested with Jellybean MR2 - Marshmellow.
*/
final class ClassLoaderPatcher {
private static final String TAG = "incrementalinstall";
private final File mAppFilesSubDir;
private final ClassLoader mClassLoader;
private final Object mLibcoreOs;
private final int mProcessUid;
final boolean mIsPrimaryProcess;
ClassLoaderPatcher(Context context) throws ReflectiveOperationException {
mAppFilesSubDir =
new File(context.getApplicationInfo().dataDir, "incremental-install-files");
mClassLoader = context.getClassLoader();
mLibcoreOs = Reflect.getField(Class.forName("libcore.io.Libcore"), "os");
mProcessUid = Process.myUid();
mIsPrimaryProcess = context.getApplicationInfo().uid == mProcessUid;
Log.i(TAG, "uid=" + mProcessUid + " (isPrimary=" + mIsPrimaryProcess + ")");
}
/**
* Loads all dex files within |dexDir| into the app's ClassLoader.
*/
@SuppressLint({
"SetWorldReadable",
"SetWorldWritable",
})
DexFile[] loadDexFiles(File dexDir, String packageName)
throws ReflectiveOperationException, IOException {
Log.i(TAG, "Installing dex files from: " + dexDir);
File optimizedDir = null;
boolean isAtLeastOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
if (isAtLeastOreo) {
// In O, optimizedDirectory is ignored, and the files are always put in an "oat"
// directory that is a sibling to the dex files themselves. SELinux policies
// prevent using odex files from /data/local/tmp, so we must first copy them
// into the app's data directory in order to get the odex files to live there.
// Use a package-name subdirectory to prevent name collisions when apk-under-test is
// used.
File newDexDir = new File(mAppFilesSubDir, packageName + "-dexes");
if (mIsPrimaryProcess) {
safeCopyAllFiles(dexDir, newDexDir);
}
dexDir = newDexDir;
} else {
// The optimized dex files will be owned by this process' user.
// Store them within the app's data dir rather than on /data/local/tmp
// so that they are still deleted (by the OS) when we uninstall
// (even on a non-rooted device).
File incrementalDexesDir = new File(mAppFilesSubDir, "optimized-dexes");
File isolatedDexesDir = new File(mAppFilesSubDir, "isolated-dexes");
if (mIsPrimaryProcess) {
ensureAppFilesSubDirExists();
// Allows isolated processes to access the same files.
incrementalDexesDir.mkdir();
incrementalDexesDir.setReadable(true, false);
incrementalDexesDir.setExecutable(true, false);
// Create a directory for isolated processes to create directories in.
isolatedDexesDir.mkdir();
isolatedDexesDir.setWritable(true, false);
isolatedDexesDir.setExecutable(true, false);
optimizedDir = incrementalDexesDir;
} else {
// There is a UID check of the directory in dalvik.system.DexFile():
// https://android.googlesource.com/platform/libcore/+/45e0260/dalvik/src/main/java/dalvik/system/DexFile.java#101
// Rather than have each isolated process run DexOpt though, we use
// symlinks within the directory to point at the browser process'
// optimized dex files.
optimizedDir = new File(isolatedDexesDir, "isolated-" + mProcessUid);
optimizedDir.mkdir();
// Always wipe it out and re-create for simplicity.
Log.i(TAG, "Creating dex file symlinks for isolated process");
for (File f : optimizedDir.listFiles()) {
f.delete();
}
for (File f : incrementalDexesDir.listFiles()) {
String to = "../../" + incrementalDexesDir.getName() + "/" + f.getName();
File from = new File(optimizedDir, f.getName());
createSymlink(to, from);
}
}
Log.i(TAG, "Code cache dir: " + optimizedDir);
}
// Ignore "oat" directory.
// Also ignore files that sometimes show up (e.g. .jar.arm.flock).
File[] dexFilesArr = dexDir.listFiles(f -> f.getName().endsWith(".jar"));
if (dexFilesArr == null) {
throw new FileNotFoundException("Dex dir does not exist: " + dexDir);
}
Log.i(TAG, "Loading " + dexFilesArr.length + " dex files");
Object dexPathList = Reflect.getField(mClassLoader, "pathList");
Object[] dexElements = (Object[]) Reflect.getField(dexPathList, "dexElements");
dexElements = addDexElements(dexFilesArr, optimizedDir, dexElements);
Reflect.setField(dexPathList, "dexElements", dexElements);
// Return the list of new DexFile instances for the .jars in dexPathList.
DexFile[] ret = new DexFile[dexFilesArr.length];
int startIndex = dexElements.length - dexFilesArr.length;
for (int i = 0; i < ret.length; ++i) {
ret[i] = (DexFile) Reflect.getField(dexElements[startIndex + i], "dexFile");
}
return ret;
}
/**
* Sets up all libraries within |libDir| to be loadable by System.loadLibrary().
*/
@SuppressLint("SetWorldReadable")
void importNativeLibs(File libDir) throws ReflectiveOperationException, IOException {
Log.i(TAG, "Importing native libraries from: " + libDir);
if (!libDir.exists()) {
Log.i(TAG, "No native libs exist.");
return;
}
// The library copying is not necessary on older devices, but we do it anyways to
// simplify things (it's fast compared to dexing).
// https://code.google.com/p/android/issues/detail?id=79480
File localLibsDir = new File(mAppFilesSubDir, "lib");
safeCopyAllFiles(libDir, localLibsDir);
addNativeLibrarySearchPath(localLibsDir);
}
@SuppressLint("SetWorldReadable")
private void safeCopyAllFiles(File srcDir, File dstDir) throws IOException {
// The library copying is not necessary on older devices, but we do it anyways to
// simplify things (it's fast compared to dexing).
// https://code.google.com/p/android/issues/detail?id=79480
File lockFile = new File(mAppFilesSubDir, dstDir.getName() + ".lock");
if (mIsPrimaryProcess) {
ensureAppFilesSubDirExists();
LockFile lock = LockFile.acquireRuntimeLock(lockFile);
if (lock == null) {
LockFile.waitForRuntimeLock(lockFile, 10 * 1000);
} else {
try {
dstDir.mkdir();
dstDir.setReadable(true, false);
dstDir.setExecutable(true, false);
copyChangedFiles(srcDir, dstDir);
} finally {
lock.release();
}
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// TODO: Work around this issue by using APK splits to install each dex / lib.
throw new RuntimeException("Incremental install does not work on Android M+ "
+ "with isolated processes. Build system should have removed this. "
+ "Please file a bug.");
}
// Other processes: Waits for primary process to finish copying.
LockFile.waitForRuntimeLock(lockFile, 10 * 1000);
}
}
@SuppressWarnings("unchecked")
private void addNativeLibrarySearchPath(File nativeLibDir) throws ReflectiveOperationException {
Object dexPathList = Reflect.getField(mClassLoader, "pathList");
Object currentDirs = Reflect.getField(dexPathList, "nativeLibraryDirectories");
File[] newDirs = new File[] { nativeLibDir };
// Switched from an array to an ArrayList in Lollipop.
if (currentDirs instanceof List) {
List<File> dirsAsList = (List<File>) currentDirs;
dirsAsList.add(0, nativeLibDir);
} else {
File[] dirsAsArray = (File[]) currentDirs;
Reflect.setField(dexPathList, "nativeLibraryDirectories",
Reflect.concatArrays(newDirs, newDirs, dirsAsArray));
}
Object[] nativeLibraryPathElements;
try {
nativeLibraryPathElements =
(Object[]) Reflect.getField(dexPathList, "nativeLibraryPathElements");
} catch (NoSuchFieldException e) {
// This field doesn't exist pre-M.
return;
}
Object[] additionalElements = makeNativePathElements(newDirs);
Reflect.setField(dexPathList, "nativeLibraryPathElements",
Reflect.concatArrays(nativeLibraryPathElements, additionalElements,
nativeLibraryPathElements));
}
private static void copyChangedFiles(File srcDir, File dstDir) throws IOException {
int numUpdated = 0;
File[] srcFiles = srcDir.listFiles();
for (File f : srcFiles) {
// Note: Tried using hardlinks, but resulted in EACCES exceptions.
File dest = new File(dstDir, f.getName());
if (copyIfModified(f, dest)) {
numUpdated++;
}
}
// Delete stale files.
int numDeleted = 0;
for (File f : dstDir.listFiles()) {
File src = new File(srcDir, f.getName());
if (!src.exists()) {
numDeleted++;
f.delete();
}
}
String msg = String.format(Locale.US,
"copyChangedFiles: %d of %d updated. %d stale files removed.", numUpdated,
srcFiles.length, numDeleted);
Log.i(TAG, msg);
}
@SuppressLint("SetWorldReadable")
private static boolean copyIfModified(File src, File dest) throws IOException {
long lastModified = src.lastModified();
if (dest.exists() && dest.lastModified() == lastModified) {
return false;
}
Log.i(TAG, "Copying " + src + " -> " + dest);
FileInputStream istream = new FileInputStream(src);
FileOutputStream ostream = new FileOutputStream(dest);
ostream.getChannel().transferFrom(istream.getChannel(), 0, istream.getChannel().size());
istream.close();
ostream.close();
dest.setReadable(true, false);
dest.setExecutable(true, false);
dest.setLastModified(lastModified);
return true;
}
private void ensureAppFilesSubDirExists() {
mAppFilesSubDir.mkdir();
mAppFilesSubDir.setExecutable(true, false);
}
private void createSymlink(String to, File from) throws ReflectiveOperationException {
Reflect.invokeMethod(mLibcoreOs, "symlink", to, from.getAbsolutePath());
}
private static Object[] makeNativePathElements(File[] paths)
throws ReflectiveOperationException {
Object[] entries = new Object[paths.length];
if (Build.VERSION.SDK_INT >= 26) {
Class<?> entryClazz = Class.forName("dalvik.system.DexPathList$NativeLibraryElement");
for (int i = 0; i < paths.length; ++i) {
entries[i] = Reflect.newInstance(entryClazz, paths[i]);
}
} else {
Class<?> entryClazz = Class.forName("dalvik.system.DexPathList$Element");
for (int i = 0; i < paths.length; ++i) {
entries[i] = Reflect.newInstance(entryClazz, paths[i], true, null, null);
}
}
return entries;
}
private Object[] addDexElements(File[] files, File optimizedDirectory, Object[] curDexElements)
throws ReflectiveOperationException {
Class<?> entryClazz = Class.forName("dalvik.system.DexPathList$Element");
Class<?> clazz = Class.forName("dalvik.system.DexPathList");
Object[] ret =
Reflect.concatArrays(curDexElements, curDexElements, new Object[files.length]);
File emptyDir = new File("");
for (int i = 0; i < files.length; ++i) {
File file = files[i];
Object dexFile;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// loadDexFile requires that ret contain all previously added elements.
dexFile = Reflect.invokeMethod(clazz, "loadDexFile", file, optimizedDirectory,
mClassLoader, ret);
} else {
dexFile = Reflect.invokeMethod(clazz, "loadDexFile", file, optimizedDirectory);
}
Object dexElement;
if (Build.VERSION.SDK_INT >= 26) {
dexElement = Reflect.newInstance(entryClazz, dexFile, file);
} else {
dexElement = Reflect.newInstance(entryClazz, emptyDir, false, file, dexFile);
}
ret[curDexElements.length + i] = dexElement;
}
return ret;
}
}

View File

@ -0,0 +1,129 @@
// Copyright 2015 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.incrementalinstall;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.util.concurrent.Callable;
/**
* Helpers for dealing with .lock files used during install / first run.
*/
final class LockFile {
private static final String TAG = "incrementalinstall";
private final File mFile;
private final FileOutputStream mOutputStream;
private final FileLock mFileLock;
private LockFile(File file, FileOutputStream outputStream, FileLock fileLock) {
mFile = file;
mOutputStream = outputStream;
mFileLock = fileLock;
}
/**
* Clears the lock file by writing to it (making it non-zero in length);
*/
static void clearInstallerLock(File lockFile) throws IOException {
Log.i(TAG, "Clearing " + lockFile);
// On Android M+, we can't delete files in /data/local/tmp, so we write to it instead.
FileOutputStream os = new FileOutputStream(lockFile);
os.write(1);
os.close();
}
/**
* Waits for the given file to be non-zero in length.
*/
static void waitForInstallerLock(final File file, long timeoutMs) {
pollingWait(new Callable<Boolean>() {
@Override public Boolean call() {
return !installerLockExists(file);
}
}, file, timeoutMs);
}
/**
* Waits for the given file to be non-zero in length.
*/
private static void pollingWait(Callable<Boolean> func, File file, long timeoutMs) {
long pollIntervalMs = 200;
for (int i = 0; i < timeoutMs / pollIntervalMs; i++) {
try {
if (func.call()) {
if (i > 0) {
Log.i(TAG, "Finished waiting on lock file: " + file);
}
return;
} else if (i == 0) {
Log.i(TAG, "Waiting on lock file: " + file);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
Thread.sleep(pollIntervalMs);
} catch (InterruptedException e) {
// Should never happen.
}
}
throw new RuntimeException("Timed out waiting for lock file: " + file);
}
/**
* Returns whether the given lock file is missing or is in the locked state.
*/
static boolean installerLockExists(File file) {
return !file.exists() || file.length() == 0;
}
/**
* Attempts to acquire a lock for the given file.
* @return Returns the FileLock if it was acquired, or null otherwise.
*/
static LockFile acquireRuntimeLock(File file) {
try {
FileOutputStream outputStream = new FileOutputStream(file);
FileLock lock = outputStream.getChannel().tryLock();
if (lock != null) {
Log.i(TAG, "Created lock file: " + file);
return new LockFile(file, outputStream, lock);
}
outputStream.close();
} catch (IOException e) {
// Do nothing. We didn't get the lock.
Log.w(TAG, "Exception trying to acquire lock " + file, e);
}
return null;
}
/**
* Waits for the given file to not exist.
*/
static void waitForRuntimeLock(final File file, long timeoutMs) {
pollingWait(new Callable<Boolean>() {
@Override public Boolean call() {
return !file.exists();
}
}, file, timeoutMs);
}
/**
* Releases and deletes the lock file.
*/
void release() throws IOException {
Log.i(TAG, "Deleting lock file: " + mFile);
mFileLock.release();
mOutputStream.close();
if (!mFile.delete()) {
throw new IOException("Failed to delete lock file: " + mFile);
}
}
}

View File

@ -0,0 +1,142 @@
// Copyright 2015 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.incrementalinstall;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Reflection helper methods.
*/
final class Reflect {
/**
* Sets the value of an object's field (even if it's not visible).
*
* @param instance The object containing the field to set.
* @param name The name of the field to set.
* @param value The new value for the field.
*/
static void setField(Object instance, String name, Object value)
throws ReflectiveOperationException {
Field field = findField(instance, name);
field.setAccessible(true);
field.set(instance, value);
}
/**
* Retrieves the value of an object's field (even if it's not visible).
*
* @param instance The object containing the field to set.
* @param name The name of the field to set.
* @return The field's value. Primitive values are returned as their boxed
* type.
*/
static Object getField(Object instance, String name) throws ReflectiveOperationException {
Field field = findField(instance, name);
field.setAccessible(true);
return field.get(instance);
}
/**
* Concatenates two arrays into a new array. The arrays must be of the same
* type.
*/
static Object[] concatArrays(Object[] arrType, Object[] left, Object[] right) {
Object[] result = (Object[]) Array.newInstance(
arrType.getClass().getComponentType(), left.length + right.length);
System.arraycopy(left, 0, result, 0, left.length);
System.arraycopy(right, 0, result, left.length, right.length);
return result;
}
/**
* Invokes a method with zero or more parameters. For static methods, use the Class as the
* instance.
*/
static Object invokeMethod(Object instance, String name, Object... params)
throws ReflectiveOperationException {
boolean isStatic = instance instanceof Class;
Class<?> clazz = isStatic ? (Class<?>) instance : instance.getClass();
Method method = findMethod(clazz, name, params);
method.setAccessible(true);
return method.invoke(instance, params);
}
/**
* Calls a constructor with zero or more parameters.
*/
static Object newInstance(Class<?> clazz, Object... params)
throws ReflectiveOperationException {
Constructor<?> constructor = findConstructor(clazz, params);
constructor.setAccessible(true);
return constructor.newInstance(params);
}
private static Field findField(Object instance, String name) throws NoSuchFieldException {
boolean isStatic = instance instanceof Class;
Class<?> clazz = isStatic ? (Class<?>) instance : instance.getClass();
for (; clazz != null; clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
// Need to look in the super class.
}
}
throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
}
private static Method findMethod(Class<?> clazz, String name, Object... params)
throws NoSuchMethodException {
for (; clazz != null; clazz = clazz.getSuperclass()) {
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().equals(name)
&& areParametersCompatible(method.getParameterTypes(), params)) {
return method;
}
}
}
throw new NoSuchMethodException("Method " + name + " with parameters "
+ Arrays.asList(params) + " not found in " + clazz);
}
private static Constructor<?> findConstructor(Class<?> clazz, Object... params)
throws NoSuchMethodException {
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
if (areParametersCompatible(constructor.getParameterTypes(), params)) {
return constructor;
}
}
throw new NoSuchMethodException("Constructor with parameters " + Arrays.asList(params)
+ " not found in " + clazz);
}
private static boolean areParametersCompatible(Class<?>[] paramTypes, Object... params) {
if (params.length != paramTypes.length) {
return false;
}
for (int i = 0; i < params.length; i++) {
if (!isAssignableFrom(paramTypes[i], params[i])) {
return false;
}
}
return true;
}
private static boolean isAssignableFrom(Class<?> left, Object right) {
if (right == null) {
return !left.isPrimitive();
}
Class<?> rightClazz = right.getClass();
if (left.isPrimitive()) {
// TODO(agrieve): Fill in the rest as needed.
return left == boolean.class && rightClazz == Boolean.class
|| left == int.class && rightClazz == Integer.class;
}
return left.isAssignableFrom(rightClazz);
}
}

View File

@ -0,0 +1,12 @@
// Copyright 2017 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.incrementalinstall;
import android.app.Instrumentation;
/**
* Exists to support an app having multiple instrumentations.
*/
public final class SecondInstrumentation extends Instrumentation {}

View File

@ -0,0 +1,116 @@
// Copyright 2017 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.build;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import proguard.retrace.ReTrace;
/**
* A wrapper around ReTrace that:
* 1. Hardcodes a more useful line regular expression
* 2. Disables output buffering
*/
public class FlushingReTrace {
// E.g.: D/ConnectivityService(18029): Message
// E.g.: W/GCM ( 151): Message
// E.g.: 09-08 14:22:59.995 18029 18055 I ProcessStatsService: Message
// E.g.: 09-08 14:30:59.145 17731 18020 D MDnsDS : Message
private static final String LOGCAT_PREFIX =
"(?:[VDIWEF]/.*?\\( *\\d+\\): |\\d\\d-\\d\\d [0-9:. ]+[VDIWEF] .*?: )?";
// Note: Order of these sub-patterns defines their precedence.
// Note: Deobfuscation of methods without the presense of line numbers basically never works.
// There is a test for these pattern at //build/android/stacktrace/java_deobfuscate_test.py
private static final String LINE_PARSE_REGEX =
// Eagerly match logcat prefix to avoid conflicting with the patterns below.
LOGCAT_PREFIX
+ "(?:"
// Based on default ReTrace regex, but with whitespaces allowed in file:line parentheses
// and "at" changed to to allow :
// E.g.: 06-22 13:58:02.895 4674 4674 E THREAD_STATE: bLA.a( PG : 173 )
// Normal stack trace lines look like:
// \tat org.chromium.chrome.browser.tab.Tab.handleJavaCrash(Tab.java:682)
+ "(?:.*?(?::|\\bat)\\s+%c\\.%m\\s*\\(\\s*%s(?:\\s*:\\s*%l\\s*)?\\))|"
// E.g.: Caused by: java.lang.NullPointerException: Attempt to read from field 'int bLA'
// on a null object reference
+ "(?:.*java\\.lang\\.NullPointerException.*[\"']%t\\s*%c\\.(?:%f|%m\\(%a\\))[\"'].*)|"
// E.g.: java.lang.VerifyError: bLA
+ "(?:java\\.lang\\.VerifyError: %c)|"
// E.g.: java.lang.NoSuchFieldError: No instance field e of type L...; in class LbxK;
+ "(?:java\\.lang\\.NoSuchFieldError: No instance field %f of type .*? in class L%C;)|"
// E.g.: Object of type Clazz was not destroyed... (See LifetimeAssert.java)
+ "(?:.*?Object of type %c .*)|"
// E.g.: VFY: unable to resolve new-instance 3810 (LSome/Framework/Class;) in Lfoo/Bar;
+ "(?:.*L%C;.*)|"
// E.g.: END SomeTestClass#someMethod
+ "(?:.*?%c#%m.*?)|"
// Special-case for a common junit logcat message:
// E.g.: java.lang.NoClassDefFoundError: SomeFrameworkClass in isTestClass for Foo
+ "(?:.* isTestClass for %c)|"
// E.g.: Caused by: java.lang.RuntimeException: Intentional Java Crash
+ "(?:Caused by: %c:.*)|"
// Quoted values and lines that end with a class / class+method:
// E.g.: The class: Foo
// E.g.: INSTRUMENTATION_STATUS: class=Foo
// E.g.: NoClassDefFoundError: SomeFrameworkClass in isTestClass for Foo
// E.g.: Could not find class 'SomeFrameworkClass', referenced from method Foo.bar
// E.g.: Could not find method SomeFrameworkMethod, referenced from method Foo.bar
// E.g.: The member "Foo.bar"
// E.g.: The class "Foobar"
// Be careful about matching %c without %m since language tags look like class names.
+ "(?:.*?%c\\.%m)|"
+ "(?:.*?\"%c\\.%m\".*)|"
+ "(?:.*\\b(?:[Cc]lass|[Tt]ype)\\b.*?\"%c\".*)|"
+ "(?:.*\\b(?:[Cc]lass|[Tt]ype)\\b.*?%c)|"
// E.g.: java.lang.RuntimeException: Intentional Java Crash
+ "(?:%c:.*)|"
// See if entire line matches a class name (e.g. for manual deobfuscation)
+ "(?:%c)"
+ ")";
private static void usage() {
System.err.println("Usage: echo $OBFUSCATED_CLASS | java_deobfuscate Foo.apk.mapping");
System.err.println("Usage: java_deobfuscate Foo.apk.mapping < foo.log");
System.err.println("Note: Deobfuscation of symbols outside the context of stack "
+ "traces will work only when lines match the regular expression defined "
+ "in FlushingReTrace.java.");
System.err.println("Also: Deobfuscation of method names without associated line "
+ "numbers does not seem to work.");
System.exit(1);
}
public static void main(String[] args) {
if (args.length != 1 || args[0].startsWith("-")) {
usage();
}
File mappingFile = new File(args[0]);
try {
LineNumberReader reader = new LineNumberReader(
new BufferedReader(new InputStreamReader(System.in, "UTF-8")));
// Enabling autoFlush is the main difference from ReTrace.main().
boolean autoFlush = true;
PrintWriter writer =
new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"), autoFlush);
boolean verbose = false;
new ReTrace(LINE_PARSE_REGEX, verbose, mappingFile).retrace(reader, writer);
} catch (IOException ex) {
// Print a verbose stack trace.
ex.printStackTrace();
System.exit(1);
}
System.exit(0);
}
}

View File

@ -347,7 +347,7 @@ default_compiler_configs = [
"//build/config/compiler:prevent_unsafe_narrowing",
"//build/config/compiler:runtime_library",
"//build/config/compiler:thin_archive",
"//build/config/compiler:thinlto_optimize_max",
"//build/config/compiler:thinlto_optimize_default",
"//build/config/compiler/pgo:default_pgo_flags",
"//build/config/coverage:default_coverage",
"//build/config/sanitizers:default_sanitizer_flags",

View File

@ -12,6 +12,4 @@ clang_toolchain(target_cpu) {
current_cpu = target_cpu
current_os = "openwrt"
}
strip = rebase_path("$clang_base_path/bin/llvm-strip", root_build_dir)
}

View File

@ -1,213 +0,0 @@
# Copyright 2017 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.
import("//build/buildflag_header.gni")
import("//build/config/c++/c++.gni")
import("//build/toolchain/toolchain.gni")
import("//build/util/lastchange.gni")
import("//build/util/process_version.gni")
import("//build/util/version.gni")
import("//components/cronet/native/include/headers.gni")
import("//components/grpc_support/include/headers.gni")
declare_args() {
# If set to true, this will remove histogram manager to reduce binary size.
disable_histogram_support = true
}
# Disable histogram support is not allowed on Android.
#assert(!disable_histogram_support || !is_android)
buildflag_header("cronet_buildflags") {
header = "cronet_buildflags.h"
flags = [ "DISABLE_HISTOGRAM_SUPPORT=$disable_histogram_support" ]
}
process_version("cronet_version_header") {
template_file = "//components/cronet/version.h.in"
sources = [ "//chrome/VERSION" ]
output = "$target_gen_dir/version.h"
extra_args = [
"-e",
"VERSION_FULL=\"%s.%s.%s.%s\" % (MAJOR,MINOR,BUILD,PATCH)",
]
}
# Cronet common implementation.
source_set("cronet_common") {
sources = [
"cronet_context.cc",
"cronet_context.h",
"cronet_global_state.h",
"cronet_prefs_manager.cc",
"cronet_prefs_manager.h",
"cronet_upload_data_stream.cc",
"cronet_upload_data_stream.h",
"cronet_url_request.cc",
"cronet_url_request.h",
"host_cache_persistence_manager.cc",
"host_cache_persistence_manager.h",
"stale_host_resolver.cc",
"stale_host_resolver.h",
"url_request_context_config.cc",
"url_request_context_config.h",
]
deps = [
":cronet_buildflags",
":cronet_version_header",
"//base",
"//components/prefs:prefs",
"//net",
]
if (!disable_histogram_support) {
public_deps = [ "//components/metrics:library_support" ]
}
}
source_set("metrics_util") {
sources = [
"metrics_util.cc",
"metrics_util.h",
]
deps = [ "//base" ]
}
# For platforms on which the native Cronet library is used, build the library,
# a cronet_tests binary that exercises it, and a unit-tests binary.
# Android and iOS have their own platform-specific rules to build Cronet.
if (false) {
group("cronet_package") {
testonly = true
deps = [ "//components/cronet/android:cronet_package_android" ]
}
} else if (is_ios) {
group("cronet_package") {
deps = [ "//components/cronet/ios:cronet_package_ios" ]
}
} else {
config("shared_library_public_config") {
if (is_mac && !is_component_build) {
# Executable targets that depend on the shared libraries below need to have
# the rpath setup in non-component build configurations.
ldflags = [ "-Wl,-rpath,@executable_path/" ]
}
}
_cronet_shared_lib_name = "cronet"
_cronet_shared_lib_file_name =
"$shlib_prefix$_cronet_shared_lib_name$shlib_extension"
static_library("cronet_static") {
deps = [
"//base",
"//components/cronet:cronet_common",
"//components/cronet/native:cronet_native_impl",
"//net",
]
if (use_custom_libcxx) {
deps += [
# Add shared_library_deps to include custom libc++ into dependencies.
# They are by default only added to executable(), loadable_module(), and
# shared_library() targets, but cronet_static_complete library needs it as well to
# avoid linking with different versions of libc++.
"//build/config:shared_library_deps",
]
}
sources = [ "cronet_global_state_stubs.cc" ]
complete_static_lib = true
configs -= [ "//build/config/compiler:thin_archive" ]
}
shared_library("cronet") {
output_name = _cronet_shared_lib_name
deps = [
"//base",
"//components/cronet:cronet_common",
"//components/cronet/native:cronet_native_impl",
"//net",
]
sources = [ "cronet_global_state_stubs.cc" ]
if (is_mac && !is_component_build) {
ldflags = [
"-install_name",
"@executable_path/$_cronet_shared_lib_file_name",
]
public_configs = [ ":shared_library_public_config" ]
}
if (is_android) {
configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
}
}
_package_dir = "$root_out_dir/cronet"
# Generate LICENSE file by recursively joining all dependent licenses.
action("generate_license") {
_license_path = "$_package_dir/LICENSE"
script = "//tools/licenses.py"
inputs = [ lastchange_file ]
outputs = [ _license_path ]
args = [
"license_file",
rebase_path(_license_path, root_build_dir),
"--gn-target",
"//components/cronet:cronet",
"--gn-out-dir",
".",
]
}
# Copy boiler-plate files into the package.
copy("cronet_package_copy") {
sources = [
"${root_out_dir}${shlib_subdir}/${_cronet_shared_lib_file_name}",
"//AUTHORS",
"//chrome/VERSION",
]
deps = [ ":cronet" ]
outputs = [ "$_package_dir/{{source_file_part}}" ]
}
# Copy headers.
copy("cronet_package_headers") {
sources = cronet_native_public_headers + grpc_public_headers
outputs = [ "$_package_dir/include/{{source_file_part}}" ]
}
group("cronet_package") {
deps = [
":cronet_package_copy",
":cronet_package_headers",
":generate_license",
]
}
executable("cronet_sample") {
testonly = true
sources = [
"native/sample/main.cc",
"native/sample/sample_executor.cc",
"native/sample/sample_executor.h",
"native/sample/sample_url_request_callback.cc",
"native/sample/sample_url_request_callback.h",
]
deps = [
"//components/cronet",
"//components/cronet/native:cronet_native_headers",
]
if ((is_linux || is_chromeos) && !is_component_build) {
public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
}
}
}

View File

@ -1,8 +0,0 @@
include_rules = [
"+components/grpc_support",
"+components/metrics",
"+components/prefs",
"+net",
"+third_party/metrics_proto",
"+third_party/zlib",
]

View File

@ -1,5 +0,0 @@
monorail {
component: "Internals>Network>Library"
}
team_email: "net-dev@chromium.org"

View File

@ -1,5 +0,0 @@
cleborgne@google.com
danstahr@google.com
sporeba@google.com
torne@chromium.org
file://net/OWNERS

View File

@ -1,107 +0,0 @@
# Copyright 2015 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.
"""Top-level presubmit script for src/components/cronet.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
import os
USE_PYTHON3 = True
def _PyLintChecks(input_api, output_api):
pylint_checks = input_api.canned_checks.GetPylint(input_api, output_api,
extra_paths_list=_GetPathsToPrepend(input_api), pylintrc='pylintrc',
version='2.7')
return input_api.RunTests(pylint_checks)
def _GetPathsToPrepend(input_api):
current_dir = input_api.PresubmitLocalPath()
chromium_src_dir = input_api.os_path.join(current_dir, '..', '..')
return [
input_api.os_path.join(chromium_src_dir, 'components'),
input_api.os_path.join(chromium_src_dir, 'tools', 'perf'),
input_api.os_path.join(chromium_src_dir, 'build', 'android'),
input_api.os_path.join(chromium_src_dir, 'build', 'android', 'gyp'),
input_api.os_path.join(chromium_src_dir,
'mojo', 'public', 'tools', 'bindings', 'pylib'),
input_api.os_path.join(chromium_src_dir, 'net', 'tools', 'net_docs'),
input_api.os_path.join(chromium_src_dir, 'tools'),
input_api.os_path.join(chromium_src_dir, 'third_party'),
input_api.os_path.join(chromium_src_dir,
'third_party', 'catapult', 'telemetry'),
input_api.os_path.join(chromium_src_dir,
'third_party', 'catapult', 'devil'),
input_api.os_path.join(chromium_src_dir,
'third_party', 'catapult', 'common', 'py_utils'),
]
def _PackageChecks(input_api, output_api):
"""Verify API classes are in org.chromium.net package, and implementation
classes are not in org.chromium.net package."""
api_file_pattern = input_api.re.compile(
r'^components/cronet/android/api/.*\.(java|template)$')
impl_file_pattern = input_api.re.compile(
r'^components/cronet/android/java/.*\.(java|template)$')
api_package_pattern = input_api.re.compile(r'^package (?!org.chromium.net;)')
impl_package_pattern = input_api.re.compile(r'^package org.chromium.net;')
source_filter = lambda path: input_api.FilterSourceFile(path,
files_to_check=[r'^components/cronet/android/.*\.(java|template)$'])
problems = []
for f in input_api.AffectedSourceFiles(source_filter):
local_path = f.LocalPath()
for line_number, line in f.ChangedContents():
if (api_file_pattern.search(local_path)):
if (api_package_pattern.search(line)):
problems.append(
'%s:%d\n %s' % (local_path, line_number, line.strip()))
elif (impl_file_pattern.search(local_path)):
if (impl_package_pattern.search(line)):
problems.append(
'%s:%d\n %s' % (local_path, line_number, line.strip()))
if problems:
return [output_api.PresubmitError(
'API classes must be in org.chromium.net package, and implementation\n'
'classes must not be in org.chromium.net package.',
problems)]
return []
def _RunToolsUnittests(input_api, output_api):
return input_api.canned_checks.RunUnitTestsInDirectory(
input_api, output_api,
'.',
[ r'^tools_unittest\.py$'],
run_on_python3=USE_PYTHON3,
skip_shebang_check = True)
def _ChangeAffectsCronetTools(change):
""" Returns |true| if the change may affect Cronet tools. """
for path in change.LocalPaths():
if path.startswith(os.path.join('components', 'cronet', 'tools')):
return True
return False
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_PyLintChecks(input_api, output_api))
results.extend(_PackageChecks(input_api, output_api))
if _ChangeAffectsCronetTools(input_api.change):
results.extend(_RunToolsUnittests(input_api, output_api))
return results
def CheckChangeOnCommit(input_api, output_api):
return _RunToolsUnittests(input_api, output_api)

View File

@ -1,176 +0,0 @@
# Quick Start Guide to Using Cronet
Cronet is the networking stack of Chromium put into a library for use on
mobile. This is the same networking stack that is used in the Chrome browser
by over a billion people. It offers an easy-to-use, high performance,
standards-compliant, and secure way to perform HTTP requests. Cronet has support
for both Android and iOS. On Android, Cronet offers its own Java asynchronous
API as well as support for the [java.net.HttpURLConnection] API.
This document gives a brief introduction to using these two Java APIs.
For instructions on checking out and building Cronet see
[Cronet build instructions](build_instructions.md).
Testing information is available on the [native
API](native/test_instructions.md) and [Android
API](android/test_instructions.md) pages.
### Basics
First you will need to extend `UrlRequest.Callback` to handle
events during the lifetime of a request. For example:
class MyCallback extends UrlRequest.Callback {
@Override
public void onRedirectReceived(UrlRequest request,
UrlResponseInfo responseInfo, String newLocationUrl) {
if (followRedirect) {
// Let's tell Cronet to follow the redirect!
request.followRedirect();
} else {
// Not worth following the redirect? Abandon the request.
request.cancel();
}
}
@Override
public void onResponseStarted(UrlRequest request,
UrlResponseInfo responseInfo) {
// Now we have response headers!
int httpStatusCode = responseInfo.getHttpStatusCode();
if (httpStatusCode == 200) {
// Success! Let's tell Cronet to read the response body.
request.read(myBuffer);
} else if (httpStatusCode == 503) {
// Do something. Note that 4XX and 5XX are not considered
// errors from Cronet's perspective since the response is
// successfully read.
}
mResponseHeaders = responseInfo.getAllHeaders();
}
@Override
public void onReadCompleted(UrlRequest request,
UrlResponseInfo responseInfo, ByteBuffer byteBuffer) {
// Response body is available.
doSomethingWithResponseData(byteBuffer);
// Let's tell Cronet to continue reading the response body or
// inform us that the response is complete!
request.read(mBuffer);
}
@Override
public void onSucceeded(UrlRequest request,
UrlResponseInfo responseInfo) {
// Request has completed successfully!
}
@Override
public void onFailed(UrlRequest request,
UrlResponseInfo responseInfo, CronetException error) {
// Request has failed. responseInfo might be null.
Log.e("MyCallback", "Request failed. " + error.getMessage());
// Maybe handle error here. Typical errors include hostname
// not resolved, connection to server refused, etc.
}
}
Make a request like this:
CronetEngine.Builder engineBuilder = new CronetEngine.Builder(getContext());
CronetEngine engine = engineBuilder.build();
Executor executor = Executors.newSingleThreadExecutor();
MyCallback callback = new MyCallback();
UrlRequest.Builder requestBuilder = engine.newUrlRequestBuilder(
"https://www.example.com", callback, executor);
UrlRequest request = requestBuilder.build();
request.start();
In the above example, `MyCallback` extends `UrlRequest.Callback`. The request
is started asynchronously. When the response is ready (fully or partially), and
in the event of failures or redirects, `callback`'s methods will be invoked on
`executor`'s thread to inform the client of the request state and/or response
information.
### Downloading Data
When Cronet fetches response headers from the server or gets them from the
cache, `UrlRequest.Callback.onResponseStarted` will be invoked. To read the
response body, the client should call `UrlRequest.read` and supply a
[ByteBuffer] for Cronet to fill. Once a portion or all of
the response body is read, `UrlRequest.Callback.onReadCompleted` will be invoked.
The client may then read and consume the data within `byteBuffer`.
Once the client is ready to consume more data, the client should call
`UrlRequest.read` again. The process continues until
`UrlRequest.Callback.onSucceeded` or `UrlRequest.Callback.onFailed` is invoked,
which signals the completion of the request.
### Uploading Data
MyUploadDataProvider myUploadDataProvider = new MyUploadDataProvider();
requestBuilder.setHttpMethod("POST");
requestBuilder.setUploadDataProvider(myUploadDataProvider, executor);
In the above example, `MyUploadDataProvider` extends `UploadDataProvider`.
When Cronet is ready to send the request body,
`myUploadDataProvider.read(UploadDataSink uploadDataSink,
ByteBuffer byteBuffer)` will be invoked. The client will need to write the
request body into `byteBuffer`. Once the client is done writing into
`byteBuffer`, the client can let Cronet know by calling
`uploadDataSink.onReadSucceeded`. If the request body doesn't fit into
`byteBuffer`, the client can continue writing when `UploadDataProvider.read` is
invoked again. For more details, please see the API reference.
### <a id=configuring-cronet></a> Configuring Cronet
Various configuration options are available via the `CronetEngine.Builder`
object.
Enabling HTTP/2 and QUIC:
- For Example:
engineBuilder.enableHttp2(true).enableQuic(true);
Controlling the cache:
- Use a 100KiB in-memory cache:
engineBuilder.enableHttpCache(
CronetEngine.Builder.HttpCache.IN_MEMORY, 100 * 1024);
- or use a 1MiB disk cache:
engineBuilder.setStoragePath(storagePathString);
engineBuilder.enableHttpCache(CronetEngine.Builder.HttpCache.DISK,
1024 * 1024);
### Debugging
To get more information about how Cronet is processing network
requests, you can start and stop **NetLog** logging by calling
`CronetEngine.startNetLogToFile` and `CronetEngine.stopNetLog`.
Bear in mind that logs may contain sensitive data. You may analyze the
generated log by navigating to [chrome://net-internals#import] using a
Chrome browser.
# Using the java.net.HttpURLConnection API
Cronet offers an implementation of the [java.net.HttpURLConnection] API to make
it easier for apps which rely on this API to use Cronet.
To open individual connections using Cronet's implementation, do the following:
HttpURLConnection connection =
(HttpURLConnection)engine.openConnection(url);
To use Cronet's implementation instead of the system's default implementation
for all connections established using `URL.openConnection()` do the following:
URL.setURLStreamHandlerFactory(engine.createURLStreamHandlerFactory());
Cronet's
HttpURLConnection implementation has some limitations as compared to the system
implementation, including not utilizing the default system HTTP cache (Please
see {@link org.chromium.net.CronetEngine#createURLStreamHandlerFactory} for
more information).
You can configure Cronet and control caching through the
`CronetEngine.Builder` instance, `engineBuilder`
(See [Configuring Cronet](#configuring-cronet) section), before you build the
`CronetEngine` and then call `CronetEngine.createURLStreamHandlerFactory()`.
[ByteBuffer]: https://developer.android.com/reference/java/nio/ByteBuffer.html
[chrome://net-internals#import]: chrome://net-internals#import
[java.net.HttpURLConnection]: https://developer.android.com/reference/java/net/HttpURLConnection.html

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
include_rules = [
"+components/metrics",
"+crypto",
"+jni",
]

View File

@ -1 +0,0 @@
per-file lint-*.xml=*

View File

@ -1,13 +0,0 @@
# Copyright 2017 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.
# Linker script that exports only symbols required for JNI and Cronet Native
# API to work.
{
global:
JNI_OnLoad;
Cronet_*;
local:
*;
};

View File

@ -1,401 +0,0 @@
DO NOT EDIT THIS FILE, USE update_api.py TO UPDATE IT
public class org.chromium.net.ApiVersion {
public static java.lang.String getCronetVersionWithLastChange();
public static int getMaximumAvailableApiLevel();
public static int getApiLevel();
public static java.lang.String getCronetVersion();
public static java.lang.String getLastChange();
}
public abstract class org.chromium.net.BidirectionalStream$Builder {
public static final int STREAM_PRIORITY_IDLE;
public static final int STREAM_PRIORITY_LOWEST;
public static final int STREAM_PRIORITY_LOW;
public static final int STREAM_PRIORITY_MEDIUM;
public static final int STREAM_PRIORITY_HIGHEST;
public org.chromium.net.BidirectionalStream$Builder();
public abstract org.chromium.net.BidirectionalStream$Builder setHttpMethod(java.lang.String);
public abstract org.chromium.net.BidirectionalStream$Builder addHeader(java.lang.String, java.lang.String);
public abstract org.chromium.net.BidirectionalStream$Builder setPriority(int);
public abstract org.chromium.net.BidirectionalStream$Builder delayRequestHeadersUntilFirstFlush(boolean);
public abstract org.chromium.net.BidirectionalStream build();
}
public abstract class org.chromium.net.BidirectionalStream$Callback {
public org.chromium.net.BidirectionalStream$Callback();
public abstract void onStreamReady(org.chromium.net.BidirectionalStream);
public abstract void onResponseHeadersReceived(org.chromium.net.BidirectionalStream, org.chromium.net.UrlResponseInfo);
public abstract void onReadCompleted(org.chromium.net.BidirectionalStream, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer, boolean);
public abstract void onWriteCompleted(org.chromium.net.BidirectionalStream, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer, boolean);
public void onResponseTrailersReceived(org.chromium.net.BidirectionalStream, org.chromium.net.UrlResponseInfo, org.chromium.net.UrlResponseInfo$HeaderBlock);
public abstract void onSucceeded(org.chromium.net.BidirectionalStream, org.chromium.net.UrlResponseInfo);
public abstract void onFailed(org.chromium.net.BidirectionalStream, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException);
public void onCanceled(org.chromium.net.BidirectionalStream, org.chromium.net.UrlResponseInfo);
}
public abstract class org.chromium.net.BidirectionalStream {
public org.chromium.net.BidirectionalStream();
public abstract void start();
public abstract void read(java.nio.ByteBuffer);
public abstract void write(java.nio.ByteBuffer, boolean);
public abstract void flush();
public abstract void cancel();
public abstract boolean isDone();
}
public abstract class org.chromium.net.CallbackException extends org.chromium.net.CronetException {
protected org.chromium.net.CallbackException(java.lang.String, java.lang.Throwable);
}
public abstract class org.chromium.net.CronetEngine$Builder$LibraryLoader {
public org.chromium.net.CronetEngine$Builder$LibraryLoader();
public abstract void loadLibrary(java.lang.String);
}
public class org.chromium.net.CronetEngine$Builder {
protected final org.chromium.net.ICronetEngineBuilder mBuilderDelegate;
public static final int HTTP_CACHE_DISABLED;
public static final int HTTP_CACHE_IN_MEMORY;
public static final int HTTP_CACHE_DISK_NO_HTTP;
public static final int HTTP_CACHE_DISK;
public org.chromium.net.CronetEngine$Builder(android.content.Context);
public org.chromium.net.CronetEngine$Builder(org.chromium.net.ICronetEngineBuilder);
public java.lang.String getDefaultUserAgent();
public org.chromium.net.CronetEngine$Builder setUserAgent(java.lang.String);
public org.chromium.net.CronetEngine$Builder setStoragePath(java.lang.String);
public org.chromium.net.CronetEngine$Builder setLibraryLoader(org.chromium.net.CronetEngine$Builder$LibraryLoader);
public org.chromium.net.CronetEngine$Builder enableQuic(boolean);
public org.chromium.net.CronetEngine$Builder enableHttp2(boolean);
public org.chromium.net.CronetEngine$Builder enableSdch(boolean);
public org.chromium.net.CronetEngine$Builder enableBrotli(boolean);
public org.chromium.net.CronetEngine$Builder enableHttpCache(int, long);
public org.chromium.net.CronetEngine$Builder addQuicHint(java.lang.String, int, int);
public org.chromium.net.CronetEngine$Builder addPublicKeyPins(java.lang.String, java.util.Set<byte[]>, boolean, java.util.Date);
public org.chromium.net.CronetEngine$Builder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean);
public org.chromium.net.CronetEngine build();
}
public abstract class org.chromium.net.CronetEngine {
public org.chromium.net.CronetEngine();
public abstract java.lang.String getVersionString();
public abstract void shutdown();
public abstract void startNetLogToFile(java.lang.String, boolean);
public abstract void stopNetLog();
public abstract byte[] getGlobalMetricsDeltas();
public abstract java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
public abstract java.net.URLStreamHandlerFactory createURLStreamHandlerFactory();
public abstract org.chromium.net.UrlRequest$Builder newUrlRequestBuilder(java.lang.String, org.chromium.net.UrlRequest$Callback, java.util.concurrent.Executor);
}
public abstract class org.chromium.net.CronetException extends java.io.IOException {
protected org.chromium.net.CronetException(java.lang.String, java.lang.Throwable);
}
public abstract class org.chromium.net.CronetProvider {
public static final java.lang.String PROVIDER_NAME_APP_PACKAGED;
public static final java.lang.String PROVIDER_NAME_FALLBACK;
protected final android.content.Context mContext;
protected org.chromium.net.CronetProvider(android.content.Context);
public abstract org.chromium.net.CronetEngine$Builder createBuilder();
public abstract java.lang.String getName();
public abstract java.lang.String getVersion();
public abstract boolean isEnabled();
public java.lang.String toString();
public static java.util.List<org.chromium.net.CronetProvider> getAllProviders(android.content.Context);
}
public abstract class org.chromium.net.ExperimentalBidirectionalStream$Builder extends org.chromium.net.BidirectionalStream$Builder {
public org.chromium.net.ExperimentalBidirectionalStream$Builder();
public org.chromium.net.ExperimentalBidirectionalStream$Builder addRequestAnnotation(java.lang.Object);
public org.chromium.net.ExperimentalBidirectionalStream$Builder setTrafficStatsTag(int);
public org.chromium.net.ExperimentalBidirectionalStream$Builder setTrafficStatsUid(int);
public abstract org.chromium.net.ExperimentalBidirectionalStream$Builder setHttpMethod(java.lang.String);
public abstract org.chromium.net.ExperimentalBidirectionalStream$Builder addHeader(java.lang.String, java.lang.String);
public abstract org.chromium.net.ExperimentalBidirectionalStream$Builder setPriority(int);
public abstract org.chromium.net.ExperimentalBidirectionalStream$Builder delayRequestHeadersUntilFirstFlush(boolean);
public abstract org.chromium.net.ExperimentalBidirectionalStream build();
public org.chromium.net.BidirectionalStream build();
public org.chromium.net.BidirectionalStream$Builder delayRequestHeadersUntilFirstFlush(boolean);
public org.chromium.net.BidirectionalStream$Builder setPriority(int);
public org.chromium.net.BidirectionalStream$Builder addHeader(java.lang.String, java.lang.String);
public org.chromium.net.BidirectionalStream$Builder setHttpMethod(java.lang.String);
}
public abstract class org.chromium.net.ExperimentalBidirectionalStream extends org.chromium.net.BidirectionalStream {
public org.chromium.net.ExperimentalBidirectionalStream();
}
public class org.chromium.net.ExperimentalCronetEngine$Builder extends org.chromium.net.CronetEngine$Builder {
public org.chromium.net.ExperimentalCronetEngine$Builder(android.content.Context);
public org.chromium.net.ExperimentalCronetEngine$Builder(org.chromium.net.ICronetEngineBuilder);
public org.chromium.net.ExperimentalCronetEngine$Builder enableNetworkQualityEstimator(boolean);
public org.chromium.net.ExperimentalCronetEngine$Builder setExperimentalOptions(java.lang.String);
public org.chromium.net.ExperimentalCronetEngine$Builder setThreadPriority(int);
public org.chromium.net.ICronetEngineBuilder getBuilderDelegate();
public org.chromium.net.ExperimentalCronetEngine$Builder setUserAgent(java.lang.String);
public org.chromium.net.ExperimentalCronetEngine$Builder setStoragePath(java.lang.String);
public org.chromium.net.ExperimentalCronetEngine$Builder setLibraryLoader(org.chromium.net.CronetEngine$Builder$LibraryLoader);
public org.chromium.net.ExperimentalCronetEngine$Builder enableQuic(boolean);
public org.chromium.net.ExperimentalCronetEngine$Builder enableHttp2(boolean);
public org.chromium.net.ExperimentalCronetEngine$Builder enableSdch(boolean);
public org.chromium.net.ExperimentalCronetEngine$Builder enableHttpCache(int, long);
public org.chromium.net.ExperimentalCronetEngine$Builder addQuicHint(java.lang.String, int, int);
public org.chromium.net.ExperimentalCronetEngine$Builder addPublicKeyPins(java.lang.String, java.util.Set<byte[]>, boolean, java.util.Date);
public org.chromium.net.ExperimentalCronetEngine$Builder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean);
public org.chromium.net.ExperimentalCronetEngine build();
public org.chromium.net.CronetEngine build();
public org.chromium.net.CronetEngine$Builder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean);
public org.chromium.net.CronetEngine$Builder addPublicKeyPins(java.lang.String, java.util.Set, boolean, java.util.Date);
public org.chromium.net.CronetEngine$Builder addQuicHint(java.lang.String, int, int);
public org.chromium.net.CronetEngine$Builder enableHttpCache(int, long);
public org.chromium.net.CronetEngine$Builder enableSdch(boolean);
public org.chromium.net.CronetEngine$Builder enableHttp2(boolean);
public org.chromium.net.CronetEngine$Builder enableQuic(boolean);
public org.chromium.net.CronetEngine$Builder setLibraryLoader(org.chromium.net.CronetEngine$Builder$LibraryLoader);
public org.chromium.net.CronetEngine$Builder setStoragePath(java.lang.String);
public org.chromium.net.CronetEngine$Builder setUserAgent(java.lang.String);
}
public abstract class org.chromium.net.ExperimentalCronetEngine extends org.chromium.net.CronetEngine {
public static final int CONNECTION_METRIC_UNKNOWN;
public static final int EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
public static final int EFFECTIVE_CONNECTION_TYPE_OFFLINE;
public static final int EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
public static final int EFFECTIVE_CONNECTION_TYPE_2G;
public static final int EFFECTIVE_CONNECTION_TYPE_3G;
public static final int EFFECTIVE_CONNECTION_TYPE_4G;
public org.chromium.net.ExperimentalCronetEngine();
public abstract org.chromium.net.ExperimentalBidirectionalStream$Builder newBidirectionalStreamBuilder(java.lang.String, org.chromium.net.BidirectionalStream$Callback, java.util.concurrent.Executor);
public abstract org.chromium.net.ExperimentalUrlRequest$Builder newUrlRequestBuilder(java.lang.String, org.chromium.net.UrlRequest$Callback, java.util.concurrent.Executor);
public void startNetLogToDisk(java.lang.String, boolean, int);
public int getEffectiveConnectionType();
public void configureNetworkQualityEstimatorForTesting(boolean, boolean, boolean);
public void addRttListener(org.chromium.net.NetworkQualityRttListener);
public void removeRttListener(org.chromium.net.NetworkQualityRttListener);
public void addThroughputListener(org.chromium.net.NetworkQualityThroughputListener);
public void removeThroughputListener(org.chromium.net.NetworkQualityThroughputListener);
public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
public void addRequestFinishedListener(org.chromium.net.RequestFinishedInfo$Listener);
public void removeRequestFinishedListener(org.chromium.net.RequestFinishedInfo$Listener);
public int getHttpRttMs();
public int getTransportRttMs();
public int getDownstreamThroughputKbps();
public org.chromium.net.UrlRequest$Builder newUrlRequestBuilder(java.lang.String, org.chromium.net.UrlRequest$Callback, java.util.concurrent.Executor);
}
public abstract class org.chromium.net.ExperimentalUrlRequest$Builder extends org.chromium.net.UrlRequest$Builder {
public static final int DEFAULT_IDEMPOTENCY;
public static final int IDEMPOTENT;
public static final int NOT_IDEMPOTENT;
public org.chromium.net.ExperimentalUrlRequest$Builder();
public org.chromium.net.ExperimentalUrlRequest$Builder disableConnectionMigration();
public org.chromium.net.ExperimentalUrlRequest$Builder addRequestAnnotation(java.lang.Object);
public org.chromium.net.ExperimentalUrlRequest$Builder setTrafficStatsTag(int);
public org.chromium.net.ExperimentalUrlRequest$Builder setTrafficStatsUid(int);
public org.chromium.net.ExperimentalUrlRequest$Builder setRequestFinishedListener(org.chromium.net.RequestFinishedInfo$Listener);
public org.chromium.net.ExperimentalUrlRequest$Builder setIdempotency(int);
public abstract org.chromium.net.ExperimentalUrlRequest$Builder setHttpMethod(java.lang.String);
public abstract org.chromium.net.ExperimentalUrlRequest$Builder addHeader(java.lang.String, java.lang.String);
public abstract org.chromium.net.ExperimentalUrlRequest$Builder disableCache();
public abstract org.chromium.net.ExperimentalUrlRequest$Builder setPriority(int);
public abstract org.chromium.net.ExperimentalUrlRequest$Builder setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor);
public abstract org.chromium.net.ExperimentalUrlRequest$Builder allowDirectExecutor();
public abstract org.chromium.net.ExperimentalUrlRequest build();
public org.chromium.net.UrlRequest build();
public org.chromium.net.UrlRequest$Builder allowDirectExecutor();
public org.chromium.net.UrlRequest$Builder setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor);
public org.chromium.net.UrlRequest$Builder setPriority(int);
public org.chromium.net.UrlRequest$Builder disableCache();
public org.chromium.net.UrlRequest$Builder addHeader(java.lang.String, java.lang.String);
public org.chromium.net.UrlRequest$Builder setHttpMethod(java.lang.String);
}
public abstract class org.chromium.net.ExperimentalUrlRequest extends org.chromium.net.UrlRequest {
public org.chromium.net.ExperimentalUrlRequest();
}
public abstract class org.chromium.net.ICronetEngineBuilder {
public org.chromium.net.ICronetEngineBuilder();
public abstract org.chromium.net.ICronetEngineBuilder addPublicKeyPins(java.lang.String, java.util.Set<byte[]>, boolean, java.util.Date);
public abstract org.chromium.net.ICronetEngineBuilder addQuicHint(java.lang.String, int, int);
public abstract org.chromium.net.ICronetEngineBuilder enableHttp2(boolean);
public abstract org.chromium.net.ICronetEngineBuilder enableHttpCache(int, long);
public abstract org.chromium.net.ICronetEngineBuilder enablePublicKeyPinningBypassForLocalTrustAnchors(boolean);
public abstract org.chromium.net.ICronetEngineBuilder enableQuic(boolean);
public abstract org.chromium.net.ICronetEngineBuilder enableSdch(boolean);
public org.chromium.net.ICronetEngineBuilder enableBrotli(boolean);
public abstract org.chromium.net.ICronetEngineBuilder setExperimentalOptions(java.lang.String);
public abstract org.chromium.net.ICronetEngineBuilder setLibraryLoader(org.chromium.net.CronetEngine$Builder$LibraryLoader);
public abstract org.chromium.net.ICronetEngineBuilder setStoragePath(java.lang.String);
public abstract org.chromium.net.ICronetEngineBuilder setUserAgent(java.lang.String);
public abstract java.lang.String getDefaultUserAgent();
public abstract org.chromium.net.ExperimentalCronetEngine build();
public org.chromium.net.ICronetEngineBuilder enableNetworkQualityEstimator(boolean);
public org.chromium.net.ICronetEngineBuilder setThreadPriority(int);
}
public final class org.chromium.net.InlineExecutionProhibitedException extends java.util.concurrent.RejectedExecutionException {
public org.chromium.net.InlineExecutionProhibitedException();
}
public abstract class org.chromium.net.NetworkException extends org.chromium.net.CronetException {
public static final int ERROR_HOSTNAME_NOT_RESOLVED;
public static final int ERROR_INTERNET_DISCONNECTED;
public static final int ERROR_NETWORK_CHANGED;
public static final int ERROR_TIMED_OUT;
public static final int ERROR_CONNECTION_CLOSED;
public static final int ERROR_CONNECTION_TIMED_OUT;
public static final int ERROR_CONNECTION_REFUSED;
public static final int ERROR_CONNECTION_RESET;
public static final int ERROR_ADDRESS_UNREACHABLE;
public static final int ERROR_QUIC_PROTOCOL_FAILED;
public static final int ERROR_OTHER;
protected org.chromium.net.NetworkException(java.lang.String, java.lang.Throwable);
public abstract int getErrorCode();
public abstract int getCronetInternalErrorCode();
public abstract boolean immediatelyRetryable();
}
public abstract class org.chromium.net.NetworkQualityRttListener {
public org.chromium.net.NetworkQualityRttListener(java.util.concurrent.Executor);
public java.util.concurrent.Executor getExecutor();
public abstract void onRttObservation(int, long, int);
}
public abstract class org.chromium.net.NetworkQualityThroughputListener {
public org.chromium.net.NetworkQualityThroughputListener(java.util.concurrent.Executor);
public java.util.concurrent.Executor getExecutor();
public abstract void onThroughputObservation(int, long, int);
}
public abstract class org.chromium.net.QuicException extends org.chromium.net.NetworkException {
protected org.chromium.net.QuicException(java.lang.String, java.lang.Throwable);
public abstract int getQuicDetailedErrorCode();
}
public abstract class org.chromium.net.RequestFinishedInfo$Listener {
public org.chromium.net.RequestFinishedInfo$Listener(java.util.concurrent.Executor);
public abstract void onRequestFinished(org.chromium.net.RequestFinishedInfo);
public java.util.concurrent.Executor getExecutor();
}
public abstract class org.chromium.net.RequestFinishedInfo$Metrics {
public org.chromium.net.RequestFinishedInfo$Metrics();
public abstract java.util.Date getRequestStart();
public abstract java.util.Date getDnsStart();
public abstract java.util.Date getDnsEnd();
public abstract java.util.Date getConnectStart();
public abstract java.util.Date getConnectEnd();
public abstract java.util.Date getSslStart();
public abstract java.util.Date getSslEnd();
public abstract java.util.Date getSendingStart();
public abstract java.util.Date getSendingEnd();
public abstract java.util.Date getPushStart();
public abstract java.util.Date getPushEnd();
public abstract java.util.Date getResponseStart();
public abstract java.util.Date getRequestEnd();
public abstract boolean getSocketReused();
public abstract java.lang.Long getTtfbMs();
public abstract java.lang.Long getTotalTimeMs();
public abstract java.lang.Long getSentByteCount();
public abstract java.lang.Long getReceivedByteCount();
}
public abstract class org.chromium.net.RequestFinishedInfo {
public static final int SUCCEEDED;
public static final int FAILED;
public static final int CANCELED;
public org.chromium.net.RequestFinishedInfo();
public abstract java.lang.String getUrl();
public abstract java.util.Collection<java.lang.Object> getAnnotations();
public abstract org.chromium.net.RequestFinishedInfo$Metrics getMetrics();
public abstract int getFinishedReason();
public abstract org.chromium.net.UrlResponseInfo getResponseInfo();
public abstract org.chromium.net.CronetException getException();
}
public abstract class org.chromium.net.UploadDataProvider implements java.io.Closeable {
public org.chromium.net.UploadDataProvider();
public abstract long getLength() throws java.io.IOException;
public abstract void read(org.chromium.net.UploadDataSink, java.nio.ByteBuffer) throws java.io.IOException;
public abstract void rewind(org.chromium.net.UploadDataSink) throws java.io.IOException;
public void close() throws java.io.IOException;
}
final class org.chromium.net.UploadDataProviders$ByteBufferUploadProvider extends org.chromium.net.UploadDataProvider {
public long getLength();
public void read(org.chromium.net.UploadDataSink, java.nio.ByteBuffer);
public void rewind(org.chromium.net.UploadDataSink);
}
interface org.chromium.net.UploadDataProviders$FileChannelProvider {
public abstract java.nio.channels.FileChannel getChannel() throws java.io.IOException;
}
final class org.chromium.net.UploadDataProviders$FileUploadProvider extends org.chromium.net.UploadDataProvider {
public long getLength() throws java.io.IOException;
public void read(org.chromium.net.UploadDataSink, java.nio.ByteBuffer) throws java.io.IOException;
public void rewind(org.chromium.net.UploadDataSink) throws java.io.IOException;
public void close() throws java.io.IOException;
}
public final class org.chromium.net.UploadDataProviders {
public static org.chromium.net.UploadDataProvider create(java.io.File);
public static org.chromium.net.UploadDataProvider create(android.os.ParcelFileDescriptor);
public static org.chromium.net.UploadDataProvider create(java.nio.ByteBuffer);
public static org.chromium.net.UploadDataProvider create(byte[], int, int);
public static org.chromium.net.UploadDataProvider create(byte[]);
}
public abstract class org.chromium.net.UploadDataSink {
public org.chromium.net.UploadDataSink();
public abstract void onReadSucceeded(boolean);
public abstract void onReadError(java.lang.Exception);
public abstract void onRewindSucceeded();
public abstract void onRewindError(java.lang.Exception);
}
public abstract class org.chromium.net.UrlRequest$Builder {
public static final int REQUEST_PRIORITY_IDLE;
public static final int REQUEST_PRIORITY_LOWEST;
public static final int REQUEST_PRIORITY_LOW;
public static final int REQUEST_PRIORITY_MEDIUM;
public static final int REQUEST_PRIORITY_HIGHEST;
public org.chromium.net.UrlRequest$Builder();
public abstract org.chromium.net.UrlRequest$Builder setHttpMethod(java.lang.String);
public abstract org.chromium.net.UrlRequest$Builder addHeader(java.lang.String, java.lang.String);
public abstract org.chromium.net.UrlRequest$Builder disableCache();
public abstract org.chromium.net.UrlRequest$Builder setPriority(int);
public abstract org.chromium.net.UrlRequest$Builder setUploadDataProvider(org.chromium.net.UploadDataProvider, java.util.concurrent.Executor);
public abstract org.chromium.net.UrlRequest$Builder allowDirectExecutor();
public abstract org.chromium.net.UrlRequest build();
}
public abstract class org.chromium.net.UrlRequest$Callback {
public org.chromium.net.UrlRequest$Callback();
public abstract void onRedirectReceived(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.lang.String) throws java.lang.Exception;
public abstract void onResponseStarted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo) throws java.lang.Exception;
public abstract void onReadCompleted(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, java.nio.ByteBuffer) throws java.lang.Exception;
public abstract void onSucceeded(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo);
public abstract void onFailed(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo, org.chromium.net.CronetException);
public void onCanceled(org.chromium.net.UrlRequest, org.chromium.net.UrlResponseInfo);
}
public class org.chromium.net.UrlRequest$Status {
public static final int INVALID;
public static final int IDLE;
public static final int WAITING_FOR_STALLED_SOCKET_POOL;
public static final int WAITING_FOR_AVAILABLE_SOCKET;
public static final int WAITING_FOR_DELEGATE;
public static final int WAITING_FOR_CACHE;
public static final int DOWNLOADING_PAC_FILE;
public static final int RESOLVING_PROXY_FOR_URL;
public static final int RESOLVING_HOST_IN_PAC_FILE;
public static final int ESTABLISHING_PROXY_TUNNEL;
public static final int RESOLVING_HOST;
public static final int CONNECTING;
public static final int SSL_HANDSHAKE;
public static final int SENDING_REQUEST;
public static final int WAITING_FOR_RESPONSE;
public static final int READING_RESPONSE;
}
public abstract class org.chromium.net.UrlRequest$StatusListener {
public org.chromium.net.UrlRequest$StatusListener();
public abstract void onStatus(int);
}
public abstract class org.chromium.net.UrlRequest {
public org.chromium.net.UrlRequest();
public abstract void start();
public abstract void followRedirect();
public abstract void read(java.nio.ByteBuffer);
public abstract void cancel();
public abstract boolean isDone();
public abstract void getStatus(org.chromium.net.UrlRequest$StatusListener);
}
public abstract class org.chromium.net.UrlResponseInfo$HeaderBlock {
public org.chromium.net.UrlResponseInfo$HeaderBlock();
public abstract java.util.List<java.util.Map$Entry<java.lang.String, java.lang.String>> getAsList();
public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> getAsMap();
}
public abstract class org.chromium.net.UrlResponseInfo {
public org.chromium.net.UrlResponseInfo();
public abstract java.lang.String getUrl();
public abstract java.util.List<java.lang.String> getUrlChain();
public abstract int getHttpStatusCode();
public abstract java.lang.String getHttpStatusText();
public abstract java.util.List<java.util.Map$Entry<java.lang.String, java.lang.String>> getAllHeadersAsList();
public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> getAllHeaders();
public abstract boolean wasCached();
public abstract java.lang.String getNegotiatedProtocol();
public abstract java.lang.String getProxyServer();
public abstract long getReceivedByteCount();
}
Stamp: 1c548f381e3715d983e967e830d675d8

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@string/CronetProviderClassName" />

View File

@ -1,518 +0,0 @@
// Copyright 2015 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 "cronet_bidirectional_stream_adapter.h"
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/strings/abseil_string_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "components/cronet/android/cronet_context_adapter.h"
#include "components/cronet/android/cronet_jni_headers/CronetBidirectionalStream_jni.h"
#include "components/cronet/android/io_buffer_with_byte_buffer.h"
#include "components/cronet/android/url_request_error.h"
#include "components/cronet/metrics_util.h"
#include "net/base/http_user_agent_settings.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/http/bidirectional_stream_request_info.h"
#include "net/http/http_network_session.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/http/http_transaction_factory.h"
#include "net/http/http_util.h"
#include "net/ssl/ssl_info.h"
#include "net/third_party/quiche/src/quic/core/quic_packets.h"
#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
#include "net/url_request/url_request_context.h"
#include "url/gurl.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::ConvertJavaStringToUTF8;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace cronet {
namespace {
// As |GetArrayLength| makes no guarantees about the returned value (e.g., it
// may be -1 if |array| is not a valid Java array), provide a safe wrapper
// that always returns a valid, non-negative size.
template <typename JavaArrayType>
size_t SafeGetArrayLength(JNIEnv* env, JavaArrayType jarray) {
DCHECK(jarray);
jsize length = env->GetArrayLength(jarray);
DCHECK_GE(length, 0) << "Invalid array length: " << length;
return static_cast<size_t>(std::max(0, length));
}
} // namespace
PendingWriteData::PendingWriteData(
JNIEnv* env,
const JavaRef<jobjectArray>& jwrite_buffer_list,
const JavaRef<jintArray>& jwrite_buffer_pos_list,
const JavaRef<jintArray>& jwrite_buffer_limit_list,
jboolean jwrite_end_of_stream) {
this->jwrite_buffer_list.Reset(jwrite_buffer_list);
this->jwrite_buffer_pos_list.Reset(jwrite_buffer_pos_list);
this->jwrite_buffer_limit_list.Reset(jwrite_buffer_limit_list);
this->jwrite_end_of_stream = jwrite_end_of_stream;
}
PendingWriteData::~PendingWriteData() {
// Reset global references.
jwrite_buffer_list.Reset();
jwrite_buffer_pos_list.Reset();
jwrite_buffer_limit_list.Reset();
}
static jlong JNI_CronetBidirectionalStream_CreateBidirectionalStream(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jbidi_stream,
jlong jurl_request_context_adapter,
jboolean jsend_request_headers_automatically,
jboolean jenable_metrics,
jboolean jtraffic_stats_tag_set,
jint jtraffic_stats_tag,
jboolean jtraffic_stats_uid_set,
jint jtraffic_stats_uid,
jlong jnetwork_handle) {
CronetContextAdapter* context_adapter =
reinterpret_cast<CronetContextAdapter*>(jurl_request_context_adapter);
DCHECK(context_adapter);
CronetBidirectionalStreamAdapter* adapter =
new CronetBidirectionalStreamAdapter(
context_adapter, env, jbidi_stream,
jsend_request_headers_automatically, jenable_metrics,
jtraffic_stats_tag_set, jtraffic_stats_tag, jtraffic_stats_uid_set,
jtraffic_stats_uid, jnetwork_handle);
return reinterpret_cast<jlong>(adapter);
}
CronetBidirectionalStreamAdapter::CronetBidirectionalStreamAdapter(
CronetContextAdapter* context,
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jbidi_stream,
bool send_request_headers_automatically,
bool enable_metrics,
bool traffic_stats_tag_set,
int32_t traffic_stats_tag,
bool traffic_stats_uid_set,
int32_t traffic_stats_uid,
net::NetworkChangeNotifier::NetworkHandle network)
: context_(context),
owner_(env, jbidi_stream),
send_request_headers_automatically_(send_request_headers_automatically),
enable_metrics_(enable_metrics),
traffic_stats_tag_set_(traffic_stats_tag_set),
traffic_stats_tag_(traffic_stats_tag),
traffic_stats_uid_set_(traffic_stats_uid_set),
traffic_stats_uid_(traffic_stats_uid),
network_(network),
stream_failed_(false) {}
CronetBidirectionalStreamAdapter::~CronetBidirectionalStreamAdapter() {
DCHECK(context_->IsOnNetworkThread());
}
void CronetBidirectionalStreamAdapter::SendRequestHeaders(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
context_->PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(
&CronetBidirectionalStreamAdapter::SendRequestHeadersOnNetworkThread,
base::Unretained(this)));
}
jint CronetBidirectionalStreamAdapter::Start(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jstring>& jurl,
jint jpriority,
const base::android::JavaParamRef<jstring>& jmethod,
const base::android::JavaParamRef<jobjectArray>& jheaders,
jboolean jend_of_stream) {
// Prepare request info here to be able to return the error.
std::unique_ptr<net::BidirectionalStreamRequestInfo> request_info(
new net::BidirectionalStreamRequestInfo());
request_info->url = GURL(ConvertJavaStringToUTF8(env, jurl));
request_info->priority = static_cast<net::RequestPriority>(jpriority);
// Http method is a token, just as header name.
request_info->method = ConvertJavaStringToUTF8(env, jmethod);
if (!net::HttpUtil::IsValidHeaderName(request_info->method))
return -1;
std::vector<std::string> headers;
base::android::AppendJavaStringArrayToStringVector(env, jheaders, &headers);
for (size_t i = 0; i < headers.size(); i += 2) {
std::string name(headers[i]);
std::string value(headers[i + 1]);
if (!net::HttpUtil::IsValidHeaderName(name) ||
!net::HttpUtil::IsValidHeaderValue(value)) {
return i + 1;
}
request_info->extra_headers.SetHeader(name, value);
}
request_info->end_stream_on_headers = jend_of_stream;
if (traffic_stats_tag_set_ || traffic_stats_uid_set_) {
request_info->socket_tag = net::SocketTag(
traffic_stats_uid_set_ ? traffic_stats_uid_ : net::SocketTag::UNSET_UID,
traffic_stats_tag_set_ ? traffic_stats_tag_
: net::SocketTag::UNSET_TAG);
}
context_->PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetBidirectionalStreamAdapter::StartOnNetworkThread,
base::Unretained(this), std::move(request_info)));
return 0;
}
jboolean CronetBidirectionalStreamAdapter::ReadData(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& jbyte_buffer,
jint jposition,
jint jlimit) {
DCHECK_LT(jposition, jlimit);
void* data = env->GetDirectBufferAddress(jbyte_buffer);
if (!data)
return JNI_FALSE;
scoped_refptr<IOBufferWithByteBuffer> read_buffer(
new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition, jlimit));
int remaining_capacity = jlimit - jposition;
context_->PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetBidirectionalStreamAdapter::ReadDataOnNetworkThread,
base::Unretained(this), read_buffer, remaining_capacity));
return JNI_TRUE;
}
jboolean CronetBidirectionalStreamAdapter::WritevData(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobjectArray>& jbyte_buffers,
const base::android::JavaParamRef<jintArray>& jbyte_buffers_pos,
const base::android::JavaParamRef<jintArray>& jbyte_buffers_limit,
jboolean jend_of_stream) {
size_t buffers_array_size = SafeGetArrayLength(env, jbyte_buffers.obj());
size_t pos_array_size = SafeGetArrayLength(env, jbyte_buffers.obj());
size_t limit_array_size = SafeGetArrayLength(env, jbyte_buffers.obj());
if (buffers_array_size != pos_array_size ||
buffers_array_size != limit_array_size) {
DLOG(ERROR) << "Illegal arguments.";
return JNI_FALSE;
}
std::unique_ptr<PendingWriteData> pending_write_data;
pending_write_data.reset(
new PendingWriteData(env, jbyte_buffers, jbyte_buffers_pos,
jbyte_buffers_limit, jend_of_stream));
for (size_t i = 0; i < buffers_array_size; ++i) {
ScopedJavaLocalRef<jobject> jbuffer(
env, env->GetObjectArrayElement(
pending_write_data->jwrite_buffer_list.obj(), i));
void* data = env->GetDirectBufferAddress(jbuffer.obj());
if (!data)
return JNI_FALSE;
jint pos;
env->GetIntArrayRegion(pending_write_data->jwrite_buffer_pos_list.obj(), i,
1, &pos);
jint limit;
env->GetIntArrayRegion(pending_write_data->jwrite_buffer_limit_list.obj(),
i, 1, &limit);
DCHECK_LE(pos, limit);
scoped_refptr<net::WrappedIOBuffer> write_buffer =
base::MakeRefCounted<net::WrappedIOBuffer>(static_cast<char*>(data) +
pos);
pending_write_data->write_buffer_list.push_back(write_buffer);
pending_write_data->write_buffer_len_list.push_back(limit - pos);
}
context_->PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(
&CronetBidirectionalStreamAdapter::WritevDataOnNetworkThread,
base::Unretained(this), std::move(pending_write_data)));
return JNI_TRUE;
}
void CronetBidirectionalStreamAdapter::Destroy(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jboolean jsend_on_canceled) {
// Destroy could be called from any thread, including network thread (if
// posting task to executor throws an exception), but is posted, so |this|
// is valid until calling task is complete. Destroy() is always called from
// within a synchronized java block that guarantees no future posts to the
// network thread with the adapter pointer.
context_->PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetBidirectionalStreamAdapter::DestroyOnNetworkThread,
base::Unretained(this), jsend_on_canceled));
}
void CronetBidirectionalStreamAdapter::OnStreamReady(
bool request_headers_sent) {
DCHECK(context_->IsOnNetworkThread());
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetBidirectionalStream_onStreamReady(
env, owner_, request_headers_sent ? JNI_TRUE : JNI_FALSE);
}
void CronetBidirectionalStreamAdapter::OnHeadersReceived(
const spdy::Http2HeaderBlock& response_headers) {
DCHECK(context_->IsOnNetworkThread());
JNIEnv* env = base::android::AttachCurrentThread();
// Get http status code from response headers.
jint http_status_code = 0;
const auto http_status_header = response_headers.find(":status");
if (http_status_header != response_headers.end())
base::StringToInt(base::StringViewToStringPiece(http_status_header->second),
&http_status_code);
std::string protocol;
switch (bidi_stream_->GetProtocol()) {
case net::kProtoHTTP2:
protocol = "h2";
break;
case net::kProtoQUIC:
protocol = "quic/1+spdy/3";
break;
default:
break;
}
cronet::Java_CronetBidirectionalStream_onResponseHeadersReceived(
env, owner_, http_status_code, ConvertUTF8ToJavaString(env, protocol),
GetHeadersArray(env, response_headers),
bidi_stream_->GetTotalReceivedBytes());
}
void CronetBidirectionalStreamAdapter::OnDataRead(int bytes_read) {
DCHECK(context_->IsOnNetworkThread());
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetBidirectionalStream_onReadCompleted(
env, owner_, read_buffer_->byte_buffer(), bytes_read,
read_buffer_->initial_position(), read_buffer_->initial_limit(),
bidi_stream_->GetTotalReceivedBytes());
// Free the read buffer. This lets the Java ByteBuffer be freed, if the
// embedder releases it, too.
read_buffer_ = nullptr;
}
void CronetBidirectionalStreamAdapter::OnDataSent() {
DCHECK(context_->IsOnNetworkThread());
DCHECK(pending_write_data_);
JNIEnv* env = base::android::AttachCurrentThread();
// Call into Java.
cronet::Java_CronetBidirectionalStream_onWritevCompleted(
env, owner_, pending_write_data_->jwrite_buffer_list,
pending_write_data_->jwrite_buffer_pos_list,
pending_write_data_->jwrite_buffer_limit_list,
pending_write_data_->jwrite_end_of_stream);
// Free the java objects. This lets the Java ByteBuffers be freed, if the
// embedder releases it, too.
pending_write_data_.reset();
}
void CronetBidirectionalStreamAdapter::OnTrailersReceived(
const spdy::Http2HeaderBlock& response_trailers) {
DCHECK(context_->IsOnNetworkThread());
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetBidirectionalStream_onResponseTrailersReceived(
env, owner_, GetHeadersArray(env, response_trailers));
}
void CronetBidirectionalStreamAdapter::OnFailed(int error) {
DCHECK(context_->IsOnNetworkThread());
stream_failed_ = true;
JNIEnv* env = base::android::AttachCurrentThread();
net::NetErrorDetails net_error_details;
bidi_stream_->PopulateNetErrorDetails(&net_error_details);
cronet::Java_CronetBidirectionalStream_onError(
env, owner_, NetErrorToUrlRequestError(error), error,
net_error_details.quic_connection_error,
ConvertUTF8ToJavaString(env, net::ErrorToString(error)),
bidi_stream_->GetTotalReceivedBytes());
}
void CronetBidirectionalStreamAdapter::StartOnNetworkThread(
std::unique_ptr<net::BidirectionalStreamRequestInfo> request_info) {
DCHECK(context_->IsOnNetworkThread());
DCHECK(!bidi_stream_);
request_info->detect_broken_connection =
context_->cronet_url_request_context()
->bidi_stream_detect_broken_connection();
request_info->heartbeat_interval =
context_->cronet_url_request_context()->heartbeat_interval();
request_info->extra_headers.SetHeaderIfMissing(
net::HttpRequestHeaders::kUserAgent,
context_->GetURLRequestContext(network_)
->http_user_agent_settings()
->GetUserAgent());
bidi_stream_.reset(
new net::BidirectionalStream(std::move(request_info),
context_->GetURLRequestContext(network_)
->http_transaction_factory()
->GetSession(),
send_request_headers_automatically_, this));
}
void CronetBidirectionalStreamAdapter::SendRequestHeadersOnNetworkThread() {
DCHECK(context_->IsOnNetworkThread());
DCHECK(!send_request_headers_automatically_);
if (stream_failed_) {
// If stream failed between the time when SendRequestHeaders is invoked and
// SendRequestHeadersOnNetworkThread is executed, do not call into
// |bidi_stream_| since the underlying stream might have been destroyed.
// Do not invoke Java callback either, since onError is posted when
// |stream_failed_| is set to true.
return;
}
bidi_stream_->SendRequestHeaders();
}
void CronetBidirectionalStreamAdapter::ReadDataOnNetworkThread(
scoped_refptr<IOBufferWithByteBuffer> read_buffer,
int buffer_size) {
DCHECK(context_->IsOnNetworkThread());
DCHECK(read_buffer);
DCHECK(!read_buffer_);
read_buffer_ = read_buffer;
int bytes_read = bidi_stream_->ReadData(read_buffer_.get(), buffer_size);
// If IO is pending, wait for the BidirectionalStream to call OnDataRead.
if (bytes_read == net::ERR_IO_PENDING)
return;
if (bytes_read < 0) {
OnFailed(bytes_read);
return;
}
OnDataRead(bytes_read);
}
void CronetBidirectionalStreamAdapter::WritevDataOnNetworkThread(
std::unique_ptr<PendingWriteData> pending_write_data) {
DCHECK(context_->IsOnNetworkThread());
DCHECK(pending_write_data);
DCHECK(!pending_write_data_);
if (stream_failed_) {
// If stream failed between the time when WritevData is invoked and
// WritevDataOnNetworkThread is executed, do not call into |bidi_stream_|
// since the underlying stream might have been destroyed. Do not invoke
// Java callback either, since onError is posted when |stream_failed_| is
// set to true.
return;
}
pending_write_data_ = std::move(pending_write_data);
bool end_of_stream = pending_write_data_->jwrite_end_of_stream == JNI_TRUE;
bidi_stream_->SendvData(pending_write_data_->write_buffer_list,
pending_write_data_->write_buffer_len_list,
end_of_stream);
}
void CronetBidirectionalStreamAdapter::DestroyOnNetworkThread(
bool send_on_canceled) {
DCHECK(context_->IsOnNetworkThread());
if (send_on_canceled) {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetBidirectionalStream_onCanceled(env, owner_);
}
MaybeReportMetrics();
delete this;
}
base::android::ScopedJavaLocalRef<jobjectArray>
CronetBidirectionalStreamAdapter::GetHeadersArray(
JNIEnv* env,
const spdy::Http2HeaderBlock& header_block) {
DCHECK(context_->IsOnNetworkThread());
std::vector<std::string> headers;
for (const auto& header : header_block) {
auto value = std::string(header.second);
size_t start = 0;
size_t end = 0;
// The do loop will split headers by '\0' so that applications can skip it.
do {
end = value.find('\0', start);
std::string split_value;
if (end != value.npos) {
split_value = value.substr(start, end - start);
} else {
split_value = value.substr(start);
}
headers.push_back(std::string(header.first));
headers.push_back(split_value);
start = end + 1;
} while (end != value.npos);
}
return base::android::ToJavaArrayOfStrings(env, headers);
}
void CronetBidirectionalStreamAdapter::MaybeReportMetrics() {
if (!enable_metrics_)
return;
if (!bidi_stream_)
return;
net::LoadTimingInfo load_timing_info;
bidi_stream_->GetLoadTimingInfo(&load_timing_info);
JNIEnv* env = base::android::AttachCurrentThread();
base::Time start_time = load_timing_info.request_start_time;
base::TimeTicks start_ticks = load_timing_info.request_start;
cronet::Java_CronetBidirectionalStream_onMetricsCollected(
env, owner_,
metrics_util::ConvertTime(start_ticks, start_ticks, start_time),
metrics_util::ConvertTime(load_timing_info.connect_timing.dns_start,
start_ticks, start_time),
metrics_util::ConvertTime(load_timing_info.connect_timing.dns_end,
start_ticks, start_time),
metrics_util::ConvertTime(load_timing_info.connect_timing.connect_start,
start_ticks, start_time),
metrics_util::ConvertTime(load_timing_info.connect_timing.connect_end,
start_ticks, start_time),
metrics_util::ConvertTime(load_timing_info.connect_timing.ssl_start,
start_ticks, start_time),
metrics_util::ConvertTime(load_timing_info.connect_timing.ssl_end,
start_ticks, start_time),
metrics_util::ConvertTime(load_timing_info.send_start, start_ticks,
start_time),
metrics_util::ConvertTime(load_timing_info.send_end, start_ticks,
start_time),
metrics_util::ConvertTime(load_timing_info.push_start, start_ticks,
start_time),
metrics_util::ConvertTime(load_timing_info.push_end, start_ticks,
start_time),
metrics_util::ConvertTime(load_timing_info.receive_headers_end,
start_ticks, start_time),
metrics_util::ConvertTime(base::TimeTicks::Now(), start_ticks,
start_time),
load_timing_info.socket_reused, bidi_stream_->GetTotalSentBytes(),
bidi_stream_->GetTotalReceivedBytes());
}
} // namespace cronet

View File

@ -1,201 +0,0 @@
// Copyright 2015 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.
#ifndef COMPONENTS_CRONET_ANDROID_CRONET_BIDIRECTIONAL_STREAM_ADAPTER_H_
#define COMPONENTS_CRONET_ANDROID_CRONET_BIDIRECTIONAL_STREAM_ADAPTER_H_
#include <jni.h>
#include <memory>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "net/http/bidirectional_stream.h"
#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
namespace net {
struct BidirectionalStreamRequestInfo;
} // namespace net
namespace cronet {
class CronetContextAdapter;
class IOBufferWithByteBuffer;
// Convenient wrapper to hold Java references and data to represent the pending
// data to be written.
struct PendingWriteData {
PendingWriteData(
JNIEnv* env,
const base::android::JavaRef<jobjectArray>& jwrite_buffer_list,
const base::android::JavaRef<jintArray>& jwrite_buffer_pos_list,
const base::android::JavaRef<jintArray>& jwrite_buffer_limit_list,
jboolean jwrite_end_of_stream);
PendingWriteData(const PendingWriteData&) = delete;
PendingWriteData& operator=(const PendingWriteData&) = delete;
~PendingWriteData();
// Arguments passed in from Java. Retain a global ref so they won't get GC-ed
// until the corresponding onWriteCompleted is invoked.
base::android::ScopedJavaGlobalRef<jobjectArray> jwrite_buffer_list;
base::android::ScopedJavaGlobalRef<jintArray> jwrite_buffer_pos_list;
base::android::ScopedJavaGlobalRef<jintArray> jwrite_buffer_limit_list;
// A copy of the end of stream flag passed in from Java.
jboolean jwrite_end_of_stream;
// Every IOBuffer in |write_buffer_list| points to the memory owned by the
// corresponding Java ByteBuffer in |jwrite_buffer_list|.
std::vector<scoped_refptr<net::IOBuffer>> write_buffer_list;
// A list of the length of each IOBuffer in |write_buffer_list|.
std::vector<int> write_buffer_len_list;
};
// An adapter from Java BidirectionalStream object to net::BidirectionalStream.
// Created and configured from a Java thread. Start, ReadData, WritevData and
// Destroy can be called on any thread (including network thread), and post
// calls to corresponding {Start|ReadData|WritevData|Destroy}OnNetworkThread to
// the network thread. The object is always deleted on network thread. All
// callbacks into the Java BidirectionalStream are done on the network thread.
// Java BidirectionalStream is expected to initiate the next step like ReadData
// or Destroy. Public methods can be called on any thread.
class CronetBidirectionalStreamAdapter
: public net::BidirectionalStream::Delegate {
public:
CronetBidirectionalStreamAdapter(
CronetContextAdapter* context,
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jbidi_stream,
bool jsend_request_headers_automatically,
bool enable_metrics,
bool traffic_stats_tag_set,
int32_t traffic_stats_tag,
bool traffic_stats_uid_set,
int32_t traffic_stats_uid,
net::NetworkChangeNotifier::NetworkHandle network);
CronetBidirectionalStreamAdapter(const CronetBidirectionalStreamAdapter&) =
delete;
CronetBidirectionalStreamAdapter& operator=(
const CronetBidirectionalStreamAdapter&) = delete;
~CronetBidirectionalStreamAdapter() override;
// Validates method and headers, initializes and starts the request. If
// |jend_of_stream| is true, then stream is half-closed after sending header
// frame and no data is expected to be written.
// Returns 0 if request is valid and started successfully,
// Returns -1 if |jmethod| is not valid HTTP method name.
// Returns position of invalid header value in |jheaders| if header name is
// not valid.
jint Start(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jstring>& jurl,
jint jpriority,
const base::android::JavaParamRef<jstring>& jmethod,
const base::android::JavaParamRef<jobjectArray>& jheaders,
jboolean jend_of_stream);
// Sends request headers to server.
// When |send_request_headers_automatically_| is
// false and OnStreamReady() is invoked with request_headers_sent = false,
// headers will be combined with next WriteData/WritevData unless this
// method is called first, in which case headers will be sent separately
// without delay.
// (This method cannot be called when |send_request_headers_automatically_| is
// true nor when OnStreamReady() is invoked with request_headers_sent = true,
// since headers have been sent by the stream when stream is negotiated
// successfully.)
void SendRequestHeaders(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Reads more data into |jbyte_buffer| starting at |jposition| and not
// exceeding |jlimit|. Arguments are preserved to ensure that |jbyte_buffer|
// is not modified by the application during read.
jboolean ReadData(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& jbyte_buffer,
jint jposition,
jint jlimit);
// Writes more data from |jbyte_buffers|. For the i_th buffer in
// |jbyte_buffers|, bytes to write start from i_th position in |jpositions|
// and end at i_th limit in |jlimits|.
// Arguments are preserved to ensure that |jbyte_buffer|
// is not modified by the application during write. The |jend_of_stream| is
// passed to remote to indicate end of stream.
jboolean WritevData(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobjectArray>& jbyte_buffers,
const base::android::JavaParamRef<jintArray>& jpositions,
const base::android::JavaParamRef<jintArray>& jlimits,
jboolean jend_of_stream);
// Releases all resources for the request and deletes the object itself.
// |jsend_on_canceled| indicates if Java onCanceled callback should be
// issued to indicate that no more callbacks will be issued.
void Destroy(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jboolean jsend_on_canceled);
private:
// net::BidirectionalStream::Delegate implementations:
void OnStreamReady(bool request_headers_sent) override;
void OnHeadersReceived(
const spdy::Http2HeaderBlock& response_headers) override;
void OnDataRead(int bytes_read) override;
void OnDataSent() override;
void OnTrailersReceived(const spdy::Http2HeaderBlock& trailers) override;
void OnFailed(int error) override;
void StartOnNetworkThread(
std::unique_ptr<net::BidirectionalStreamRequestInfo> request_info);
void SendRequestHeadersOnNetworkThread();
void ReadDataOnNetworkThread(
scoped_refptr<IOBufferWithByteBuffer> read_buffer,
int buffer_size);
void WritevDataOnNetworkThread(
std::unique_ptr<PendingWriteData> pending_write_data);
void DestroyOnNetworkThread(bool send_on_canceled);
// Gets headers as a Java array.
base::android::ScopedJavaLocalRef<jobjectArray> GetHeadersArray(
JNIEnv* env,
const spdy::Http2HeaderBlock& header_block);
// Helper method to report metrics to the Java layer.
void MaybeReportMetrics();
const raw_ptr<CronetContextAdapter> context_;
// Java object that owns this CronetBidirectionalStreamAdapter.
base::android::ScopedJavaGlobalRef<jobject> owner_;
const bool send_request_headers_automatically_;
// Whether metrics collection is enabled when |this| is created.
const bool enable_metrics_;
// Whether |traffic_stats_tag_| should be applied.
const bool traffic_stats_tag_set_;
// TrafficStats tag to apply to URLRequest.
const int32_t traffic_stats_tag_;
// Whether |traffic_stats_uid_| should be applied.
const bool traffic_stats_uid_set_;
// UID to be applied to URLRequest.
const int32_t traffic_stats_uid_;
// If not equal to net::NetworkChangeNotifier::kInvalidNetworkHandle, the
// network to be used to send this request.
const net::NetworkChangeNotifier::NetworkHandle network_;
scoped_refptr<IOBufferWithByteBuffer> read_buffer_;
std::unique_ptr<PendingWriteData> pending_write_data_;
std::unique_ptr<net::BidirectionalStream> bidi_stream_;
// Whether BidirectionalStream::Delegate::OnFailed callback is invoked.
bool stream_failed_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_CRONET_BIDIRECTIONAL_STREAM_ADAPTER_H_

View File

@ -1,353 +0,0 @@
// Copyright 2014 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 "components/cronet/android/cronet_context_adapter.h"
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/cronet/android/buildflags.h"
#include "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h"
#include "components/cronet/android/cronet_library_loader.h"
#include "components/cronet/cronet_prefs_manager.h"
#include "components/cronet/host_cache_persistence_manager.h"
#include "components/cronet/url_request_context_config.h"
#include "components/metrics/library_support/histogram_manager.h"
#include "net/base/load_flags.h"
#include "net/base/logging_network_change_observer.h"
#include "net/base/net_errors.h"
#include "net/base/network_delegate_impl.h"
#include "net/base/url_util.h"
#include "net/cert/caching_cert_verifier.h"
#include "net/cert/cert_verifier.h"
#include "net/cookies/cookie_monster.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_util.h"
#include "net/nqe/network_quality_estimator_params.h"
#include "net/proxy_resolution/proxy_config_service_android.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_interceptor.h"
#if BUILDFLAG(INTEGRATED_MODE)
#include "components/cronet/android/cronet_integrated_mode_state.h"
#endif
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace {
// Helper method that takes a Java string that can be null, in which case it
// will get converted to an empty string.
std::string ConvertNullableJavaStringToUTF8(JNIEnv* env,
const JavaParamRef<jstring>& jstr) {
std::string str;
if (!jstr.is_null())
base::android::ConvertJavaStringToUTF8(env, jstr, &str);
return str;
}
} // namespace
namespace cronet {
CronetContextAdapter::CronetContextAdapter(
std::unique_ptr<URLRequestContextConfig> context_config) {
// Create context and pass ownership of |this| (self) to the context.
std::unique_ptr<CronetContextAdapter> self(this);
#if BUILDFLAG(INTEGRATED_MODE)
// Create CronetContext running in integrated network task runner.
context_ = new CronetContext(std::move(context_config), std::move(self),
GetIntegratedModeNetworkTaskRunner());
#else
context_ = new CronetContext(std::move(context_config), std::move(self));
#endif
}
CronetContextAdapter::~CronetContextAdapter() = default;
void CronetContextAdapter::InitRequestContextOnInitThread(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
jcronet_url_request_context_.Reset(env, jcaller);
context_->InitRequestContextOnInitThread();
}
void CronetContextAdapter::ConfigureNetworkQualityEstimatorForTesting(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
jboolean use_local_host_requests,
jboolean use_smaller_responses,
jboolean disable_offline_check) {
context_->ConfigureNetworkQualityEstimatorForTesting(
use_local_host_requests == JNI_TRUE, use_smaller_responses == JNI_TRUE,
disable_offline_check == JNI_TRUE);
}
void CronetContextAdapter::ProvideRTTObservations(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
bool should) {
context_->ProvideRTTObservations(should == JNI_TRUE);
}
void CronetContextAdapter::ProvideThroughputObservations(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
bool should) {
context_->ProvideThroughputObservations(should == JNI_TRUE);
}
void CronetContextAdapter::OnInitNetworkThread() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_CronetUrlRequestContext_initNetworkThread(env,
jcronet_url_request_context_);
}
void CronetContextAdapter::OnDestroyNetworkThread() {
// The |context_| is destroyed.
context_ = nullptr;
}
void CronetContextAdapter::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType effective_connection_type) {
Java_CronetUrlRequestContext_onEffectiveConnectionTypeChanged(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
effective_connection_type);
}
void CronetContextAdapter::OnRTTOrThroughputEstimatesComputed(
int32_t http_rtt_ms,
int32_t transport_rtt_ms,
int32_t downstream_throughput_kbps) {
Java_CronetUrlRequestContext_onRTTOrThroughputEstimatesComputed(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
http_rtt_ms, transport_rtt_ms, downstream_throughput_kbps);
}
void CronetContextAdapter::OnRTTObservation(
int32_t rtt_ms,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) {
Java_CronetUrlRequestContext_onRttObservation(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
rtt_ms, timestamp_ms, source);
}
void CronetContextAdapter::OnThroughputObservation(
int32_t throughput_kbps,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) {
Java_CronetUrlRequestContext_onThroughputObservation(
base::android::AttachCurrentThread(), jcronet_url_request_context_,
throughput_kbps, timestamp_ms, source);
}
void CronetContextAdapter::OnStopNetLogCompleted() {
Java_CronetUrlRequestContext_stopNetLogCompleted(
base::android::AttachCurrentThread(), jcronet_url_request_context_);
}
void CronetContextAdapter::Destroy(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
// Deleting |context_| on client thread will post cleanup onto network thread,
// which will in turn delete |this| on network thread.
delete context_;
}
net::URLRequestContext* CronetContextAdapter::GetURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network) {
return context_->GetURLRequestContext(network);
}
void CronetContextAdapter::PostTaskToNetworkThread(
const base::Location& posted_from,
base::OnceClosure callback) {
context_->PostTaskToNetworkThread(posted_from, std::move(callback));
}
bool CronetContextAdapter::IsOnNetworkThread() const {
return context_->IsOnNetworkThread();
}
bool CronetContextAdapter::StartNetLogToFile(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& jfile_name,
jboolean jlog_all) {
std::string file_name(
base::android::ConvertJavaStringToUTF8(env, jfile_name));
return context_->StartNetLogToFile(file_name, jlog_all == JNI_TRUE);
}
void CronetContextAdapter::StartNetLogToDisk(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& jdir_name,
jboolean jlog_all,
jint jmax_size) {
std::string dir_name(base::android::ConvertJavaStringToUTF8(env, jdir_name));
context_->StartNetLogToDisk(dir_name, jlog_all == JNI_TRUE, jmax_size);
}
void CronetContextAdapter::StopNetLog(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
context_->StopNetLog();
}
int CronetContextAdapter::default_load_flags() const {
return context_->default_load_flags();
}
// Create a URLRequestContextConfig from the given parameters.
static jlong JNI_CronetUrlRequestContext_CreateRequestContextConfig(
JNIEnv* env,
const JavaParamRef<jstring>& juser_agent,
const JavaParamRef<jstring>& jstorage_path,
jboolean jquic_enabled,
const JavaParamRef<jstring>& jquic_default_user_agent_id,
jboolean jhttp2_enabled,
jboolean jbrotli_enabled,
jboolean jdisable_cache,
jint jhttp_cache_mode,
jlong jhttp_cache_max_size,
const JavaParamRef<jstring>& jexperimental_quic_connection_options,
jlong jmock_cert_verifier,
jboolean jenable_network_quality_estimator,
jboolean jbypass_public_key_pinning_for_local_trust_anchors,
jint jnetwork_thread_priority) {
std::unique_ptr<URLRequestContextConfig> url_request_context_config =
URLRequestContextConfig::CreateURLRequestContextConfig(
jquic_enabled,
ConvertNullableJavaStringToUTF8(env, jquic_default_user_agent_id),
jhttp2_enabled, jbrotli_enabled,
static_cast<URLRequestContextConfig::HttpCacheType>(jhttp_cache_mode),
jhttp_cache_max_size, jdisable_cache,
ConvertNullableJavaStringToUTF8(env, jstorage_path),
/* accept_languages */ std::string(),
ConvertNullableJavaStringToUTF8(env, juser_agent),
ConvertNullableJavaStringToUTF8(
env, jexperimental_quic_connection_options),
base::WrapUnique(
reinterpret_cast<net::CertVerifier*>(jmock_cert_verifier)),
jenable_network_quality_estimator,
jbypass_public_key_pinning_for_local_trust_anchors,
jnetwork_thread_priority >= -20 && jnetwork_thread_priority <= 19
? absl::optional<double>(jnetwork_thread_priority)
: absl::optional<double>());
return reinterpret_cast<jlong>(url_request_context_config.release());
}
// Add a QUIC hint to a URLRequestContextConfig.
static void JNI_CronetUrlRequestContext_AddQuicHint(
JNIEnv* env,
jlong jurl_request_context_config,
const JavaParamRef<jstring>& jhost,
jint jport,
jint jalternate_port) {
URLRequestContextConfig* config =
reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
config->quic_hints.push_back(
std::make_unique<URLRequestContextConfig::QuicHint>(
base::android::ConvertJavaStringToUTF8(env, jhost), jport,
jalternate_port));
}
// Add a public key pin to URLRequestContextConfig.
// |jhost| is the host to apply the pin to.
// |jhashes| is an array of jbyte[32] representing SHA256 key hashes.
// |jinclude_subdomains| indicates if pin should be applied to subdomains.
// |jexpiration_time| is the time that the pin expires, in milliseconds since
// Jan. 1, 1970, midnight GMT.
static void JNI_CronetUrlRequestContext_AddPkp(
JNIEnv* env,
jlong jurl_request_context_config,
const JavaParamRef<jstring>& jhost,
const JavaParamRef<jobjectArray>& jhashes,
jboolean jinclude_subdomains,
jlong jexpiration_time) {
URLRequestContextConfig* config =
reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
std::unique_ptr<URLRequestContextConfig::Pkp> pkp(
new URLRequestContextConfig::Pkp(
base::android::ConvertJavaStringToUTF8(env, jhost),
jinclude_subdomains,
base::Time::UnixEpoch() + base::Milliseconds(jexpiration_time)));
for (auto bytes_array : jhashes.ReadElements<jbyteArray>()) {
static_assert(std::is_pod<net::SHA256HashValue>::value,
"net::SHA256HashValue is not POD");
static_assert(sizeof(net::SHA256HashValue) * CHAR_BIT == 256,
"net::SHA256HashValue contains overhead");
if (env->GetArrayLength(bytes_array.obj()) !=
sizeof(net::SHA256HashValue)) {
LOG(ERROR) << "Unable to add public key hash value.";
continue;
}
jbyte* bytes = env->GetByteArrayElements(bytes_array.obj(), nullptr);
net::HashValue hash(*reinterpret_cast<net::SHA256HashValue*>(bytes));
pkp->pin_hashes.push_back(hash);
env->ReleaseByteArrayElements(bytes_array.obj(), bytes, JNI_ABORT);
}
config->pkp_list.push_back(std::move(pkp));
}
// Creates RequestContextAdater if config is valid URLRequestContextConfig,
// returns 0 otherwise.
static jlong JNI_CronetUrlRequestContext_CreateRequestContextAdapter(
JNIEnv* env,
jlong jconfig) {
std::unique_ptr<URLRequestContextConfig> context_config(
reinterpret_cast<URLRequestContextConfig*>(jconfig));
CronetContextAdapter* context_adapter =
new CronetContextAdapter(std::move(context_config));
return reinterpret_cast<jlong>(context_adapter);
}
static jint JNI_CronetUrlRequestContext_SetMinLogLevel(JNIEnv* env,
jint jlog_level) {
jint old_log_level = static_cast<jint>(logging::GetMinLogLevel());
// MinLogLevel is global, shared by all URLRequestContexts.
logging::SetMinLogLevel(static_cast<int>(jlog_level));
return old_log_level;
}
static ScopedJavaLocalRef<jbyteArray>
JNI_CronetUrlRequestContext_GetHistogramDeltas(JNIEnv* env) {
std::vector<uint8_t> data;
if (!metrics::HistogramManager::GetInstance()->GetDeltas(&data))
return ScopedJavaLocalRef<jbyteArray>();
return base::android::ToJavaByteArray(env, data.data(), data.size());
}
} // namespace cronet

View File

@ -1,153 +0,0 @@
// Copyright 2014 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.
#ifndef COMPONENTS_CRONET_ANDROID_CRONET_CONTEXT_ADAPTER_H_
#define COMPONENTS_CRONET_ANDROID_CRONET_CONTEXT_ADAPTER_H_
#include <jni.h>
#include <stdint.h>
#include <memory>
#include "base/android/scoped_java_ref.h"
#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread.h"
#include "components/cronet/cronet_context.h"
#include "components/prefs/json_pref_store.h"
#include "net/base/network_change_notifier.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/effective_connection_type_observer.h"
#include "net/nqe/network_quality_estimator.h"
#include "net/nqe/network_quality_observation_source.h"
#include "net/nqe/rtt_throughput_estimates_observer.h"
namespace net {
class NetLog;
class URLRequestContext;
} // namespace net
namespace cronet {
class TestUtil;
struct URLRequestContextConfig;
// Adapter between Java CronetUrlRequestContext and CronetContext.
class CronetContextAdapter : public CronetContext::Callback {
public:
explicit CronetContextAdapter(
std::unique_ptr<URLRequestContextConfig> context_config);
CronetContextAdapter(const CronetContextAdapter&) = delete;
CronetContextAdapter& operator=(const CronetContextAdapter&) = delete;
~CronetContextAdapter() override;
// Called on init Java thread to initialize URLRequestContext.
void InitRequestContextOnInitThread(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Releases all resources for the request context and deletes the object.
// Blocks until network thread is destroyed after running all pending tasks.
void Destroy(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Posts a task that might depend on the context being initialized
// to the network thread.
void PostTaskToNetworkThread(const base::Location& posted_from,
base::OnceClosure callback);
bool IsOnNetworkThread() const;
net::URLRequestContext* GetURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network =
net::NetworkChangeNotifier::kInvalidNetworkHandle);
// TODO(xunjieli): Keep only one version of StartNetLog().
// Starts NetLog logging to file. This can be called on any thread.
// Return false if |jfile_name| cannot be opened.
bool StartNetLogToFile(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jstring>& jfile_name,
jboolean jlog_all);
// Starts NetLog logging to disk with a bounded amount of disk space. This
// can be called on any thread.
void StartNetLogToDisk(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jstring>& jdir_name,
jboolean jlog_all,
jint jmax_size);
// Stops NetLog logging to file. This can be called on any thread. This will
// flush any remaining writes to disk.
void StopNetLog(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Default net::LOAD flags used to create requests.
int default_load_flags() const;
// Called on init Java thread to initialize URLRequestContext.
void InitRequestContextOnInitThread();
// Configures the network quality estimator to observe requests to localhost,
// to use smaller responses when estimating throughput, and to disable the
// device offline checks when computing the effective connection type or when
// writing the prefs. This should only be used for testing. This can be
// called only after the network quality estimator has been enabled.
void ConfigureNetworkQualityEstimatorForTesting(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jboolean use_local_host_requests,
jboolean use_smaller_responses,
jboolean disable_offline_check);
// Request that RTT and/or throughput observations should or should not be
// provided by the network quality estimator.
void ProvideRTTObservations(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
bool should);
void ProvideThroughputObservations(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
bool should);
CronetContext* cronet_url_request_context() const { return context_; }
// CronetContext::Callback
void OnInitNetworkThread() override;
void OnDestroyNetworkThread() override;
void OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType effective_connection_type) override;
void OnRTTOrThroughputEstimatesComputed(
int32_t http_rtt_ms,
int32_t transport_rtt_ms,
int32_t downstream_throughput_kbps) override;
void OnRTTObservation(int32_t rtt_ms,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) override;
void OnThroughputObservation(
int32_t throughput_kbps,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) override;
void OnStopNetLogCompleted() override;
private:
friend class TestUtil;
// Native Cronet URL Request Context.
raw_ptr<CronetContext> context_;
// Java object that owns this CronetContextAdapter.
base::android::ScopedJavaGlobalRef<jobject> jcronet_url_request_context_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_CRONET_ADAPTER_H_

View File

@ -1 +0,0 @@
# Proguard config for apps that depend on cronet_impl_common_java.jar.

View File

@ -1,6 +0,0 @@
# Proguard config for apps that depend on cronet_impl_fake_java.jar.
# This constructor is called using the reflection from Cronet API (cronet_api.jar).
-keep class org.chromium.net.test.FakeCronetProvider {
public <init>(android.content.Context);
}

View File

@ -1,32 +0,0 @@
# Proguard config for apps that depend on cronet_impl_native_java.jar.
# This constructor is called using the reflection from Cronet API (cronet_api.jar).
-keep class org.chromium.net.impl.NativeCronetProvider {
public <init>(android.content.Context);
}
# Suppress unnecessary warnings.
-dontnote org.chromium.net.ProxyChangeListener$ProxyReceiver
-dontnote org.chromium.net.AndroidKeyStore
# Needs 'void setTextAppearance(int)' (API level 23).
-dontwarn org.chromium.base.ApiCompatibilityUtils
# Needs 'boolean onSearchRequested(android.view.SearchEvent)' (API level 23).
-dontwarn org.chromium.base.WindowCallbackWrapper
# Generated for chrome apk and not included into cronet.
-dontwarn org.chromium.base.multidex.ChromiumMultiDexInstaller
-dontwarn org.chromium.base.library_loader.LibraryLoader
-dontwarn org.chromium.base.SysUtils
-dontwarn org.chromium.build.NativeLibraries
# Objects of this type are passed around by native code, but the class
# is never used directly by native code. Since the class is not loaded, it does
# not need to be preserved as an entry point.
-dontnote org.chromium.net.UrlRequest$ResponseHeadersMap
# https://android.googlesource.com/platform/sdk/+/marshmallow-mr1-release/files/proguard-android.txt#54
-dontwarn android.support.**
# This class should be explicitly kept to avoid failure if
# class/merging/horizontal proguard optimization is enabled.
-keep class org.chromium.base.CollectionUtil

View File

@ -1,6 +0,0 @@
# Proguard config for apps that depend on cronet_impl_platform_java.jar.
# This constructor is called using the reflection from Cronet API (cronet_api.jar).
-keep class org.chromium.net.impl.JavaCronetProvider {
public <init>(android.content.Context);
}

View File

@ -1,31 +0,0 @@
// 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.
#include "components/cronet/android/cronet_integrated_mode_state.h"
#include "base/atomicops.h"
namespace cronet {
namespace {
base::subtle::AtomicWord g_integrated_mode_network_task_runner = 0;
} // namespace
void SetIntegratedModeNetworkTaskRunner(
base::SingleThreadTaskRunner* network_task_runner) {
CHECK_EQ(base::subtle::Release_CompareAndSwap(
&g_integrated_mode_network_task_runner, 0,
reinterpret_cast<base::subtle::AtomicWord>(network_task_runner)),
0);
}
base::SingleThreadTaskRunner* GetIntegratedModeNetworkTaskRunner() {
base::subtle::AtomicWord task_runner =
base::subtle::Acquire_Load(&g_integrated_mode_network_task_runner);
CHECK(task_runner);
return reinterpret_cast<base::SingleThreadTaskRunner*>(task_runner);
}
} // namespace cronet

View File

@ -1,29 +0,0 @@
// 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.
#ifndef COMPONENTS_CRONET_ANDROID_CRONET_INTEGRATED_MODE_STATE_H_
#define COMPONENTS_CRONET_ANDROID_CRONET_INTEGRATED_MODE_STATE_H_
#include "base/task/thread_pool/thread_pool_instance.h"
namespace cronet {
/**
* Set a shared network task runner into Cronet in integrated mode. All the
* Cronet network tasks would be running in this task runner. This method should
* be invoked in native side before creating Cronet instance.
*/
void SetIntegratedModeNetworkTaskRunner(
base::SingleThreadTaskRunner* network_task_runner);
/**
* Get the task runner for Cronet integrated mode. It would be invoked in the
* initialization of CronetURLRequestContext. This method must be invoked after
* SetIntegratedModeNetworkTaskRunner.
*/
base::SingleThreadTaskRunner* GetIntegratedModeNetworkTaskRunner();
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_CRONET_INTEGRATED_MODE_STATE_H_

View File

@ -1,15 +0,0 @@
// Copyright 2014 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 "components/cronet/android/cronet_library_loader.h"
// This is called by the VM when the shared library is first loaded.
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return cronet::CronetOnLoad(vm, reserved);
}
extern "C" void JNI_OnUnLoad(JavaVM* vm, void* reserved) {
cronet::CronetOnUnLoad(vm, reserved);
}

View File

@ -1,223 +0,0 @@
// Copyright 2014 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 <jni.h>
#include <memory>
#include <string>
#include <utility>
#include "base/android/base_jni_onload.h"
#include "base/android/build_info.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "base/android/jni_string.h"
#include "base/android/jni_utils.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/message_loop/message_pump_type.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_executor.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "build/build_config.h"
#include "components/cronet/android/buildflags.h"
#include "components/cronet/android/cronet_jni_headers/CronetLibraryLoader_jni.h"
#include "components/cronet/cronet_global_state.h"
#include "components/cronet/version.h"
#include "net/android/network_change_notifier_factory_android.h"
#include "net/base/network_change_notifier.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/proxy_resolution/proxy_config_service_android.h"
#include "third_party/zlib/zlib.h"
#include "url/buildflags.h"
#if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
#include "base/i18n/icu_util.h" // nogncheck
#endif
#if !BUILDFLAG(INTEGRATED_MODE)
#include "components/cronet/android/cronet_jni_registration.h"
#include "components/cronet/android/cronet_library_loader.h"
#endif
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace cronet {
namespace {
// SingleThreadTaskExecutor on the init thread, which is where objects that
// receive Java notifications generally live.
base::SingleThreadTaskExecutor* g_init_task_executor = nullptr;
#if !BUILDFLAG(INTEGRATED_MODE)
std::unique_ptr<net::NetworkChangeNotifier> g_network_change_notifier;
#endif
base::WaitableEvent g_init_thread_init_done(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
void NativeInit() {
// In integrated mode, ICU and FeatureList has been initialized by the host.
#if !BUILDFLAG(INTEGRATED_MODE)
#if !BUILDFLAG(USE_PLATFORM_ICU_ALTERNATIVES)
base::i18n::InitializeICU();
#endif
base::FeatureList::InitializeInstance(std::string(), std::string());
#endif
if (!base::ThreadPoolInstance::Get())
base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Cronet");
}
} // namespace
bool OnInitThread() {
DCHECK(g_init_task_executor);
return g_init_task_executor->task_runner()->RunsTasksInCurrentSequence();
}
// In integrated mode, Cronet native library is built and loaded together with
// the native library of the host app.
#if !BUILDFLAG(INTEGRATED_MODE)
// Checks the available version of JNI. Also, caches Java reflection artifacts.
jint CronetOnLoad(JavaVM* vm, void* reserved) {
base::android::InitVM(vm);
JNIEnv* env = base::android::AttachCurrentThread();
if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
return -1;
}
if (!base::android::OnJNIOnLoadInit())
return -1;
NativeInit();
return JNI_VERSION_1_6;
}
void CronetOnUnLoad(JavaVM* jvm, void* reserved) {
if (base::ThreadPoolInstance::Get())
base::ThreadPoolInstance::Get()->Shutdown();
base::android::LibraryLoaderExitHook();
}
#endif
void JNI_CronetLibraryLoader_CronetInitOnInitThread(JNIEnv* env) {
// Initialize SingleThreadTaskExecutor for init thread.
DCHECK(!base::CurrentThread::IsSet());
DCHECK(!g_init_task_executor);
g_init_task_executor =
new base::SingleThreadTaskExecutor(base::MessagePumpType::JAVA);
// In integrated mode, NetworkChangeNotifier has been initialized by the host.
#if BUILDFLAG(INTEGRATED_MODE)
CHECK(!net::NetworkChangeNotifier::CreateIfNeeded());
#else
DCHECK(!g_network_change_notifier);
if (!net::NetworkChangeNotifier::GetFactory()) {
net::NetworkChangeNotifier::SetFactory(
new net::NetworkChangeNotifierFactoryAndroid());
}
g_network_change_notifier = net::NetworkChangeNotifier::CreateIfNeeded();
DCHECK(g_network_change_notifier);
#endif
g_init_thread_init_done.Signal();
}
ScopedJavaLocalRef<jstring> JNI_CronetLibraryLoader_GetCronetVersion(
JNIEnv* env) {
#if defined(ARCH_CPU_ARM64)
// Attempt to avoid crashes on some ARM64 Marshmallow devices by
// prompting zlib ARM feature detection early on. https://crbug.com/853725
if (base::android::BuildInfo::GetInstance()->sdk_int() ==
base::android::SDK_VERSION_MARSHMALLOW) {
crc32(0, Z_NULL, 0);
}
#endif
return base::android::ConvertUTF8ToJavaString(env, CRONET_VERSION);
}
void PostTaskToInitThread(const base::Location& posted_from,
base::OnceClosure task) {
g_init_thread_init_done.Wait();
g_init_task_executor->task_runner()->PostTask(posted_from, std::move(task));
}
void EnsureInitialized() {
if (g_init_task_executor) {
// Ensure that init is done on the init thread.
g_init_thread_init_done.Wait();
return;
}
// The initialization can only be done once, so static |s_run_once| variable
// is used to do it in the constructor.
static class RunOnce {
public:
RunOnce() {
NativeInit();
JNIEnv* env = base::android::AttachCurrentThread();
// Ensure initialized from Java side to properly create Init thread.
cronet::Java_CronetLibraryLoader_ensureInitializedFromNative(env);
}
} s_run_once;
}
std::unique_ptr<net::ProxyConfigService> CreateProxyConfigService(
const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) {
// Note: CreateSystemProxyConfigService internally assumes that
// base::ThreadTaskRunnerHandle::Get() == JNI communication thread.
std::unique_ptr<net::ProxyConfigService> service =
net::ConfiguredProxyResolutionService::CreateSystemProxyConfigService(
io_task_runner);
// If a PAC URL is present, ignore it and use the address and port of
// Android system's local HTTP proxy server. See: crbug.com/432539.
// TODO(csharrison) Architect the wrapper better so we don't need to cast for
// android ProxyConfigServices.
net::ProxyConfigServiceAndroid* android_proxy_config_service =
static_cast<net::ProxyConfigServiceAndroid*>(service.get());
android_proxy_config_service->set_exclude_pac_url(true);
return service;
}
// Creates a proxy resolution service appropriate for this platform.
std::unique_ptr<net::ProxyResolutionService> CreateProxyResolutionService(
std::unique_ptr<net::ProxyConfigService> proxy_config_service,
net::NetLog* net_log) {
// Android provides a local HTTP proxy server that handles proxying when a PAC
// URL is present. Create a proxy service without a resolver and rely on this
// local HTTP proxy. See: crbug.com/432539.
return net::ConfiguredProxyResolutionService::CreateWithoutProxyResolver(
std::move(proxy_config_service), net_log);
}
// Creates default User-Agent request value, combining optional
// |partial_user_agent| with system-dependent values.
std::string CreateDefaultUserAgent(const std::string& partial_user_agent) {
// Cronet global state must be initialized to include application info
// into default user agent
cronet::EnsureInitialized();
JNIEnv* env = base::android::AttachCurrentThread();
std::string user_agent = base::android::ConvertJavaStringToUTF8(
cronet::Java_CronetLibraryLoader_getDefaultUserAgent(env));
if (!partial_user_agent.empty())
user_agent.insert(user_agent.size() - 1, "; " + partial_user_agent);
return user_agent;
}
void SetNetworkThreadPriorityOnNetworkThread(double priority) {
int priority_int = priority;
DCHECK_LE(priority_int, 19);
DCHECK_GE(priority_int, -20);
if (priority_int >= -20 && priority_int <= 19) {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetLibraryLoader_setNetworkThreadPriorityOnNetworkThread(
env, priority_int);
}
}
} // namespace cronet

View File

@ -1,17 +0,0 @@
// Copyright 2014 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.
#ifndef COMPONENTS_CRONET_ANDROID_CRONET_LIBRARY_LOADER_H_
#define COMPONENTS_CRONET_ANDROID_CRONET_LIBRARY_LOADER_H_
#include <jni.h>
namespace cronet {
jint CronetOnLoad(JavaVM* vm, void* reserved);
void CronetOnUnLoad(JavaVM* jvm, void* reserved);
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_CRONET_LIBRARY_LOADER_H_

View File

@ -1,147 +0,0 @@
// Copyright 2015 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 "components/cronet/android/cronet_upload_data_stream_adapter.h"
#include <memory>
#include <string>
#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/check_op.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/cronet/android/cronet_jni_headers/CronetUploadDataStream_jni.h"
#include "components/cronet/android/cronet_url_request_adapter.h"
#include "components/cronet/android/io_buffer_with_byte_buffer.h"
using base::android::JavaParamRef;
namespace cronet {
CronetUploadDataStreamAdapter::CronetUploadDataStreamAdapter(
JNIEnv* env,
jobject jupload_data_stream) {
jupload_data_stream_.Reset(env, jupload_data_stream);
}
CronetUploadDataStreamAdapter::~CronetUploadDataStreamAdapter() {
}
void CronetUploadDataStreamAdapter::InitializeOnNetworkThread(
base::WeakPtr<CronetUploadDataStream> upload_data_stream) {
DCHECK(!upload_data_stream_);
DCHECK(!network_task_runner_.get());
upload_data_stream_ = upload_data_stream;
network_task_runner_ = base::ThreadTaskRunnerHandle::Get();
DCHECK(network_task_runner_);
}
void CronetUploadDataStreamAdapter::Read(scoped_refptr<net::IOBuffer> buffer,
int buf_len) {
DCHECK(upload_data_stream_);
DCHECK(network_task_runner_);
DCHECK(network_task_runner_->BelongsToCurrentThread());
DCHECK_GT(buf_len, 0);
JNIEnv* env = base::android::AttachCurrentThread();
// Allow buffer reuse if |buffer| and |buf_len| are exactly the same as the
// ones used last time.
if (!(buffer_ && buffer_->io_buffer()->data() == buffer->data() &&
buffer_->io_buffer_len() == buf_len)) {
buffer_ = std::make_unique<ByteBufferWithIOBuffer>(env, std::move(buffer),
buf_len);
}
Java_CronetUploadDataStream_readData(env, jupload_data_stream_,
buffer_->byte_buffer());
}
void CronetUploadDataStreamAdapter::Rewind() {
DCHECK(upload_data_stream_);
DCHECK(network_task_runner_->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
Java_CronetUploadDataStream_rewind(env, jupload_data_stream_);
}
void CronetUploadDataStreamAdapter::OnUploadDataStreamDestroyed() {
// If CronetUploadDataStream::InitInternal was never called,
// |upload_data_stream_| and |network_task_runner_| will be NULL.
DCHECK(!network_task_runner_ ||
network_task_runner_->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
Java_CronetUploadDataStream_onUploadDataStreamDestroyed(env,
jupload_data_stream_);
// |this| is invalid here since the Java call above effectively destroys it.
}
void CronetUploadDataStreamAdapter::OnReadSucceeded(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
int bytes_read,
bool final_chunk) {
DCHECK(bytes_read > 0 || (final_chunk && bytes_read == 0));
network_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CronetUploadDataStream::OnReadSuccess,
upload_data_stream_, bytes_read, final_chunk));
}
void CronetUploadDataStreamAdapter::OnRewindSucceeded(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
network_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CronetUploadDataStream::OnRewindSuccess,
upload_data_stream_));
}
void CronetUploadDataStreamAdapter::Destroy(JNIEnv* env) {
delete this;
}
static jlong JNI_CronetUploadDataStream_AttachUploadDataToRequest(
JNIEnv* env,
const JavaParamRef<jobject>& jupload_data_stream,
jlong jcronet_url_request_adapter,
jlong jlength) {
CronetURLRequestAdapter* request_adapter =
reinterpret_cast<CronetURLRequestAdapter*>(jcronet_url_request_adapter);
DCHECK(request_adapter != nullptr);
CronetUploadDataStreamAdapter* adapter =
new CronetUploadDataStreamAdapter(env, jupload_data_stream);
std::unique_ptr<CronetUploadDataStream> upload_data_stream(
new CronetUploadDataStream(adapter, jlength));
request_adapter->SetUpload(std::move(upload_data_stream));
return reinterpret_cast<jlong>(adapter);
}
static jlong JNI_CronetUploadDataStream_CreateAdapterForTesting(
JNIEnv* env,
const JavaParamRef<jobject>& jupload_data_stream) {
CronetUploadDataStreamAdapter* adapter =
new CronetUploadDataStreamAdapter(env, jupload_data_stream);
return reinterpret_cast<jlong>(adapter);
}
static jlong JNI_CronetUploadDataStream_CreateUploadDataStreamForTesting(
JNIEnv* env,
const JavaParamRef<jobject>& jupload_data_stream,
jlong jlength,
jlong jadapter) {
CronetUploadDataStreamAdapter* adapter =
reinterpret_cast<CronetUploadDataStreamAdapter*>(jadapter);
CronetUploadDataStream* upload_data_stream =
new CronetUploadDataStream(adapter, jlength);
return reinterpret_cast<jlong>(upload_data_stream);
}
} // namespace cronet

View File

@ -1,79 +0,0 @@
// Copyright 2015 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.
#ifndef COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_ADAPTER_H_
#define COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_ADAPTER_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "components/cronet/cronet_upload_data_stream.h"
#include "net/base/io_buffer.h"
namespace base {
class SingleThreadTaskRunner;
} // namespace base
namespace cronet {
class ByteBufferWithIOBuffer;
// The Adapter holds onto a reference to the IOBuffer that is currently being
// written to in Java, so may not be deleted until any read operation in Java
// has completed.
//
// The Adapter is owned by the Java CronetUploadDataStream, and also owns a
// reference to it. The Adapter is only destroyed after the net::URLRequest
// destroys the C++ CronetUploadDataStream and the Java CronetUploadDataStream
// has no read operation pending, at which point it also releases its reference
// to the Java CronetUploadDataStream.
//
// Failures don't go back through the Adapter, but directly to the Java request
// object, since normally reads aren't allowed to fail during an upload.
class CronetUploadDataStreamAdapter : public CronetUploadDataStream::Delegate {
public:
CronetUploadDataStreamAdapter(JNIEnv* env, jobject jupload_data_stream);
CronetUploadDataStreamAdapter(const CronetUploadDataStreamAdapter&) = delete;
CronetUploadDataStreamAdapter& operator=(
const CronetUploadDataStreamAdapter&) = delete;
~CronetUploadDataStreamAdapter() override;
// CronetUploadDataStream::Delegate implementation. Called on network thread.
void InitializeOnNetworkThread(
base::WeakPtr<CronetUploadDataStream> upload_data_stream) override;
void Read(scoped_refptr<net::IOBuffer> buffer, int buf_len) override;
void Rewind() override;
void OnUploadDataStreamDestroyed() override;
// Callbacks from Java, called on some Java thread.
void OnReadSucceeded(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
int bytes_read,
bool final_chunk);
void OnRewindSucceeded(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj);
// Destroys |this|. Can be called from any thread, but needs to be protected
// by the adapter lock.
void Destroy(JNIEnv* env);
private:
// Initialized on construction, effectively constant.
base::android::ScopedJavaGlobalRef<jobject> jupload_data_stream_;
// These are initialized in InitializeOnNetworkThread, so are safe to access
// during Java callbacks, which all happen after initialization.
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
base::WeakPtr<CronetUploadDataStream> upload_data_stream_;
// Keeps the net::IOBuffer and Java ByteBuffer alive until the next Read().
std::unique_ptr<ByteBufferWithIOBuffer> buffer_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_ADAPTER_H_

View File

@ -1,328 +0,0 @@
// Copyright 2014 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 "components/cronet/android/cronet_url_request_adapter.h"
#include <limits>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "components/cronet/android/cronet_context_adapter.h"
#include "components/cronet/android/cronet_jni_headers/CronetUrlRequest_jni.h"
#include "components/cronet/android/io_buffer_with_byte_buffer.h"
#include "components/cronet/android/url_request_error.h"
#include "components/cronet/metrics_util.h"
#include "net/base/idempotency.h"
#include "net/base/load_flags.h"
#include "net/base/load_states.h"
#include "net/base/net_errors.h"
#include "net/base/proxy_server.h"
#include "net/base/request_priority.h"
#include "net/base/upload_data_stream.h"
#include "net/cert/cert_status_flags.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/ssl/ssl_info.h"
#include "net/third_party/quiche/src/quic/core/quic_packets.h"
#include "net/url_request/redirect_info.h"
#include "net/url_request/url_request_context.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
namespace {
base::android::ScopedJavaLocalRef<jobjectArray> ConvertResponseHeadersToJava(
JNIEnv* env,
const net::HttpResponseHeaders* headers) {
std::vector<std::string> response_headers;
// Returns an empty array if |headers| is nullptr.
if (headers != nullptr) {
size_t iter = 0;
std::string header_name;
std::string header_value;
while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) {
response_headers.push_back(header_name);
response_headers.push_back(header_value);
}
}
return base::android::ToJavaArrayOfStrings(env, response_headers);
}
} // namespace
namespace cronet {
static jlong JNI_CronetUrlRequest_CreateRequestAdapter(
JNIEnv* env,
const JavaParamRef<jobject>& jurl_request,
jlong jurl_request_context_adapter,
const JavaParamRef<jstring>& jurl_string,
jint jpriority,
jboolean jdisable_cache,
jboolean jdisable_connection_migration,
jboolean jenable_metrics,
jboolean jtraffic_stats_tag_set,
jint jtraffic_stats_tag,
jboolean jtraffic_stats_uid_set,
jint jtraffic_stats_uid,
jint jidempotency,
jlong jnetwork_handle) {
CronetContextAdapter* context_adapter =
reinterpret_cast<CronetContextAdapter*>(jurl_request_context_adapter);
DCHECK(context_adapter);
GURL url(base::android::ConvertJavaStringToUTF8(env, jurl_string));
VLOG(1) << "New chromium network request_adapter: "
<< url.possibly_invalid_spec();
CronetURLRequestAdapter* adapter = new CronetURLRequestAdapter(
context_adapter, env, jurl_request, url,
static_cast<net::RequestPriority>(jpriority), jdisable_cache,
jdisable_connection_migration, jenable_metrics, jtraffic_stats_tag_set,
jtraffic_stats_tag, jtraffic_stats_uid_set, jtraffic_stats_uid,
static_cast<net::Idempotency>(jidempotency), jnetwork_handle);
return reinterpret_cast<jlong>(adapter);
}
CronetURLRequestAdapter::CronetURLRequestAdapter(
CronetContextAdapter* context,
JNIEnv* env,
jobject jurl_request,
const GURL& url,
net::RequestPriority priority,
jboolean jdisable_cache,
jboolean jdisable_connection_migration,
jboolean jenable_metrics,
jboolean jtraffic_stats_tag_set,
jint jtraffic_stats_tag,
jboolean jtraffic_stats_uid_set,
jint jtraffic_stats_uid,
net::Idempotency idempotency,
jlong network)
: request_(
new CronetURLRequest(context->cronet_url_request_context(),
std::unique_ptr<CronetURLRequestAdapter>(this),
url,
priority,
jdisable_cache == JNI_TRUE,
jdisable_connection_migration == JNI_TRUE,
jenable_metrics == JNI_TRUE,
jtraffic_stats_tag_set == JNI_TRUE,
jtraffic_stats_tag,
jtraffic_stats_uid_set == JNI_TRUE,
jtraffic_stats_uid,
idempotency,
network)) {
owner_.Reset(env, jurl_request);
}
CronetURLRequestAdapter::~CronetURLRequestAdapter() {
}
jboolean CronetURLRequestAdapter::SetHttpMethod(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& jmethod) {
std::string method(base::android::ConvertJavaStringToUTF8(env, jmethod));
return request_->SetHttpMethod(method) ? JNI_TRUE : JNI_FALSE;
}
jboolean CronetURLRequestAdapter::AddRequestHeader(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jstring>& jname,
const JavaParamRef<jstring>& jvalue) {
std::string name(base::android::ConvertJavaStringToUTF8(env, jname));
std::string value(base::android::ConvertJavaStringToUTF8(env, jvalue));
return request_->AddRequestHeader(name, value) ? JNI_TRUE : JNI_FALSE;
}
void CronetURLRequestAdapter::SetUpload(
std::unique_ptr<net::UploadDataStream> upload) {
request_->SetUpload(std::move(upload));
}
void CronetURLRequestAdapter::Start(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
request_->Start();
}
void CronetURLRequestAdapter::GetStatus(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jobject>& jstatus_listener) {
base::android::ScopedJavaGlobalRef<jobject> status_listener_ref;
status_listener_ref.Reset(env, jstatus_listener);
request_->GetStatus(base::BindOnce(&CronetURLRequestAdapter::OnStatus,
base::Unretained(this),
status_listener_ref));
}
void CronetURLRequestAdapter::FollowDeferredRedirect(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
request_->FollowDeferredRedirect();
}
jboolean CronetURLRequestAdapter::ReadData(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jobject>& jbyte_buffer,
jint jposition,
jint jlimit) {
DCHECK_LT(jposition, jlimit);
void* data = env->GetDirectBufferAddress(jbyte_buffer);
if (!data)
return JNI_FALSE;
IOBufferWithByteBuffer* read_buffer =
new IOBufferWithByteBuffer(env, jbyte_buffer, data, jposition, jlimit);
int remaining_capacity = jlimit - jposition;
request_->ReadData(read_buffer, remaining_capacity);
return JNI_TRUE;
}
void CronetURLRequestAdapter::Destroy(JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
jboolean jsend_on_canceled) {
// Destroy could be called from any thread, including network thread (if
// posting task to executor throws an exception), but is posted, so |this|
// is valid until calling task is complete. Destroy() is always called from
// within a synchronized java block that guarantees no future posts to the
// network thread with the adapter pointer.
request_->Destroy(jsend_on_canceled == JNI_TRUE);
}
void CronetURLRequestAdapter::OnReceivedRedirect(
const std::string& new_location,
int http_status_code,
const std::string& http_status_text,
const net::HttpResponseHeaders* headers,
bool was_cached,
const std::string& negotiated_protocol,
const std::string& proxy_server,
int64_t received_byte_count) {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onRedirectReceived(
env, owner_, ConvertUTF8ToJavaString(env, new_location), http_status_code,
ConvertUTF8ToJavaString(env, http_status_text),
ConvertResponseHeadersToJava(env, headers),
was_cached ? JNI_TRUE : JNI_FALSE,
ConvertUTF8ToJavaString(env, negotiated_protocol),
ConvertUTF8ToJavaString(env, proxy_server), received_byte_count);
}
void CronetURLRequestAdapter::OnResponseStarted(
int http_status_code,
const std::string& http_status_text,
const net::HttpResponseHeaders* headers,
bool was_cached,
const std::string& negotiated_protocol,
const std::string& proxy_server,
int64_t received_byte_count) {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onResponseStarted(
env, owner_, http_status_code,
ConvertUTF8ToJavaString(env, http_status_text),
ConvertResponseHeadersToJava(env, headers),
was_cached ? JNI_TRUE : JNI_FALSE,
ConvertUTF8ToJavaString(env, negotiated_protocol),
ConvertUTF8ToJavaString(env, proxy_server), received_byte_count);
}
void CronetURLRequestAdapter::OnReadCompleted(
scoped_refptr<net::IOBuffer> buffer,
int bytes_read,
int64_t received_byte_count) {
IOBufferWithByteBuffer* read_buffer =
reinterpret_cast<IOBufferWithByteBuffer*>(buffer.get());
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onReadCompleted(
env, owner_, read_buffer->byte_buffer(), bytes_read,
read_buffer->initial_position(), read_buffer->initial_limit(),
received_byte_count);
}
void CronetURLRequestAdapter::OnSucceeded(int64_t received_byte_count) {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onSucceeded(env, owner_, received_byte_count);
}
void CronetURLRequestAdapter::OnError(int net_error,
int quic_error,
const std::string& error_string,
int64_t received_byte_count) {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onError(
env, owner_, NetErrorToUrlRequestError(net_error), net_error, quic_error,
ConvertUTF8ToJavaString(env, error_string), received_byte_count);
}
void CronetURLRequestAdapter::OnCanceled() {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onCanceled(env, owner_);
}
void CronetURLRequestAdapter::OnDestroyed() {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onNativeAdapterDestroyed(env, owner_);
// |this| adapter will be destroyed by the owner after return from this call.
}
void CronetURLRequestAdapter::OnStatus(
const base::android::ScopedJavaGlobalRef<jobject>& status_listener_ref,
net::LoadState load_status) {
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_CronetUrlRequest_onStatus(env, owner_, status_listener_ref,
load_status);
}
void CronetURLRequestAdapter::OnMetricsCollected(
const base::Time& start_time,
const base::TimeTicks& start_ticks,
const base::TimeTicks& dns_start,
const base::TimeTicks& dns_end,
const base::TimeTicks& connect_start,
const base::TimeTicks& connect_end,
const base::TimeTicks& ssl_start,
const base::TimeTicks& ssl_end,
const base::TimeTicks& send_start,
const base::TimeTicks& send_end,
const base::TimeTicks& push_start,
const base::TimeTicks& push_end,
const base::TimeTicks& receive_headers_end,
const base::TimeTicks& request_end,
bool socket_reused,
int64_t sent_bytes_count,
int64_t received_bytes_count) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_CronetUrlRequest_onMetricsCollected(
env, owner_,
metrics_util::ConvertTime(start_ticks, start_ticks, start_time),
metrics_util::ConvertTime(dns_start, start_ticks, start_time),
metrics_util::ConvertTime(dns_end, start_ticks, start_time),
metrics_util::ConvertTime(connect_start, start_ticks, start_time),
metrics_util::ConvertTime(connect_end, start_ticks, start_time),
metrics_util::ConvertTime(ssl_start, start_ticks, start_time),
metrics_util::ConvertTime(ssl_end, start_ticks, start_time),
metrics_util::ConvertTime(send_start, start_ticks, start_time),
metrics_util::ConvertTime(send_end, start_ticks, start_time),
metrics_util::ConvertTime(push_start, start_ticks, start_time),
metrics_util::ConvertTime(push_end, start_ticks, start_time),
metrics_util::ConvertTime(receive_headers_end, start_ticks, start_time),
metrics_util::ConvertTime(request_end, start_ticks, start_time),
socket_reused ? JNI_TRUE : JNI_FALSE, sent_bytes_count,
received_bytes_count);
}
} // namespace cronet

View File

@ -1,168 +0,0 @@
// Copyright 2014 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.
#ifndef COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_ADAPTER_H_
#define COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_ADAPTER_H_
#include <jni.h>
#include <memory>
#include <string>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "components/cronet/cronet_url_request.h"
#include "url/gurl.h"
namespace net {
enum LoadState;
class UploadDataStream;
} // namespace net
namespace cronet {
class CronetContextAdapter;
class TestUtil;
// An adapter from Java CronetUrlRequest object to native CronetURLRequest.
// Created and configured from a Java thread. Start, ReadData, and Destroy are
// posted to network thread and all callbacks into the Java CronetUrlRequest are
// done on the network thread. Java CronetUrlRequest is expected to initiate the
// next step like FollowDeferredRedirect, ReadData or Destroy. Public methods
// can be called on any thread.
class CronetURLRequestAdapter : public CronetURLRequest::Callback {
public:
// Bypasses cache if |jdisable_cache| is true. If context is not set up to
// use cache, |jdisable_cache| has no effect. |jdisable_connection_migration|
// causes connection migration to be disabled for this request if true. If
// global connection migration flag is not enabled,
// |jdisable_connection_migration| has no effect.
CronetURLRequestAdapter(CronetContextAdapter* context,
JNIEnv* env,
jobject jurl_request,
const GURL& url,
net::RequestPriority priority,
jboolean jdisable_cache,
jboolean jdisable_connection_migration,
jboolean jenable_metrics,
jboolean jtraffic_stats_tag_set,
jint jtraffic_stats_tag,
jboolean jtraffic_stats_uid_set,
jint jtraffic_stats_uid,
net::Idempotency idempotency,
jlong network);
CronetURLRequestAdapter(const CronetURLRequestAdapter&) = delete;
CronetURLRequestAdapter& operator=(const CronetURLRequestAdapter&) = delete;
~CronetURLRequestAdapter() override;
// Methods called prior to Start are never called on network thread.
// Sets the request method GET, POST etc.
jboolean SetHttpMethod(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jstring>& jmethod);
// Adds a header to the request before it starts.
jboolean AddRequestHeader(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jstring>& jname,
const base::android::JavaParamRef<jstring>& jvalue);
// Adds a request body to the request before it starts.
void SetUpload(std::unique_ptr<net::UploadDataStream> upload);
// Starts the request.
void Start(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller);
void GetStatus(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& jstatus_listener);
// Follows redirect.
void FollowDeferredRedirect(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Reads more data.
jboolean ReadData(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& jbyte_buffer,
jint jposition,
jint jcapacity);
// Releases all resources for the request and deletes the object itself.
// |jsend_on_canceled| indicates if Java onCanceled callback should be
// issued to indicate when no more callbacks will be issued.
void Destroy(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jboolean jsend_on_canceled);
// CronetURLRequest::Callback implementations:
void OnReceivedRedirect(const std::string& new_location,
int http_status_code,
const std::string& http_status_text,
const net::HttpResponseHeaders* headers,
bool was_cached,
const std::string& negotiated_protocol,
const std::string& proxy_server,
int64_t received_byte_count) override;
void OnResponseStarted(int http_status_code,
const std::string& http_status_text,
const net::HttpResponseHeaders* headers,
bool was_cached,
const std::string& negotiated_protocol,
const std::string& proxy_server,
int64_t received_byte_count) override;
void OnReadCompleted(scoped_refptr<net::IOBuffer> buffer,
int bytes_read,
int64_t received_byte_count) override;
void OnSucceeded(int64_t received_byte_count) override;
void OnError(int net_error,
int quic_error,
const std::string& error_string,
int64_t received_byte_count) override;
void OnCanceled() override;
void OnDestroyed() override;
void OnMetricsCollected(const base::Time& request_start_time,
const base::TimeTicks& request_start,
const base::TimeTicks& dns_start,
const base::TimeTicks& dns_end,
const base::TimeTicks& connect_start,
const base::TimeTicks& connect_end,
const base::TimeTicks& ssl_start,
const base::TimeTicks& ssl_end,
const base::TimeTicks& send_start,
const base::TimeTicks& send_end,
const base::TimeTicks& push_start,
const base::TimeTicks& push_end,
const base::TimeTicks& receive_headers_end,
const base::TimeTicks& request_end,
bool socket_reused,
int64_t sent_bytes_count,
int64_t received_bytes_count) override;
void OnStatus(
const base::android::ScopedJavaGlobalRef<jobject>& status_listener_ref,
net::LoadState load_status);
private:
friend class TestUtil;
// Native Cronet URL Request that owns |this|.
raw_ptr<CronetURLRequest> request_;
// Java object that owns this CronetContextAdapter.
base::android::ScopedJavaGlobalRef<jobject> owner_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_ADAPTER_H_

View File

@ -1,41 +0,0 @@
// Copyright 2016 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 "components/cronet/android/io_buffer_with_byte_buffer.h"
#include "base/check_op.h"
namespace cronet {
IOBufferWithByteBuffer::IOBufferWithByteBuffer(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jbyte_buffer,
void* byte_buffer_data,
jint position,
jint limit)
: net::WrappedIOBuffer(static_cast<char*>(byte_buffer_data) + position),
byte_buffer_(env, jbyte_buffer),
initial_position_(position),
initial_limit_(limit) {
DCHECK(byte_buffer_data);
DCHECK_EQ(env->GetDirectBufferAddress(jbyte_buffer), byte_buffer_data);
}
IOBufferWithByteBuffer::~IOBufferWithByteBuffer() {}
ByteBufferWithIOBuffer::ByteBufferWithIOBuffer(
JNIEnv* env,
scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_len)
: io_buffer_(std::move(io_buffer)), io_buffer_len_(io_buffer_len) {
// An intermediate ScopedJavaLocalRef is needed here to release the local
// reference created by env->NewDirectByteBuffer().
base::android::ScopedJavaLocalRef<jobject> java_buffer(
env, env->NewDirectByteBuffer(io_buffer_->data(), io_buffer_len_));
byte_buffer_.Reset(env, java_buffer.obj());
}
ByteBufferWithIOBuffer::~ByteBufferWithIOBuffer() {}
} // namespace cronet

View File

@ -1,80 +0,0 @@
// Copyright 2016 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.
#ifndef COMPONENTS_CRONET_ANDROID_IO_BUFFER_WITH_BYTE_BUFFER_H_
#define COMPONENTS_CRONET_ANDROID_IO_BUFFER_WITH_BYTE_BUFFER_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "net/base/io_buffer.h"
namespace cronet {
// net::WrappedIOBuffer subclass for a buffer owned by a Java ByteBuffer. Keeps
// the ByteBuffer alive until destroyed. Uses WrappedIOBuffer because data() is
// owned by the embedder.
class IOBufferWithByteBuffer : public net::WrappedIOBuffer {
public:
// Creates a buffer wrapping the Java ByteBuffer |jbyte_buffer|.
// |byte_buffer_data| points to the memory backed by the ByteBuffer, and
// |position| is the index of the first byte of data inside of the buffer.
// |limit| is the the index of the first element that should not be read or
// written, preserved to verify that buffer is not changed externally during
// networking operations.
IOBufferWithByteBuffer(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jbyte_buffer,
void* byte_buffer_data,
jint position,
jint limit);
IOBufferWithByteBuffer(const IOBufferWithByteBuffer&) = delete;
IOBufferWithByteBuffer& operator=(const IOBufferWithByteBuffer&) = delete;
jint initial_position() const { return initial_position_; }
jint initial_limit() const { return initial_limit_; }
const base::android::JavaRef<jobject>& byte_buffer() const {
return byte_buffer_;
}
private:
~IOBufferWithByteBuffer() override;
base::android::ScopedJavaGlobalRef<jobject> byte_buffer_;
const jint initial_position_;
const jint initial_limit_;
};
// Represents a Java direct ByteBuffer backed by a net::IOBuffer. Keeps both the
// net::IOBuffer and the Java ByteBuffer object alive until destroyed.
class ByteBufferWithIOBuffer {
public:
ByteBufferWithIOBuffer(JNIEnv* env,
scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_len);
ByteBufferWithIOBuffer(const ByteBufferWithIOBuffer&) = delete;
ByteBufferWithIOBuffer& operator=(const ByteBufferWithIOBuffer&) = delete;
~ByteBufferWithIOBuffer();
const net::IOBuffer* io_buffer() const { return io_buffer_.get(); }
int io_buffer_len() const { return io_buffer_len_; }
const base::android::JavaRef<jobject>& byte_buffer() const {
return byte_buffer_;
}
private:
scoped_refptr<net::IOBuffer> io_buffer_;
int io_buffer_len_;
base::android::ScopedJavaGlobalRef<jobject> byte_buffer_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_IO_BUFFER_WITH_BYTE_BUFFER_H_

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 7.1.0" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0">
<issue
id="QueryPermissionsNeeded"
message="As of Android 11, this method no longer returns information about all apps; \&#xA;see https://g.co/dev/packagevisibility for details"
errorLine1=" return mWrapped.getInstalledPackages(flags);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="../../third_party/android_sdk/public/extras/chromium/support/src/org/chromium/android/support/PackageManagerWrapper.java"
line="115"
column="25"/>
</issue>
<issue
id="QueryPermissionsNeeded"
message="As of Android 11, this method no longer returns information about all apps; \&#xA;see https://g.co/dev/packagevisibility for details"
errorLine1=" return mWrapped.getInstalledApplications(flags);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="../../third_party/android_sdk/public/extras/chromium/support/src/org/chromium/android/support/PackageManagerWrapper.java"
line="210"
column="25"/>
</issue>
</issues>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<lint>
<!-- Ignore all lint errors in chrome code. -->
<issue id="all">
<ignore regexp="../../base/"/>
<ignore regexp="../../net/"/>
<ignore regexp="../../testing/"/>
</issue>
</lint>

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2014 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.chromium.cronet_sample_apk">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application android:name="CronetSampleApplication"
android:label="Cronet Sample">
<activity android:name="CronetSampleActivity"
android:launchMode="singleTask"
android:theme="@style/Theme.AppCompat.Light"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
android:hardwareAccelerated="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Disables at startup init of Emoji2. See http://crbug.com/1205141 -->
<provider
android:authorities="org.chromium.cronet_sample_apk.androidx-startup"
android:name="androidx.startup.InitializationProvider"
android:exported="false"
tools:replace="android:authorities"
tools:node="remove">
</provider>
</application>
</manifest>

View File

@ -1,174 +0,0 @@
How to set up and run the sample app as an Android Studio project.
Linux (Android Studio version 0.8.11 beta)
=====
(1) Launch Android Studio.
(2) Choose "Import project".
- Navigate to the location of the sample soure code.
You should be looking at a directory named "sample"
containing a file named "AndroidManifest.xml".
- Pick a new destination for it.
(3) Copy in the '.jar' files.
(a) Directly under the "app" directory of your project,
create a "libs" directory. Use a shell command if you like,
or use "File|New|Directory" from the menu. But note that
you only get "Directory" as an option if you are in
"Project" view, not "Android" view. "Project" models
the local machine's filesystem, but Android is a virtual
layout of files corresponding to the deployed hierarchy.
That is to say, do step (b) before step (a) if you're inclined.
(b) Toggle the view from "Android" to "Project"
in the selection list above the file hierarchy.
Otherwise you won't see "libs".
(c) Copy 'cronet.jar' and 'cronet_stub.jar' to "libs".
[Also note that it is possible to leave the '.jar' files
in their original locations, though this seems to be
somewhat discouraged by convention]
(4) Inform the IDE about the '.jar' files.
(a) Select both files at once.
(b) Bring up the context menu and choose "Add as Library".
(d) Confirm "OK" at the "Add to module" dialog.
[Note: the keyboard shortcut and/or main menu selection
for these steps seems to be missing from Android Studio.
If you prefer, the advice under problem #2
in "Troubleshooting" below will perform the same thing
without reliance on menu selections]
(5) Copy in the '.so' file.
(a) Under "app/src/main" create a directory named "jniLibs"
(b) Copy armeabi and ameabi-v7a into jniLibs, which should
contain only subdirectories, not directly a '.so' file
(c) The IDE will automatically know about these.
(6) Click "Run".
Troubleshooting:
If you have vast swaths of red text (errors) in the edit window
for CronetSampleActivity, you should confirm that the requisite
jar files are present in 'build.gradle'. There are at least 2
files which are named 'build.gradle'. Check them both.
You should observe the following lines [see footnote 1]
dependencies {
compile file('libs/cronet.jar')
compile file('libs/cronet_stub.jar')
}
If absent, the lines may be added by hand to the gradle file
which corresponds to the module named "app", and not the project
s a whole. You might have to press a "Sync" button in the IDE
which tells it to re-scan the 'build.gradle' files.
(II) If the project builds but doesn't run, verify that the '.so' files
are present in your Android package (which is just a jar file in disguise):
% jar tf build/outputs/apk/app-debug.apk
AndroidManifest.xml
res/layout/cronet_sample_activity.xml
resource.arsc
classes.dex
lib/armeabi/libcronet.so
lib/armeabi-v7/libcronet.so
META-INF
etc
If the '.so' files are not present, it is likely that Android Studio
misinterpreted the containing folder as "ordinary" source code,
which, due to lack of any special directive pertaining to it, failed
to be copied to the apk. One thing to check for is the spelling
of "jniLibs" - it must literally be that, with a capital "L" or
it won't work. This is a bit of magic that allows users without
the NDK (Native Development Kit) to deploy '.so' files.
[With the NDK, things are different because in that case you have to
produce the '.so' files, and create build rules to do so,
so the setup is naturally more flexible to begin with.]
As a visual cue that the folder has been recognized as special,
its icon should match that of the "res" (resources) folder
which resembles a tabbed manila folder with some extra cross-hatches
on the front, and not the icon of the "java" folder.
The marking on the icon signifies that its contents land on the
the target device. But to keep things interesting, the icon is
distinct visually only in "Android" view, not "Project" view.
MacOS (Android Studio version 1.0.1)
=====
(0) You might or might not have to set a magic environment
variable as follows [see footnote 3]:
export STUDIO_JDK=/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk
(1) Launch Android Studio, which can be achieved from the command
line with
% open '/Applications/Android Studio.app'
(2) Choose "Import Non-Android Studio Project"
(a) Navigate to the path containing "sample"
(b) Pick a place to put it, and choose "Finish"
(3) If you are comfortable using shell commands to create directories,
you may proceed to step (3) for Linux above.
Otherwise, if you prefer to create directories from the UI,
proceed with the following steps.
(4) Choose "File -> New -> Folder -> Assets Folder".
Check "Change Folder Location" and delete the entire
pathname that was there. Change it to 'libs'
which is conventional for pre-built jar files.
(5) Copy and paste the two pre-built '.jar' files into 'assets'.
When you do this, it will say that the destination is
"app/libs". This is right. If you prefer to see
the file hierarchy as it really exists, you can change
the dropdown above the tree view from "android view"
to "project view". Or just keep in mind that assets = libs
at this level of the hierarchy.
(6) Select both jar files that you added (Shift+click).
and pull up the menu for them (Ctrl+click).
Select "Add as library"
(7) Choose "File -> New -> Folder -> JNI Folder".
Choose "Change Folder Location"
and change the name to "src/main/jniLibs" [see footnote 2]
----
Footnotes:
[1] "compile file" as used in a dependency line means to package the
named jars into the apk, and not to make those files.
The opposite of this is "provided file" which assumes that the
jars exist on the device already (in whatever the standard
location is for systemwide jar files), and not that a file
has been externally provided to Android Studio.
[2] The menu option to add JNI files assumes that you have the
NDK (Native Development Kit) installed and want to produce
files into the named directory. This is triggered by an
automatic rule that tries to look for C++ source code
and the NDK based on the existence of "src/main/jni".
Changing this directory to "src/main/jniLibs" is magical
in a different way: it informs Android Studio that you will
place precompiled binaries into that directory.
[3] This has to do with differences between the JDK that the studio
runs in as distinct from the JDK that the studio understands
to be present on the target machine.
There is discussion of the issue in
https://code.google.com/p/android/issues/detail?id=82378
Additional notes:
Ideally the two .jar files and one .so file could be delivered as one .aar
(Android Archive) file, but Android Studio will try to pull aar files from
a Maven repository without some workarounds that are about as much trouble
as adding in three separate files.
See https://code.google.com/p/android/issues/detail?id=55863
Additionally, it is unclear how to automate the creation of a '.aar' file
outside of Android Studio and Gradle. If the entire workflow were controlled
by Gradle, it would be one thing; but presently the cronet jars are
produced as artifacts of the Chromium build which uses Ninja.

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2014 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 name must be unique so suffix with "tests" so package loader
doesn't ignore this. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.chromium.cronet_sample_apk.tests">
<uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
<!-- We add an application tag here just so that we can indicate that this
package needs to link against the android.test library, which is
needed when building test cases. -->
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="org.chromium.cronet_sample_apk"
android:label="Tests for org.chromium.cronet_sample_apk"/>
</manifest>

View File

@ -1,4 +0,0 @@
# Proguard settings for CronetSampleTestApk.
-keep class org.chromium.cronet_sample_apk.CronetSampleActivity {
*;
}

View File

@ -1,24 +0,0 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".CronetSampleActivity">
<TextView
android:id="@+id/resultView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/resultView">
<TextView
android:id="@+id/dataView"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"/>
</ScrollView>
</RelativeLayout>

View File

@ -1,41 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/urlView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginStart="4dp"
android:textSize="16sp"
android:text="@string/urlText" />
<!-- TODO(crbug.com/900912): Fix and remove lint ignore -->
<EditText
tools:ignore="LabelFor"
android:id="@+id/urlText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri" />
<TextView
android:id="@+id/postView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"
android:layout_marginStart="4dp"
android:textSize="16sp"
android:text="@string/postText" />
<!-- TODO(crbug.com/900912): Fix and remove lint ignore -->
<EditText
tools:ignore="LabelFor"
android:id="@+id/postText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</LinearLayout>

View File

@ -1,5 +0,0 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2014 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.
-->
<resources>
<string name="urlText">Enter a URL</string>
<string name="postText">Enter post data (leave it blank for GET request)</string>
</resources>

View File

@ -1,24 +0,0 @@
// Copyright 2014 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 <jni.h>
#include "base/android/base_jni_onload.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "base/android/library_loader/library_loader_hooks.h"
// This is called by the VM when the shared library is first loaded.
// Checks the available version of JNI. Also, caches Java reflection artifacts.
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {
base::android::InitVM(vm);
if (!base::android::OnJNIOnLoadInit()) {
return -1;
}
return JNI_VERSION_1_6;
}
extern "C" void JNI_OnUnLoad(JavaVM* vm, void* reserved) {
base::android::LibraryLoaderExitHook();
}

View File

@ -1,134 +0,0 @@
// Copyright 2016 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 "components/cronet/android/test/cronet_test_util.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/message_loop/message_pump.h"
#include "base/message_loop/message_pump_type.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/cronet/android/cronet_context_adapter.h"
#include "components/cronet/android/cronet_tests_jni_headers/CronetTestUtil_jni.h"
#include "components/cronet/android/cronet_url_request_adapter.h"
#include "components/cronet/cronet_context.h"
#include "components/cronet/cronet_url_request.h"
#include "net/socket/socket_test_util.h"
#include "net/url_request/url_request.h"
namespace cronet {
namespace {
using ::base::MessagePump;
using ::base::MessagePumpType;
using ::base::android::JavaParamRef;
using ::base::sequence_manager::SequenceManager;
SequenceManager* g_sequence_manager = nullptr;
} // namespace
jint JNI_CronetTestUtil_GetLoadFlags(JNIEnv* env,
const jlong jurl_request_adapter) {
return TestUtil::GetURLRequest(jurl_request_adapter)->load_flags();
}
// static
scoped_refptr<base::SingleThreadTaskRunner> TestUtil::GetTaskRunner(
jlong jcontext_adapter) {
CronetContextAdapter* context_adapter =
reinterpret_cast<CronetContextAdapter*>(jcontext_adapter);
return context_adapter->context_->network_task_runner_;
}
// static
net::URLRequestContext* TestUtil::GetURLRequestContext(jlong jcontext_adapter) {
CronetContextAdapter* context_adapter =
reinterpret_cast<CronetContextAdapter*>(jcontext_adapter);
return context_adapter->context_->network_tasks_->default_context_;
}
// static
void TestUtil::RunAfterContextInitOnNetworkThread(jlong jcontext_adapter,
base::OnceClosure task) {
CronetContextAdapter* context_adapter =
reinterpret_cast<CronetContextAdapter*>(jcontext_adapter);
if (context_adapter->context_->network_tasks_
->is_default_context_initialized_) {
std::move(task).Run();
} else {
context_adapter->context_->network_tasks_->tasks_waiting_for_context_.push(
std::move(task));
}
}
// static
void TestUtil::RunAfterContextInit(jlong jcontext_adapter,
base::OnceClosure task) {
GetTaskRunner(jcontext_adapter)
->PostTask(FROM_HERE,
base::BindOnce(&TestUtil::RunAfterContextInitOnNetworkThread,
jcontext_adapter, std::move(task)));
}
// static
net::URLRequest* TestUtil::GetURLRequest(jlong jrequest_adapter) {
CronetURLRequestAdapter* request_adapter =
reinterpret_cast<CronetURLRequestAdapter*>(jrequest_adapter);
return request_adapter->request_->network_tasks_.url_request_.get();
}
static void PrepareNetworkThreadOnNetworkThread(jlong jcontext_adapter) {
g_sequence_manager =
base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(
MessagePump::Create(MessagePumpType::IO),
SequenceManager::Settings::Builder()
.SetMessagePumpType(MessagePumpType::IO)
.Build())
.release();
g_sequence_manager->SetDefaultTaskRunner(
TestUtil::GetTaskRunner(jcontext_adapter));
}
// Tests need to call into libcronet.so code on libcronet.so threads.
// libcronet.so's threads are registered with static tables for MessageLoops
// and SingleThreadTaskRunners in libcronet.so, so libcronet_test.so
// functions that try and access these tables will find missing entries in
// the corresponding static tables in libcronet_test.so. Fix this by
// initializing a MessageLoop and SingleThreadTaskRunner in libcronet_test.so
// for these threads. Called from Java CronetTestUtil class.
void JNI_CronetTestUtil_PrepareNetworkThread(
JNIEnv* env,
jlong jcontext_adapter) {
TestUtil::GetTaskRunner(jcontext_adapter)
->PostTask(FROM_HERE, base::BindOnce(&PrepareNetworkThreadOnNetworkThread,
jcontext_adapter));
}
static void CleanupNetworkThreadOnNetworkThread() {
DCHECK(g_sequence_manager);
delete g_sequence_manager;
g_sequence_manager = nullptr;
}
// Called from Java CronetTestUtil class.
void JNI_CronetTestUtil_CleanupNetworkThread(
JNIEnv* env,
jlong jcontext_adapter) {
TestUtil::RunAfterContextInit(
jcontext_adapter, base::BindOnce(&CleanupNetworkThreadOnNetworkThread));
}
jboolean JNI_CronetTestUtil_CanGetTaggedBytes(JNIEnv* env) {
return net::CanGetTaggedBytes();
}
jlong JNI_CronetTestUtil_GetTaggedBytes(JNIEnv* env,
jint jexpected_tag) {
return net::GetTaggedBytes(jexpected_tag);
}
} // namespace cronet

View File

@ -1,53 +0,0 @@
// Copyright 2016 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.
#ifndef COMPONENTS_CRONET_ANDROID_TEST_CRONET_TEST_UTIL_H_
#define COMPONENTS_CRONET_ANDROID_TEST_CRONET_TEST_UTIL_H_
#include <jni.h>
#include "base/android/jni_android.h"
#include "base/memory/ref_counted.h"
#include "base/task/single_thread_task_runner.h"
namespace net {
class URLRequest;
class URLRequestContext;
} // namespace net
namespace cronet {
// Various test utility functions for testing Cronet.
// NOTE(pauljensen): This class is friended by Cronet internal implementation
// classes to provide access to internals.
class TestUtil {
public:
TestUtil() = delete;
TestUtil(const TestUtil&) = delete;
TestUtil& operator=(const TestUtil&) = delete;
// CronetURLRequestContextAdapter manipulation:
// Returns SingleThreadTaskRunner for the network thread of the context
// adapter.
static scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
jlong jcontext_adapter);
// Returns underlying URLRequestContext.
static net::URLRequestContext* GetURLRequestContext(jlong jcontext_adapter);
// Run |task| after URLRequestContext is initialized.
static void RunAfterContextInit(jlong jcontext_adapter,
base::OnceClosure task);
// CronetURLRequestAdapter manipulation:
// Returns underlying URLRequest.
static net::URLRequest* GetURLRequest(jlong jrequest_adapter);
private:
static void RunAfterContextInitOnNetworkThread(jlong jcontext_adapter,
base::OnceClosure task);
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_TEST_CRONET_TEST_UTIL_H_

View File

@ -1,16 +0,0 @@
// Copyright 2015 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.
#ifndef COMPONENTS_CRONET_ANDROID_TEST_CRONET_URL_REQUEST_CONTEXT_CONFIG_TEST_H_
#define COMPONENTS_CRONET_ANDROID_TEST_CRONET_URL_REQUEST_CONTEXT_CONFIG_TEST_H_
#include <jni.h>
namespace cronet {
bool RegisterCronetUrlRequestContextConfigTest(JNIEnv* env);
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_TEST_CRONET_URL_REQUEST_CONTEXT_CONFIG_TEST_H_

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.chromium.net">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application>
<!-- Used to use Theme.NoDisplay but this is no longer the recommended
way and crashes in Android Marshmallow.
https://plus.google.com/105051985738280261832/posts/LjnRzJKWPGW -->
<activity android:name="CronetPerfTestActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,8 +0,0 @@
# Test Expectation file for javaperftests.
# tags: All Android_Svelte Android_Webview Android_but_not_webview Mac Win Linux
# tags: ChromeOS Android Desktop Mobile Nexus_5 Nexus_5X Nexus_6 Nexus_6P
# tags: Nexus_7 Cherry_Mobile_Android_One Mac_10.11 Mac_10.12 Nexus6_Webview
# tags: Nexus5X_Webview
# Benchmark: run.CronetPerfTestBenchmark

View File

@ -1,223 +0,0 @@
#!/usr/bin/env python
# Copyright 2015 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.
"""This script runs an automated Cronet performance benchmark.
This script:
1. Sets up "USB reverse tethering" which allow network traffic to flow from
an Android device connected to the host machine via a USB cable.
2. Starts HTTP and QUIC servers on the host machine.
3. Installs an Android app on the attached Android device and runs it.
4. Collects the results from the app.
Prerequisites:
1. A rooted (i.e. "adb root" succeeds) Android device connected via a USB cable
to the host machine (i.e. the computer running this script).
2. quic_server has been built for the host machine, e.g. via:
gn gen out/Release --args="is_debug=false"
ninja -C out/Release quic_server
3. cronet_perf_test_apk has been built for the Android device, e.g. via:
./components/cronet/tools/cr_cronet.py gn -r
ninja -C out/Release cronet_perf_test_apk
4. If "sudo ufw status" doesn't say "Status: inactive", run "sudo ufw disable".
5. sudo apt-get install lighttpd
6. If the usb0 interface on the host keeps losing it's IPv4 address
(WaitFor(HasHostAddress) will keep failing), NetworkManager may need to be
told to leave usb0 alone with these commands:
sudo bash -c "printf \"\\n[keyfile]\
\\nunmanaged-devices=interface-name:usb0\\n\" \
>> /etc/NetworkManager/NetworkManager.conf"
sudo service network-manager restart
Invocation:
./run.py
Output:
Benchmark timings are output by telemetry to stdout and written to
./results.html
"""
import json
import argparse
import os
import shutil
import sys
import tempfile
import time
import six.moves.urllib_parse # pylint: disable=import-error
REPOSITORY_ROOT = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..', '..', '..', '..', '..'))
sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools', 'perf'))
sys.path.append(os.path.join(REPOSITORY_ROOT, 'build', 'android'))
sys.path.append(os.path.join(REPOSITORY_ROOT, 'components'))
# pylint: disable=wrong-import-position
from chrome_telemetry_build import chromium_config
from devil.android import device_utils
from devil.android.sdk import intent
from core import benchmark_runner
from cronet.tools import android_rndis_forwarder
from cronet.tools import perf_test_utils
import lighttpd_server
from pylib import constants
from telemetry import android
from telemetry import benchmark
from telemetry import story as story_module
from telemetry.web_perf import timeline_based_measurement
# pylint: enable=wrong-import-position
# pylint: disable=super-with-arguments
def GetDevice():
devices = device_utils.DeviceUtils.HealthyDevices()
assert len(devices) == 1
return devices[0]
class CronetPerfTestAndroidStory(android.AndroidStory):
# Android AppStory implementation wrapping CronetPerfTest app.
# Launches Cronet perf test app and waits for execution to complete
# by waiting for presence of DONE_FILE.
def __init__(self, device):
self._device = device
config = perf_test_utils.GetConfig(device)
device.RemovePath(config['DONE_FILE'], force=True)
self.url ='http://dummy/?' + six.moves.urllib_parse.urlencode(config)
start_intent = intent.Intent(
package=perf_test_utils.APP_PACKAGE,
activity=perf_test_utils.APP_ACTIVITY,
action=perf_test_utils.APP_ACTION,
# |config| maps from configuration value names to the configured values.
# |config| is encoded as URL parameter names and values and passed to
# the Cronet perf test app via the Intent data field.
data=self.url,
extras=None,
category=None)
super(CronetPerfTestAndroidStory, self).__init__(
start_intent, name='CronetPerfTest',
# No reason to wait for app; Run() will wait for results. By default
# StartActivity will timeout waiting for CronetPerfTest, so override
# |is_app_ready_predicate| to not wait.
is_app_ready_predicate=lambda app: True)
def Run(self, shared_state):
while not self._device.FileExists(
perf_test_utils.GetConfig(self._device)['DONE_FILE']):
time.sleep(1.0)
class CronetPerfTestStorySet(story_module.StorySet):
def __init__(self, device):
super(CronetPerfTestStorySet, self).__init__()
# Create and add Cronet perf test AndroidStory.
self.AddStory(CronetPerfTestAndroidStory(device))
class CronetPerfTestMeasurement(
timeline_based_measurement.TimelineBasedMeasurement):
# For now AndroidStory's SharedAppState works only with
# TimelineBasedMeasurements, so implement one that just forwards results from
# Cronet perf test app.
def __init__(self, device, options):
super(CronetPerfTestMeasurement, self).__init__(options)
self._device = device
def WillRunStory(self, platform, story=None):
# Skip parent implementation which doesn't apply to Cronet perf test app as
# it is not a browser with a timeline interface.
pass
def Measure(self, platform, results):
# Reads results from |RESULTS_FILE| on target and adds to |results|.
jsonResults = json.loads(self._device.ReadFile(
perf_test_utils.GetConfig(self._device)['RESULTS_FILE']))
for test in jsonResults:
results.AddMeasurement(test, 'ms', jsonResults[test])
def DidRunStory(self, platform, results):
# Skip parent implementation which calls into tracing_controller which this
# doesn't have.
pass
class CronetPerfTestBenchmark(benchmark.Benchmark):
# Benchmark implementation spawning off Cronet perf test measurement and
# StorySet.
SUPPORTED_PLATFORMS = [story_module.expectations.ALL_ANDROID]
def __init__(self, max_failures=None):
super(CronetPerfTestBenchmark, self).__init__(max_failures)
self._device = GetDevice()
def CreatePageTest(self, options):
return CronetPerfTestMeasurement(self._device, options)
def CreateStorySet(self, options):
return CronetPerfTestStorySet(self._device)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--output-format', default='html',
help='The output format of the results file.')
parser.add_argument('--output-dir', default=None,
help='The directory for the output file. Default value is '
'the base directory of this script.')
args, _ = parser.parse_known_args()
constants.SetBuildType(perf_test_utils.BUILD_TYPE)
# Install APK
device = GetDevice()
device.EnableRoot()
device.Install(perf_test_utils.APP_APK)
# Start USB reverse tethering.
android_rndis_forwarder.AndroidRndisForwarder(device,
perf_test_utils.GetAndroidRndisConfig(device))
# Start HTTP server.
http_server_doc_root = perf_test_utils.GenerateHttpTestResources()
config_file = tempfile.NamedTemporaryFile()
http_server = lighttpd_server.LighttpdServer(http_server_doc_root,
port=perf_test_utils.HTTP_PORT,
base_config_path=config_file.name)
perf_test_utils.GenerateLighttpdConfig(config_file, http_server_doc_root,
http_server)
assert http_server.StartupHttpServer()
config_file.close()
# Start QUIC server.
quic_server_doc_root = perf_test_utils.GenerateQuicTestResources(device)
quic_server = perf_test_utils.QuicServer(quic_server_doc_root)
quic_server.StartupQuicServer(device)
# Launch Telemetry's benchmark_runner on CronetPerfTestBenchmark.
# By specifying this file's directory as the benchmark directory, it will
# allow benchmark_runner to in turn open this file up and find the
# CronetPerfTestBenchmark class to run the benchmark.
top_level_dir = os.path.dirname(os.path.realpath(__file__))
expectations_files = [os.path.join(top_level_dir, 'expectations.config')]
runner_config = chromium_config.ChromiumConfig(
top_level_dir=top_level_dir,
benchmark_dirs=[top_level_dir],
expectations_files=expectations_files)
sys.argv.insert(1, 'run')
sys.argv.insert(2, 'run.CronetPerfTestBenchmark')
sys.argv.insert(3, '--browser=android-system-chrome')
sys.argv.insert(4, '--output-format=' + args.output_format)
if args.output_dir:
sys.argv.insert(5, '--output-dir=' + args.output_dir)
benchmark_runner.main(runner_config)
# Shutdown.
quic_server.ShutdownQuicServer()
shutil.rmtree(quic_server_doc_root)
http_server.ShutdownHttpServer()
shutil.rmtree(http_server_doc_root)
if __name__ == '__main__':
main()

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2014 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.chromium.net.tests"
android:versionCode="1"
android:versionName="1.0"
tools:ignore="ScopedStorage">
<!-- tools:ignore="ScopedStorage" stops the linter from yelling at us for
bypassing Scoped Storage. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Starting from Android 11 not all packages are visible by default and this stops us
from starting an EmbeddedTestServer. Request visibility by using an intent
filter signature
(https://developer.android.com/training/package-visibility/declaring#intent-filter-signature)
-->
<queries>
<intent>
<action android:name="org.chromium.net.test.EMBEDDED_TEST_SERVER_SERVICE" />
</intent>
</queries>
<!-- Starting from Android 10, requestLegacyExternalStorage is required to
fetch test data, certificates, etc.. They currently reside in external
storage which is no longer accessible by default.
https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage
Once we start testing Android 11+, this "hack" might no longer be sufficient. -->
<application android:name="org.chromium.net.CronetTestApplication"
android:label="Cronet Test"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true" />
<!-- Does not use BaseChromiumAndroidJUnitRunner so that it's easy to run
tests when dropped into non-chromium repositories. -->
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="org.chromium.net.tests"
android:label="Tests for org.chromium.net" />
</manifest>

View File

@ -1,90 +0,0 @@
// Copyright 2015 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 <string>
#include <vector>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/test/test_support_android.h"
#include "components/cronet/android/cronet_tests_jni_headers/MockCertVerifier_jni.h"
#include "crypto/sha2.h"
#include "net/base/net_errors.h"
#include "net/cert/asn1_util.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/cert/x509_util.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_data_directory.h"
using base::android::JavaParamRef;
namespace cronet {
namespace {
// Populates |out_hash_value| with the SHA256 hash of the |cert| public key.
// Returns true on success.
static bool CalculatePublicKeySha256(const net::X509Certificate& cert,
net::HashValue* out_hash_value) {
// Extract the public key from the cert.
base::StringPiece spki_bytes;
if (!net::asn1::ExtractSPKIFromDERCert(
net::x509_util::CryptoBufferAsStringPiece(cert.cert_buffer()),
&spki_bytes)) {
LOG(INFO) << "Unable to retrieve the public key from the DER cert";
return false;
}
// Calculate SHA256 hash of public key bytes.
*out_hash_value = net::HashValue(net::HASH_VALUE_SHA256);
crypto::SHA256HashString(spki_bytes, out_hash_value->data(),
crypto::kSHA256Length);
return true;
}
} // namespace
static jlong JNI_MockCertVerifier_CreateMockCertVerifier(
JNIEnv* env,
const JavaParamRef<jobjectArray>& jcerts,
const jboolean jknown_root,
const JavaParamRef<jstring>& jtest_data_dir) {
base::FilePath test_data_dir(
base::android::ConvertJavaStringToUTF8(env, jtest_data_dir));
base::InitAndroidTestPaths(test_data_dir);
std::vector<std::string> certs;
base::android::AppendJavaStringArrayToStringVector(env, jcerts, &certs);
net::MockCertVerifier* mock_cert_verifier = new net::MockCertVerifier();
for (const auto& cert : certs) {
net::CertVerifyResult verify_result;
verify_result.verified_cert =
net::ImportCertFromFile(net::GetTestCertsDirectory(), cert);
// By default, HPKP verification is enabled for known trust roots only.
verify_result.is_issued_by_known_root = jknown_root;
// Calculate the public key hash and add it to the verify_result.
net::HashValue hashValue;
CHECK(CalculatePublicKeySha256(*verify_result.verified_cert.get(),
&hashValue));
verify_result.public_key_hashes.push_back(hashValue);
mock_cert_verifier->AddResultForCert(verify_result.verified_cert.get(),
verify_result, net::OK);
}
return reinterpret_cast<jlong>(mock_cert_verifier);
}
static jlong JNI_MockCertVerifier_CreateFreeForAllMockCertVerifier(
JNIEnv* env) {
net::MockCertVerifier* mock_cert_verifier = new net::MockCertVerifier();
mock_cert_verifier->set_default_result(net::OK);
return reinterpret_cast<jlong>(mock_cert_verifier);
}
} // namespace cronet

View File

@ -1,138 +0,0 @@
// Copyright 2014 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 "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/memory/raw_ptr.h"
#include "components/cronet/android/cronet_tests_jni_headers/MockUrlRequestJobFactory_jni.h"
#include "components/cronet/android/test/cronet_test_util.h"
#include "components/cronet/android/test/url_request_intercepting_job_factory.h"
#include "net/test/url_request/ssl_certificate_error_job.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "net/test/url_request/url_request_hanging_read_job.h"
#include "net/test/url_request/url_request_mock_data_job.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_filter.h"
#include "url/gurl.h"
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace cronet {
// Intercept URLRequestJob creation using URLRequestFilter from
// libcronet_tests.so
class UrlInterceptorJobFactoryHandle {
public:
// |jcontext_adapter| points to a URLRequestContextAdapater.
UrlInterceptorJobFactoryHandle(jlong jcontext_adapter)
: jcontext_adapter_(jcontext_adapter) {
TestUtil::RunAfterContextInit(
jcontext_adapter,
base::BindOnce(&UrlInterceptorJobFactoryHandle::InitOnNetworkThread,
base::Unretained(this)));
}
// Should only be called on network thread; other threads should use
// ShutDown().
~UrlInterceptorJobFactoryHandle() {
DCHECK(
TestUtil::GetTaskRunner(jcontext_adapter_)->BelongsToCurrentThread());
TestUtil::GetURLRequestContext(jcontext_adapter_)
->set_job_factory(old_job_factory_);
}
void ShutDown() {
TestUtil::RunAfterContextInit(
jcontext_adapter_,
base::BindOnce(&UrlInterceptorJobFactoryHandle::ShutdownOnNetworkThread,
base::Unretained(this)));
}
private:
void InitOnNetworkThread() {
net::URLRequestContext* request_context =
TestUtil::GetURLRequestContext(jcontext_adapter_);
old_job_factory_ = request_context->job_factory();
new_job_factory_.reset(new URLRequestInterceptingJobFactory(
const_cast<net::URLRequestJobFactory*>(old_job_factory_.get()),
net::URLRequestFilter::GetInstance()));
request_context->set_job_factory(new_job_factory_.get());
}
void ShutdownOnNetworkThread() { delete this; }
// The URLRequestContextAdapater this object intercepts from.
const jlong jcontext_adapter_;
// URLRequestJobFactory previously used in URLRequestContext.
raw_ptr<const net::URLRequestJobFactory> old_job_factory_;
// URLRequestJobFactory inserted during tests to intercept URLRequests with
// libcronet's URLRequestFilter.
std::unique_ptr<URLRequestInterceptingJobFactory> new_job_factory_;
};
// URL interceptors are registered with the URLRequestFilter in
// libcronet_tests.so. However tests are run on libcronet.so. Use the
// URLRequestFilter in libcronet_tests.so with the URLRequestContext in
// libcronet.so by installing a URLRequestInterceptingJobFactory
// that calls into libcronet_tests.so's URLRequestFilter.
jlong JNI_MockUrlRequestJobFactory_AddUrlInterceptors(
JNIEnv* env,
jlong jcontext_adapter) {
net::URLRequestMockDataJob::AddUrlHandler();
net::URLRequestFailedJob::AddUrlHandler();
net::URLRequestHangingReadJob::AddUrlHandler();
net::SSLCertificateErrorJob::AddUrlHandler();
return reinterpret_cast<jlong>(
new UrlInterceptorJobFactoryHandle(jcontext_adapter));
}
// Put back the old URLRequestJobFactory into the URLRequestContext.
void JNI_MockUrlRequestJobFactory_RemoveUrlInterceptorJobFactory(
JNIEnv* env,
jlong jinterceptor_handle) {
reinterpret_cast<UrlInterceptorJobFactoryHandle*>(jinterceptor_handle)
->ShutDown();
}
ScopedJavaLocalRef<jstring> JNI_MockUrlRequestJobFactory_GetMockUrlWithFailure(
JNIEnv* jenv,
jint jphase,
jint jnet_error) {
GURL url(net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase(
static_cast<net::URLRequestFailedJob::FailurePhase>(jphase),
static_cast<int>(jnet_error)));
return base::android::ConvertUTF8ToJavaString(jenv, url.spec());
}
ScopedJavaLocalRef<jstring> JNI_MockUrlRequestJobFactory_GetMockUrlForData(
JNIEnv* jenv,
const JavaParamRef<jstring>& jdata,
jint jdata_repeat_count) {
std::string data(base::android::ConvertJavaStringToUTF8(jenv, jdata));
GURL url(net::URLRequestMockDataJob::GetMockHttpUrl(data,
jdata_repeat_count));
return base::android::ConvertUTF8ToJavaString(jenv, url.spec());
}
ScopedJavaLocalRef<jstring>
JNI_MockUrlRequestJobFactory_GetMockUrlForSSLCertificateError(JNIEnv* jenv) {
GURL url(net::SSLCertificateErrorJob::GetMockUrl());
return base::android::ConvertUTF8ToJavaString(jenv, url.spec());
}
ScopedJavaLocalRef<jstring>
JNI_MockUrlRequestJobFactory_GetMockUrlForClientCertificateRequest(
JNIEnv* jenv) {
GURL url(net::URLRequestMockDataJob::GetMockUrlForClientCertificateRequest());
return base::android::ConvertUTF8ToJavaString(jenv, url.spec());
}
ScopedJavaLocalRef<jstring>
JNI_MockUrlRequestJobFactory_GetMockUrlForHangingRead(JNIEnv* jenv) {
GURL url(net::URLRequestHangingReadJob::GetMockHttpUrl());
return base::android::ConvertUTF8ToJavaString(jenv, url.spec());
}
} // namespace cronet

View File

@ -1,91 +0,0 @@
// Copyright 2014 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 <memory>
#include <string>
#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/files/file_path.h"
#include "base/test/test_support_android.h"
#include "components/cronet/android/cronet_tests_jni_headers/NativeTestServer_jni.h"
#include "components/cronet/testing/test_server/test_server.h"
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace cronet {
jboolean JNI_NativeTestServer_StartNativeTestServer(
JNIEnv* env,
const JavaParamRef<jstring>& jtest_files_root,
const JavaParamRef<jstring>& jtest_data_dir) {
base::FilePath test_data_dir(
base::android::ConvertJavaStringToUTF8(env, jtest_data_dir));
base::InitAndroidTestPaths(test_data_dir);
base::FilePath test_files_root(
base::android::ConvertJavaStringToUTF8(env, jtest_files_root));
return cronet::TestServer::StartServeFilesFromDirectory(test_files_root);
}
void JNI_NativeTestServer_ShutdownNativeTestServer(JNIEnv* env) {
cronet::TestServer::Shutdown();
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetEchoBodyURL(JNIEnv* env) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetEchoRequestBodyURL());
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetEchoHeaderURL(
JNIEnv* env,
const JavaParamRef<jstring>& jheader) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetEchoHeaderURL(
base::android::ConvertJavaStringToUTF8(env, jheader)));
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetEchoAllHeadersURL(
JNIEnv* env) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetEchoAllHeadersURL());
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetEchoMethodURL(JNIEnv* env) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetEchoMethodURL());
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetRedirectToEchoBody(
JNIEnv* env) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetRedirectToEchoBodyURL());
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetFileURL(
JNIEnv* env,
const JavaParamRef<jstring>& jfile_path) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetFileURL(
base::android::ConvertJavaStringToUTF8(env, jfile_path)));
}
jint JNI_NativeTestServer_GetPort(JNIEnv* env) {
return cronet::TestServer::GetPort();
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetExabyteResponseURL(
JNIEnv* env) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetExabyteResponseURL());
}
ScopedJavaLocalRef<jstring> JNI_NativeTestServer_GetHostPort(JNIEnv* env) {
return base::android::ConvertUTF8ToJavaString(
env, cronet::TestServer::GetHostPort());
}
} // namespace cronet

View File

@ -1,29 +0,0 @@
# Proguard configuration that is common for all type of tests.
-keepattributes Signature,InnerClasses,SourceFile,LineNumberTable,EnclosingMethod
-dontwarn io.netty.**
-keep class io.netty.** { *; }
# Keep ChromiumNativeTestSupport & ChromiumPlatformOnlyTestSupport since they are
# instantiated through Reflection by the smoke tests.
-keep class org.chromium.net.smoke.ChromiumNativeTestSupport
-keep class org.chromium.net.smoke.ChromiumPlatformOnlyTestSupport
# https://android.googlesource.com/platform/sdk/+/marshmallow-mr1-release/files/proguard-android.txt#54
-dontwarn android.support.**
# Do not obfuscate this class for testing since some of the tests check the class
# name in order to check that an instantiated engine is the Java one.
-keepnames class org.chromium.net.impl.JavaCronetEngine
# These classes should be explicitly kept to avoid failure if
# class/merging/horizontal proguard optimization is enabled.
# NOTE: make sure that only test classes are added to this list.
-keep class org.chromium.base.test.** {
*;
}
-keep class org.chromium.net.TestFilesInstaller
-keep class org.chromium.net.MetricsTestUtil
# Generated for chrome apk and not included into cronet.
-dontwarn org.chromium.build.NativeLibraries

View File

@ -1,113 +0,0 @@
// Copyright 2015 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 "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/path_utils.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/message_loop/message_pump_type.h"
#include "base/test/test_support_android.h"
#include "base/threading/thread.h"
#include "components/cronet/android/cronet_tests_jni_headers/QuicTestServer_jni.h"
#include "components/cronet/android/test/cronet_test_util.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/quic/crypto/proof_source_chromium.h"
#include "net/test/test_data_directory.h"
#include "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h"
#include "net/tools/quic/quic_simple_server.h"
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace cronet {
namespace {
static const int kServerPort = 6121;
base::Thread* g_quic_server_thread = nullptr;
quic::QuicMemoryCacheBackend* g_quic_memory_cache_backend = nullptr;
net::QuicSimpleServer* g_quic_server = nullptr;
void StartOnServerThread(const base::FilePath& test_files_root,
const base::FilePath& test_data_dir) {
DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread());
DCHECK(!g_quic_server);
// Set up in-memory cache.
base::FilePath file_dir = test_files_root.Append("quic_data");
CHECK(base::PathExists(file_dir)) << "Quic data does not exist";
g_quic_memory_cache_backend = new quic::QuicMemoryCacheBackend();
g_quic_memory_cache_backend->InitializeBackend(file_dir.value());
quic::QuicConfig config;
// Set up server certs.
base::FilePath directory = test_data_dir.Append("net/data/ssl/certificates");
std::unique_ptr<net::ProofSourceChromium> proof_source(
new net::ProofSourceChromium());
CHECK(proof_source->Initialize(
directory.Append("quic-chain.pem"),
directory.Append("quic-leaf-cert.key"),
base::FilePath()));
g_quic_server = new net::QuicSimpleServer(
std::move(proof_source), config,
quic::QuicCryptoServerConfig::ConfigOptions(),
quic::AllSupportedVersions(), g_quic_memory_cache_backend);
// Start listening.
int rv = g_quic_server->Listen(
net::IPEndPoint(net::IPAddress::IPv4AllZeros(), kServerPort));
CHECK_GE(rv, 0) << "Quic server fails to start";
JNIEnv* env = base::android::AttachCurrentThread();
Java_QuicTestServer_onServerStarted(env);
}
void ShutdownOnServerThread() {
DCHECK(g_quic_server_thread->task_runner()->BelongsToCurrentThread());
g_quic_server->Shutdown();
delete g_quic_server;
delete g_quic_memory_cache_backend;
}
} // namespace
// Quic server is currently hardcoded to run on port 6121 of the localhost on
// the device.
void JNI_QuicTestServer_StartQuicTestServer(
JNIEnv* env,
const JavaParamRef<jstring>& jtest_files_root,
const JavaParamRef<jstring>& jtest_data_dir) {
DCHECK(!g_quic_server_thread);
base::FilePath test_data_dir(
base::android::ConvertJavaStringToUTF8(env, jtest_data_dir));
base::InitAndroidTestPaths(test_data_dir);
g_quic_server_thread = new base::Thread("quic server thread");
base::Thread::Options thread_options;
thread_options.message_pump_type = base::MessagePumpType::IO;
bool started =
g_quic_server_thread->StartWithOptions(std::move(thread_options));
DCHECK(started);
base::FilePath test_files_root(
base::android::ConvertJavaStringToUTF8(env, jtest_files_root));
g_quic_server_thread->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&StartOnServerThread, test_files_root, test_data_dir));
}
void JNI_QuicTestServer_ShutdownQuicTestServer(JNIEnv* env) {
DCHECK(!g_quic_server_thread->task_runner()->BelongsToCurrentThread());
g_quic_server_thread->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&ShutdownOnServerThread));
delete g_quic_server_thread;
}
int JNI_QuicTestServer_GetServerPort(JNIEnv* env) {
return kServerPort;
}
} // namespace cronet

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2014 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.
-->
<resources>
</resources>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 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.
-->
<network-security-config>
<!-- Since Android N (API 24?) it is possible to disable cleartext support.
This is required to test that we are correctly handling this feature.
-->
<domain-config cleartextTrafficPermitted="false">
<!-- Used by CronetUrlRequestTest#testCleartextTrafficBlocked -->
<domain includeSubdomains="true">example.com</domain>
</domain-config>
<!-- Since Android 9 (API 28) cleartext support is disabled by default, this
causes some of our tests to fail (see crbug/1220357).
The following configs allow http requests for the domains used in these
tests.
TODO(stefanoduo): Figure out if we really need to use http for these tests
-->
<domain-config cleartextTrafficPermitted="true">
<!-- Used as the base URL by native test server (net::EmbeddedTestServer) -->
<domain includeSubdomains="true">127.0.0.1</domain>
<!-- Used by CronetHttpURLConnectionTest#testIOExceptionInterruptRethrown -->
<domain includeSubdomains="true">localhost</domain>
<!-- Used by CronetHttpURLConnectionTest#testBadIP -->
<domain includeSubdomains="true">0.0.0.0</domain>
<!-- Used by CronetHttpURLConnectionTest#testSetUseCachesFalse -->
<domain includeSubdomains="true">host-cache-test-host</domain>
<!-- Used by CronetHttpURLConnectionTest#testBadHostname -->
<domain includeSubdomains="true">this-weird-host-name-does-not-exist</domain>
<!-- Used by CronetUrlRequestContextTest#testHostResolverRules -->
<domain includeSubdomains="true">some-weird-hostname</domain>
</domain-config>
</network-security-config>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 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.
-->
<resources>
<string name="TestSupportImplClass">org.chromium.net.smoke.ChromiumNativeTestSupport</string>
</resources>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 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.
-->
<resources>
<string name="TestSupportImplClass">org.chromium.net.smoke.ChromiumPlatformOnlyTestSupport</string>
</resources>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 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.
-->
<network-security-config>
<!-- See components/cronet/android/test/res/xml/network_security_config.xml
as to why this is needed. -->
<domain-config cleartextTrafficPermitted="true">
<!-- Used by PlatformOnlyEngineTest#testSuccessfulResponse -->
<domain includeSubdomains="true">127.0.0.1</domain>
</domain-config>
</network-security-config>

View File

@ -1,194 +0,0 @@
// Copyright 2015 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 "components/cronet/android/test/test_upload_data_stream_handler.h"
#include <stddef.h>
#include <string>
#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "components/cronet/android/cronet_tests_jni_headers/TestUploadDataStreamHandler_jni.h"
#include "components/cronet/android/test/cronet_test_util.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_with_source.h"
using base::android::JavaParamRef;
namespace cronet {
static const size_t kReadBufferSize = 32768;
TestUploadDataStreamHandler::TestUploadDataStreamHandler(
std::unique_ptr<net::UploadDataStream> upload_data_stream,
JNIEnv* env,
jobject jtest_upload_data_stream_handler,
jlong jcontext_adapter)
: init_callback_invoked_(false),
read_callback_invoked_(false),
bytes_read_(0),
network_thread_(TestUtil::GetTaskRunner(jcontext_adapter)) {
upload_data_stream_ = std::move(upload_data_stream);
jtest_upload_data_stream_handler_.Reset(env,
jtest_upload_data_stream_handler);
}
TestUploadDataStreamHandler::~TestUploadDataStreamHandler() {
}
void TestUploadDataStreamHandler::Destroy(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
DCHECK(!network_thread_->BelongsToCurrentThread());
network_thread_->DeleteSoon(FROM_HERE, this);
}
void TestUploadDataStreamHandler::OnInitCompleted(int res) {
DCHECK(network_thread_->BelongsToCurrentThread());
init_callback_invoked_ = true;
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_TestUploadDataStreamHandler_onInitCompleted(
env, jtest_upload_data_stream_handler_, res);
}
void TestUploadDataStreamHandler::OnReadCompleted(int res) {
DCHECK(network_thread_->BelongsToCurrentThread());
read_callback_invoked_ = true;
bytes_read_ = res;
NotifyJavaReadCompleted();
}
void TestUploadDataStreamHandler::Init(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
DCHECK(!network_thread_->BelongsToCurrentThread());
network_thread_->PostTask(
FROM_HERE,
base::BindOnce(&TestUploadDataStreamHandler::InitOnNetworkThread,
base::Unretained(this)));
}
void TestUploadDataStreamHandler::Read(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
DCHECK(!network_thread_->BelongsToCurrentThread());
network_thread_->PostTask(
FROM_HERE,
base::BindOnce(&TestUploadDataStreamHandler::ReadOnNetworkThread,
base::Unretained(this)));
}
void TestUploadDataStreamHandler::Reset(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
DCHECK(!network_thread_->BelongsToCurrentThread());
network_thread_->PostTask(
FROM_HERE,
base::BindOnce(&TestUploadDataStreamHandler::ResetOnNetworkThread,
base::Unretained(this)));
}
void TestUploadDataStreamHandler::CheckInitCallbackNotInvoked(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
DCHECK(!network_thread_->BelongsToCurrentThread());
network_thread_->PostTask(
FROM_HERE, base::BindOnce(&TestUploadDataStreamHandler::
CheckInitCallbackNotInvokedOnNetworkThread,
base::Unretained(this)));
}
void TestUploadDataStreamHandler::CheckReadCallbackNotInvoked(
JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
DCHECK(!network_thread_->BelongsToCurrentThread());
network_thread_->PostTask(
FROM_HERE, base::BindOnce(&TestUploadDataStreamHandler::
CheckReadCallbackNotInvokedOnNetworkThread,
base::Unretained(this)));
}
void TestUploadDataStreamHandler::InitOnNetworkThread() {
DCHECK(network_thread_->BelongsToCurrentThread());
init_callback_invoked_ = false;
read_buffer_ = nullptr;
bytes_read_ = 0;
int res = upload_data_stream_->Init(
base::BindOnce(&TestUploadDataStreamHandler::OnInitCompleted,
base::Unretained(this)),
net::NetLogWithSource());
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_TestUploadDataStreamHandler_onInitCalled(
env, jtest_upload_data_stream_handler_, res);
if (res == net::OK) {
cronet::Java_TestUploadDataStreamHandler_onInitCompleted(
env, jtest_upload_data_stream_handler_, res);
}
}
void TestUploadDataStreamHandler::ReadOnNetworkThread() {
DCHECK(network_thread_->BelongsToCurrentThread());
read_callback_invoked_ = false;
if (!read_buffer_.get())
read_buffer_ = base::MakeRefCounted<net::IOBufferWithSize>(kReadBufferSize);
int bytes_read = upload_data_stream_->Read(
read_buffer_.get(), kReadBufferSize,
base::BindOnce(&TestUploadDataStreamHandler::OnReadCompleted,
base::Unretained(this)));
if (bytes_read == net::OK) {
bytes_read_ = bytes_read;
NotifyJavaReadCompleted();
}
}
void TestUploadDataStreamHandler::ResetOnNetworkThread() {
DCHECK(network_thread_->BelongsToCurrentThread());
read_buffer_ = nullptr;
bytes_read_ = 0;
upload_data_stream_->Reset();
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_TestUploadDataStreamHandler_onResetCompleted(
env, jtest_upload_data_stream_handler_);
}
void TestUploadDataStreamHandler::CheckInitCallbackNotInvokedOnNetworkThread() {
DCHECK(network_thread_->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_TestUploadDataStreamHandler_onCheckInitCallbackNotInvoked(
env, jtest_upload_data_stream_handler_, !init_callback_invoked_);
}
void TestUploadDataStreamHandler::CheckReadCallbackNotInvokedOnNetworkThread() {
DCHECK(network_thread_->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
cronet::Java_TestUploadDataStreamHandler_onCheckReadCallbackNotInvoked(
env, jtest_upload_data_stream_handler_, !read_callback_invoked_);
}
void TestUploadDataStreamHandler::NotifyJavaReadCompleted() {
DCHECK(network_thread_->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
std::string data_read = "";
if (read_buffer_.get() && bytes_read_ > 0)
data_read = std::string(read_buffer_->data(), bytes_read_);
cronet::Java_TestUploadDataStreamHandler_onReadCompleted(
env, jtest_upload_data_stream_handler_, bytes_read_,
base::android::ConvertUTF8ToJavaString(env, data_read));
}
static jlong JNI_TestUploadDataStreamHandler_CreateTestUploadDataStreamHandler(
JNIEnv* env,
const JavaParamRef<jobject>& jtest_upload_data_stream_handler,
jlong jupload_data_stream,
jlong jcontext_adapter) {
std::unique_ptr<net::UploadDataStream> upload_data_stream(
reinterpret_cast<net::UploadDataStream*>(jupload_data_stream));
TestUploadDataStreamHandler* handler = new TestUploadDataStreamHandler(
std::move(upload_data_stream), env, jtest_upload_data_stream_handler,
jcontext_adapter);
return reinterpret_cast<jlong>(handler);
}
} // namespace cronet

View File

@ -1,105 +0,0 @@
// Copyright 2015 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.
#ifndef COMPONENTS_CRONET_ANDROID_TEST_TEST_UPLOAD_DATA_STREAM_HANDLER_H_
#define COMPONENTS_CRONET_ANDROID_TEST_TEST_UPLOAD_DATA_STREAM_HANDLER_H_
#include <jni.h>
#include <memory>
#include "base/android/scoped_java_ref.h"
#include "base/memory/ref_counted.h"
#include "base/task/single_thread_task_runner.h"
#include "net/base/io_buffer.h"
#include "net/base/upload_data_stream.h"
namespace cronet {
/**
* This class allows a net::UploadDataStream to be driven directly from
* Java, for use in tests.
*/
class TestUploadDataStreamHandler {
public:
TestUploadDataStreamHandler(
std::unique_ptr<net::UploadDataStream> upload_data_stream,
JNIEnv* env,
jobject jtest_upload_data_stream_handler,
jlong jcontext_adapter);
TestUploadDataStreamHandler(const TestUploadDataStreamHandler&) = delete;
TestUploadDataStreamHandler& operator=(const TestUploadDataStreamHandler&) =
delete;
~TestUploadDataStreamHandler();
// Destroys |network_thread_| created by this class.
void Destroy(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Posts a task to |network_thread_| to call the corresponding method of
// net::UploadDataStream on |upload_data_stream_|.
void Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller);
void Read(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller);
void Reset(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller);
// Posts a task to |network_thread_| to check whether init complete callback
// has been invoked by net::UploadDataStream asynchronously, and notifies the
// Java side of the result.
void CheckInitCallbackNotInvoked(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
// Posts a task to |network_thread_| to check whether read complete callback
// has been invoked by net::UploadDataStream asynchronously, and notifies the
// Java side of the result.
void CheckReadCallbackNotInvoked(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
private:
// Complete callbacks that are passed to the |upload_data_stream_|.
void OnInitCompleted(int res);
void OnReadCompleted(int res);
// Helper methods that run corresponding task on |network_thread_|.
void InitOnNetworkThread();
void ReadOnNetworkThread();
void ResetOnNetworkThread();
void CheckInitCallbackNotInvokedOnNetworkThread();
void CheckReadCallbackNotInvokedOnNetworkThread();
// Notify the Java TestUploadDataStreamHandler that read has completed.
void NotifyJavaReadCompleted();
// True if |OnInitCompleted| callback has been invoked. It is set to false
// when init or reset is called again. Created on a Java thread, but is only
// accessed from |network_thread_|.
bool init_callback_invoked_;
// True if |OnReadCompleted| callback has been invoked. It is set to false
// when init or reset is called again. Created on a Java thread, but is only
// accessed from |network_thread_|.
bool read_callback_invoked_;
// Indicates the number of bytes read. It is reset to 0 when init, reset, or
// read is called again. Created on a Java thread, but is only accessed from
// |network_thread_|.
int bytes_read_;
// Created and destroyed on the same Java thread. This is where methods of
// net::UploadDataStream run on.
scoped_refptr<base::SingleThreadTaskRunner> network_thread_;
// Created on a Java thread. Accessed only on |network_thread_|.
std::unique_ptr<net::UploadDataStream> upload_data_stream_;
// Created and accessed only on |network_thread_|.
scoped_refptr<net::IOBufferWithSize> read_buffer_;
// A Java reference pointer for calling methods on the Java
// TestUploadDataStreamHandler object. Initialized during construction.
base::android::ScopedJavaGlobalRef<jobject> jtest_upload_data_stream_handler_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_TEST_TEST_UPLOAD_DATA_STREAM_HANDLER_H_

View File

@ -1,38 +0,0 @@
// Copyright 2014 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 "components/cronet/android/test/url_request_intercepting_job_factory.h"
#include <utility>
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "net/url_request/url_request_interceptor.h"
#include "net/url_request/url_request_job.h"
namespace cronet {
URLRequestInterceptingJobFactory::URLRequestInterceptingJobFactory(
net::URLRequestJobFactory* job_factory,
net::URLRequestInterceptor* interceptor)
: job_factory_(job_factory), interceptor_(interceptor) {}
URLRequestInterceptingJobFactory::~URLRequestInterceptingJobFactory() = default;
std::unique_ptr<net::URLRequestJob> URLRequestInterceptingJobFactory::CreateJob(
net::URLRequest* request) const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::unique_ptr<net::URLRequestJob> job =
interceptor_->MaybeInterceptRequest(request);
if (job)
return job;
return job_factory_->CreateJob(request);
}
bool URLRequestInterceptingJobFactory::IsSafeRedirectTarget(
const GURL& location) const {
return job_factory_->IsSafeRedirectTarget(location);
}
} // namespace cronet

View File

@ -1,57 +0,0 @@
// Copyright 2014 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.
#ifndef COMPONENTS_CRONET_ANDROID_TEST_URL_REQUEST_INTERCEPTING_JOB_FACTORY_H_
#define COMPONENTS_CRONET_ANDROID_TEST_URL_REQUEST_INTERCEPTING_JOB_FACTORY_H_
#include <memory>
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "net/url_request/url_request_job_factory.h"
class GURL;
namespace net {
class URLRequest;
class URLRequestJob;
class URLRequestInterceptor;
} // namespace net
namespace cronet {
// This class acts as a wrapper for URLRequestJobFactory. The
// URLRequestInteceptor is given the option of creating a URLRequestJob for each
// URLRequest. If the interceptor does not create a job, the URLRequest is
// forwarded to the wrapped URLRequestJobFactory instead.
//
// This class is only intended for use in intercepting requests before they
// are passed on to their default ProtocolHandler. Each supported scheme should
// have its own ProtocolHandler.
class URLRequestInterceptingJobFactory : public net::URLRequestJobFactory {
public:
// Does not take ownership of |job_factory| and |interceptor|.
URLRequestInterceptingJobFactory(net::URLRequestJobFactory* job_factory,
net::URLRequestInterceptor* interceptor);
URLRequestInterceptingJobFactory(const URLRequestInterceptingJobFactory&) =
delete;
URLRequestInterceptingJobFactory& operator=(
const URLRequestInterceptingJobFactory&) = delete;
~URLRequestInterceptingJobFactory() override;
// URLRequestJobFactory implementation
std::unique_ptr<net::URLRequestJob> CreateJob(
net::URLRequest* request) const override;
bool IsSafeRedirectTarget(const GURL& location) const override;
private:
const raw_ptr<net::URLRequestJobFactory> job_factory_;
const raw_ptr<net::URLRequestInterceptor> interceptor_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_TEST_URL_REQUEST_INTERCEPTING_JOB_FACTORY_H_

View File

@ -1,128 +0,0 @@
# Testing and debugging Cronet for Android
[TOC]
## Checkout and build
See instructions in the [common checkout and
build](/components/cronet/build_instructions.md).
## Running tests locally
First, connect an Android device by following the [Plug in your Android
device](/docs/android_build_instructions.md#Plug-in-your-Android-device)
steps. Prefer using a device running a userdebug build.
Alternatively, you can pass the --x86 flag to `gn` to test on a local emulator
-- make sure you substitute `out/Debug` for `out/Debug-x86` in the instructions
below.
### Running Cronet Java unit tests
To run Java unit tests that actuate the Cronet API:
```shell
$ ./components/cronet/tools/cr_cronet.py gn
$ ./components/cronet/tools/cr_cronet.py build-test
```
To run particular tests specify the test class and method name to the build-test
command. For example:
```shell
$ ./components/cronet/tools/cr_cronet.py build-test -f QuicTest#testQuicLoadUrl
```
### Running net_unittests and cronet_unittests_android
To run C++ and Java unit tests of net/ functionality:
```shell
$ ./components/cronet/tools/cr_cronet.py gn
$ ninja -C out/Debug net_unittests
$ ./out/Debug/bin/run_net_unittests --fast-local-dev
```
For more information about running net_unittests, read
[Android Test Instructions](/docs/testing/android_test_instructions.md).
There are a small number of C++ Cronet unit tests, called
cronet_unittests_android, that can be run by following the above instructions
and substituting cronet_unittests_android for net_unittests.
### Running Cronet performance tests
To run Cronet's perf tests, follow the instructions in
[components/cronet/android/test/javaperftests/run.py](test/javaperftests/run.py)
## Running tests remotely
Once you've uploaded a Chromium change list using `git cl upload`, you can
launch a bot to build and test your change list:
```shell
$ git cl try -B luci.chromium.try -b android-cronet-x86-dbg-10-tests
```
This will run both the Cronet Java unit tests and net_unittests.
## Debugging
### Debug Log
Messages from native (C++) code appear in the Android system log accessible with
`adb logcat`. By default you will see only messages designated as FATAL. To
enable more verbosity:
#### See VLOG(1) and VLOG(2) logging:
```shell
$ adb shell setprop log.tag.CronetUrlRequestContext VERBOSE
```
#### See VLOG(1) logging:
```shell
$ adb shell setprop log.tag.CronetUrlRequestContext DEBUG
```
#### See NO (only FATAL) logging:
```shell
$ adb shell setprop log.tag.CronetUrlRequestContext NONE
```
### Network Log
NetLog is Chromium's network logging system. To create a NetLog dump, you can
use the following pair of methods:
```
CronetEngine.startNetLogToFile()
CronetEngine.stopNetLog()
```
Unlike the Android system log which is line-based, the Chromium log is formatted
in JSON. As such, it will probably not be well-formed until you have called the
`stopNetLog()` method, as filesystem buffers will not have been flushed.
Retrieve the file from your device's file system, and import it to chrome
browser at chrome://net-internals/#import, or
http://catapult-project.github.io/catapult/netlog_viewer which helps to
visualize the data.
### Symbolicating crash stacks
If an app or test using Cronet crashes it can be useful to know the functions
and line numbers involved in the stack trace. This can be done using the
Android system log:
```shell
$ ./components/cronet/tools/cr_cronet.py stack
```
Or using tombstones left behind after crashes:
```shell
$ CHROMIUM_OUTPUT_DIR=out/Debug ./build/android/tombstones.py
```

View File

@ -1,38 +0,0 @@
// Copyright 2016 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 "components/cronet/android/url_request_error.h"
#include "net/base/net_errors.h"
namespace cronet {
UrlRequestError NetErrorToUrlRequestError(int net_error) {
switch (net_error) {
case net::ERR_NAME_NOT_RESOLVED:
return HOSTNAME_NOT_RESOLVED;
case net::ERR_INTERNET_DISCONNECTED:
return INTERNET_DISCONNECTED;
case net::ERR_NETWORK_CHANGED:
return NETWORK_CHANGED;
case net::ERR_TIMED_OUT:
return TIMED_OUT;
case net::ERR_CONNECTION_CLOSED:
return CONNECTION_CLOSED;
case net::ERR_CONNECTION_TIMED_OUT:
return CONNECTION_TIMED_OUT;
case net::ERR_CONNECTION_REFUSED:
return CONNECTION_REFUSED;
case net::ERR_CONNECTION_RESET:
return CONNECTION_RESET;
case net::ERR_ADDRESS_UNREACHABLE:
return ADDRESS_UNREACHABLE;
case net::ERR_QUIC_PROTOCOL_ERROR:
return QUIC_PROTOCOL_FAILED;
default:
return OTHER;
}
}
} // namespace cronet

View File

@ -1,34 +0,0 @@
// Copyright 2016 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.
#ifndef COMPONENTS_CRONET_ANDROID_URL_REQUEST_ERROR_H_
#define COMPONENTS_CRONET_ANDROID_URL_REQUEST_ERROR_H_
namespace cronet {
// Error codes for the most popular network stack error codes.
// For descriptions see corresponding constants in UrlRequestException.java.
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net.impl
enum UrlRequestError {
LISTENER_EXCEPTION_THROWN,
HOSTNAME_NOT_RESOLVED,
INTERNET_DISCONNECTED,
NETWORK_CHANGED,
TIMED_OUT,
CONNECTION_CLOSED,
CONNECTION_TIMED_OUT,
CONNECTION_REFUSED,
CONNECTION_RESET,
ADDRESS_UNREACHABLE,
QUIC_PROTOCOL_FAILED,
OTHER,
};
// Converts most popular net::ERR_* values to counterparts accessible in Java.
UrlRequestError NetErrorToUrlRequestError(int net_error);
} // namespace cronet
#endif // COMPONENTS_CRONET_ANDROID_URL_REQUEST_ERROR_H_

View File

@ -1,75 +0,0 @@
# Cronet build instructions
[TOC]
## Checking out the code
Follow all the
[Get the Code](https://www.chromium.org/developers/how-tos/get-the-code)
instructions for your target platform up to and including running hooks.
## Building Cronet for development and debugging
To build Cronet for development and debugging purposes:
First, `gn` is used to create ninja files targeting the intended platform, then
`ninja` executes the ninja files to run the build.
### Android / iOS builds
```shell
$ ./components/cronet/tools/cr_cronet.py gn --out_dir=out/Cronet
```
If the build host is Linux, Android binaries will be built. If the build host is
macOS, iOS binaries will be built.
Note: these commands clobber output of previously executed gn commands in
`out/Cronet`. If `--out_dir` is left out, the output directory defaults to
`out/Debug` for debug builds and `out/Release` for release builds (see below).
If `--x86` option is specified, then a native library is built for Intel x86
architecture, and the output directory defaults to `out/Debug-x86` if
unspecified. This can be useful for running on mobile emulators.
### Desktop builds (targets the current OS)
TODO(caraitto): Specify how to target Chrome OS and Fuchsia.
```shell
gn gen out/Cronet
```
### Running the ninja files
Now, use the generated ninja files to execute the build against the
`cronet_package` build target:
```shell
$ ninja -C out/Cronet cronet_package
```
## Building Cronet mobile for releases
To build Cronet with optimizations and with debug information stripped out:
```shell
$ ./components/cronet/tools/cr_cronet.py gn --release
$ ninja -C out/Release cronet_package
```
Note: these commands clobber output of previously executed gn commands in
`out/Release`.
## Building for other architectures
By default ARMv7 32-bit executables are generated. To generate executables
targeting other architectures modify [cr_cronet.py](tools/cr_cronet.py)'s
`gn_args` variable to include:
* For ARMv8 64-bit: `target_cpu="arm64"`
* For x86 32-bit: `target_cpu="x86"`
* For x86 64-bit: `target_cpu="x64"`
Alternatively you can run `gn args {out_dir}` and modify arguments in the editor
that comes up. This has advantage of not changing `cr_cronet.py`.

View File

@ -1,895 +0,0 @@
// Copyright 2014 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 "components/cronet/cronet_context.h"
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/statistics_recorder.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/cronet/cronet_global_state.h"
#include "components/cronet/cronet_prefs_manager.h"
#include "components/cronet/host_cache_persistence_manager.h"
#include "components/cronet/url_request_context_config.h"
#include "net/base/ip_address.h"
#include "net/base/load_flags.h"
#include "net/base/logging_network_change_observer.h"
#include "net/base/net_errors.h"
#include "net/base/network_delegate_impl.h"
#include "net/base/network_isolation_key.h"
#include "net/base/url_util.h"
#include "net/cert/caching_cert_verifier.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/x509_certificate.h"
#include "net/cookies/cookie_monster.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/transport_security_state.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_util.h"
#include "net/net_buildflags.h"
#include "net/nqe/network_quality_estimator_params.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/proxy_resolution/proxy_config.h"
#include "net/proxy_resolution/proxy_config_service_fixed.h"
#include "net/proxy_resolution/proxy_config_with_annotation.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_interceptor.h"
#if BUILDFLAG(ENABLE_REPORTING)
#include "net/network_error_logging/network_error_logging_service.h"
#include "net/reporting/reporting_service.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
namespace {
// This class wraps a NetLog that also contains network change events.
class NetLogWithNetworkChangeEvents {
public:
NetLogWithNetworkChangeEvents() : net_log_(net::NetLog::Get()) {}
NetLogWithNetworkChangeEvents(const NetLogWithNetworkChangeEvents&) = delete;
NetLogWithNetworkChangeEvents& operator=(
const NetLogWithNetworkChangeEvents&) = delete;
net::NetLog* net_log() { return net_log_; }
// This function registers with the NetworkChangeNotifier and so must be
// called *after* the NetworkChangeNotifier is created. Should only be
// called on the init thread as it is not thread-safe and the init thread is
// the thread the NetworkChangeNotifier is created on. This function is
// not thread-safe because accesses to |net_change_logger_| are not atomic.
// There might be multiple CronetEngines each with a network thread so
// so the init thread is used. |g_net_log_| also outlives the network threads
// so it would be unsafe to receive callbacks on the network threads without
// a complicated thread-safe reference-counting system to control callback
// registration.
void EnsureInitializedOnInitThread() {
DCHECK(cronet::OnInitThread());
if (net_change_logger_)
return;
net_change_logger_ =
std::make_unique<net::LoggingNetworkChangeObserver>(net_log_);
}
private:
raw_ptr<net::NetLog> net_log_;
// LoggingNetworkChangeObserver logs network change events to a NetLog.
// This class bundles one LoggingNetworkChangeObserver with one NetLog,
// so network change event are logged just once in the NetLog.
std::unique_ptr<net::LoggingNetworkChangeObserver> net_change_logger_;
};
// Use a global NetLog instance. See crbug.com/486120.
static base::LazyInstance<NetLogWithNetworkChangeEvents>::Leaky g_net_log =
LAZY_INSTANCE_INITIALIZER;
class BasicNetworkDelegate : public net::NetworkDelegateImpl {
public:
BasicNetworkDelegate() {}
BasicNetworkDelegate(const BasicNetworkDelegate&) = delete;
BasicNetworkDelegate& operator=(const BasicNetworkDelegate&) = delete;
~BasicNetworkDelegate() override {}
private:
// net::NetworkDelegate implementation.
bool OnAnnotateAndMoveUserBlockedCookies(
const net::URLRequest& request,
net::CookieAccessResultList& maybe_included_cookies,
net::CookieAccessResultList& excluded_cookies,
bool allowed_from_caller) override {
// Disallow sending cookies by default.
ExcludeAllCookies(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
maybe_included_cookies, excluded_cookies);
return false;
}
bool OnCanSetCookie(const net::URLRequest& request,
const net::CanonicalCookie& cookie,
net::CookieOptions* options,
bool allowed_from_caller) override {
// Disallow saving cookies by default.
return false;
}
};
// Helper function to make a net::URLRequestContext aware of a QUIC hint.
void SetQuicHint(net::URLRequestContext* context,
const cronet::URLRequestContextConfig::QuicHint* quic_hint) {
if (quic_hint->host.empty()) {
LOG(ERROR) << "Empty QUIC hint host: " << quic_hint->host;
return;
}
url::CanonHostInfo host_info;
std::string canon_host(net::CanonicalizeHost(quic_hint->host, &host_info));
if (!host_info.IsIPAddress() &&
!net::IsCanonicalizedHostCompliant(canon_host)) {
LOG(ERROR) << "Invalid QUIC hint host: " << quic_hint->host;
return;
}
if (quic_hint->port <= std::numeric_limits<uint16_t>::min() ||
quic_hint->port > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Invalid QUIC hint port: " << quic_hint->port;
return;
}
if (quic_hint->alternate_port <= std::numeric_limits<uint16_t>::min() ||
quic_hint->alternate_port > std::numeric_limits<uint16_t>::max()) {
LOG(ERROR) << "Invalid QUIC hint alternate port: "
<< quic_hint->alternate_port;
return;
}
url::SchemeHostPort quic_server("https", canon_host, quic_hint->port);
net::AlternativeService alternative_service(
net::kProtoQUIC, "", static_cast<uint16_t>(quic_hint->alternate_port));
context->http_server_properties()->SetQuicAlternativeService(
quic_server, net::NetworkIsolationKey(), alternative_service,
base::Time::Max(), quic::ParsedQuicVersionVector());
}
// net::NetworkChangeNotifier doesn't provide an API to query if a specific
// network has become disconnected. For these network though, it will return
// CONNECTION_UNKNOWN as their connection type. This should be a good enough
// approximation for the time being.
bool IsNetworkNoLongerConnected(
net::NetworkChangeNotifier::NetworkHandle network) {
return net::NetworkChangeNotifier::GetNetworkConnectionType(network) ==
net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
} // namespace
namespace cronet {
CronetContext::CronetContext(
std::unique_ptr<URLRequestContextConfig> context_config,
std::unique_ptr<Callback> callback,
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
: bidi_stream_detect_broken_connection_(
context_config->bidi_stream_detect_broken_connection),
heartbeat_interval_(context_config->heartbeat_interval),
default_load_flags_(
net::LOAD_NORMAL |
(context_config->load_disable_cache ? net::LOAD_DISABLE_CACHE : 0)),
network_tasks_(
new NetworkTasks(std::move(context_config), std::move(callback))),
network_task_runner_(network_task_runner) {
if (!network_task_runner_) {
network_thread_ = std::make_unique<base::Thread>("network");
base::Thread::Options options;
options.message_pump_type = base::MessagePumpType::IO;
network_thread_->StartWithOptions(std::move(options));
network_task_runner_ = network_thread_->task_runner();
}
}
CronetContext::~CronetContext() {
DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
GetNetworkTaskRunner()->DeleteSoon(FROM_HERE, network_tasks_.get());
}
CronetContext::NetworkTasks::NetworkTasks(
std::unique_ptr<URLRequestContextConfig> context_config,
std::unique_ptr<CronetContext::Callback> callback)
: default_context_(nullptr),
is_default_context_initialized_(false),
context_config_(std::move(context_config)),
callback_(std::move(callback)) {
DETACH_FROM_THREAD(network_thread_checker_);
}
CronetContext::NetworkTasks::~NetworkTasks() {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
callback_->OnDestroyNetworkThread();
if (cronet_prefs_manager_)
cronet_prefs_manager_->PrepareForShutdown();
if (network_quality_estimator_) {
network_quality_estimator_->RemoveRTTObserver(this);
network_quality_estimator_->RemoveThroughputObserver(this);
network_quality_estimator_->RemoveEffectiveConnectionTypeObserver(this);
network_quality_estimator_->RemoveRTTAndThroughputEstimatesObserver(this);
}
if (net::NetworkChangeNotifier::AreNetworkHandlesSupported())
net::NetworkChangeNotifier::RemoveNetworkObserver(this);
}
void CronetContext::InitRequestContextOnInitThread() {
DCHECK(OnInitThread());
// Cannot create this inside Initialize because Android requires this to be
// created on the JNI thread.
auto proxy_config_service =
cronet::CreateProxyConfigService(GetNetworkTaskRunner());
g_net_log.Get().EnsureInitializedOnInitThread();
GetNetworkTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&CronetContext::NetworkTasks::Initialize,
base::Unretained(network_tasks_), GetNetworkTaskRunner(),
GetFileThread()->task_runner(),
std::move(proxy_config_service)));
}
void CronetContext::NetworkTasks::ConfigureNetworkQualityEstimatorForTesting(
bool use_local_host_requests,
bool use_smaller_responses,
bool disable_offline_check) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
network_quality_estimator_->SetUseLocalHostRequestsForTesting(
use_local_host_requests);
network_quality_estimator_->SetUseSmallResponsesForTesting(
use_smaller_responses);
network_quality_estimator_->DisableOfflineCheckForTesting(
disable_offline_check);
}
void CronetContext::ConfigureNetworkQualityEstimatorForTesting(
bool use_local_host_requests,
bool use_smaller_responses,
bool disable_offline_check) {
PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetContext::NetworkTasks::
ConfigureNetworkQualityEstimatorForTesting,
base::Unretained(network_tasks_), use_local_host_requests,
use_smaller_responses, disable_offline_check));
}
void CronetContext::NetworkTasks::ProvideRTTObservations(bool should) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
if (!network_quality_estimator_)
return;
if (should) {
network_quality_estimator_->AddRTTObserver(this);
} else {
network_quality_estimator_->RemoveRTTObserver(this);
}
}
void CronetContext::ProvideRTTObservations(bool should) {
PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetContext::NetworkTasks::ProvideRTTObservations,
base::Unretained(network_tasks_), should));
}
void CronetContext::NetworkTasks::ProvideThroughputObservations(bool should) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
if (!network_quality_estimator_)
return;
if (should) {
network_quality_estimator_->AddThroughputObserver(this);
} else {
network_quality_estimator_->RemoveThroughputObserver(this);
}
}
void CronetContext::ProvideThroughputObservations(bool should) {
PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(
&CronetContext::NetworkTasks::ProvideThroughputObservations,
base::Unretained(network_tasks_), should));
}
void CronetContext::NetworkTasks::SpawnNetworkBoundURLRequestContextForTesting(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
DCHECK(!contexts_.contains(network));
contexts_[network] = BuildNetworkBoundURLRequestContext(network);
}
bool CronetContext::NetworkTasks::DoesURLRequestContextExistForTesting(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
return contexts_.contains(network);
}
void CronetContext::NetworkTasks::InitializeNQEPrefs() const {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
// Initializing |network_qualities_prefs_manager_| may post a callback to
// |this|. So, |network_qualities_prefs_manager_| should be initialized after
// |callback_| has been initialized.
DCHECK(is_default_context_initialized_);
cronet_prefs_manager_->SetupNqePersistence(network_quality_estimator_.get());
}
std::unique_ptr<net::URLRequestContext>
CronetContext::NetworkTasks::BuildDefaultURLRequestContext(
std::unique_ptr<net::ProxyConfigService> proxy_config_service) {
DCHECK(!network_quality_estimator_);
DCHECK(!cronet_prefs_manager_);
net::URLRequestContextBuilder context_builder;
SetSharedURLRequestContextBuilderConfig(&context_builder);
const auto proxy_server_it =
context_config_->experimental_options.find("proxy_server");
std::string proxy_server_str = "direct://";
if (proxy_server_it != context_config_->experimental_options.end()) {
const base::Value& value = proxy_server_it->second;
if (value.is_string()) {
proxy_server_str = value.GetString();
}
}
net::ProxyConfig proxy_config;
proxy_config.proxy_rules().ParseFromString(proxy_server_str);
auto proxy_service =
net::ConfiguredProxyResolutionService::CreateWithoutProxyResolver(
std::make_unique<net::ProxyConfigServiceFixed>(
net::ProxyConfigWithAnnotation(proxy_config,
MISSING_TRAFFIC_ANNOTATION)),
g_net_log.Get().net_log());
context_builder.set_proxy_resolution_service(std::move(proxy_service));
if (context_config_->enable_network_quality_estimator) {
std::unique_ptr<net::NetworkQualityEstimatorParams> nqe_params =
std::make_unique<net::NetworkQualityEstimatorParams>(
std::map<std::string, std::string>());
if (context_config_->nqe_forced_effective_connection_type) {
nqe_params->SetForcedEffectiveConnectionType(
context_config_->nqe_forced_effective_connection_type.value());
}
network_quality_estimator_ = std::make_unique<net::NetworkQualityEstimator>(
std::move(nqe_params), g_net_log.Get().net_log());
network_quality_estimator_->AddEffectiveConnectionTypeObserver(this);
network_quality_estimator_->AddRTTAndThroughputEstimatesObserver(this);
context_builder.set_network_quality_estimator(
network_quality_estimator_.get());
}
// Set up pref file if storage path is specified.
if (!context_config_->storage_path.empty()) {
#if BUILDFLAG(IS_WIN)
base::FilePath storage_path(
base::FilePath::FromUTF8Unsafe(context_config_->storage_path));
#else
base::FilePath storage_path(context_config_->storage_path);
#endif
// Currently only the default context uses a PrefManager, this means that
// contexts for specific networks do not maintain state between restarts.
// Part of that is by design, part of that is due to CronetPrefsManager's
// current interface: it assumes that a single URLRequestContext exists
// and, under that assumption, mixes NQE, HostCache, and
// HttpServerProperties management persistence. The former two should
// apply only to the default context, while the latter could also be
// applied to network-bound contexts.
// TODO(stefanoduo): Decouple CronetPrefManager management of NQE,
// HostCache and HttpServerProperties and apply HttpServerProperties to
// network bound contexts.
cronet_prefs_manager_ = std::make_unique<CronetPrefsManager>(
context_config_->storage_path, network_task_runner_, file_task_runner_,
context_config_->enable_network_quality_estimator,
context_config_->enable_host_cache_persistence,
g_net_log.Get().net_log(), &context_builder);
}
auto context = context_builder.Build();
// Set up host cache persistence if it's enabled. Happens after building the
// URLRequestContext to get access to the HostCache.
if (context_config_->enable_host_cache_persistence && cronet_prefs_manager_) {
net::HostCache* host_cache = context->host_resolver()->GetHostCache();
cronet_prefs_manager_->SetupHostCachePersistence(
host_cache, context_config_->host_cache_persistence_delay_ms,
g_net_log.Get().net_log());
}
SetSharedURLRequestContextConfig(context.get());
return context;
}
std::unique_ptr<net::URLRequestContext>
CronetContext::NetworkTasks::BuildNetworkBoundURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network) {
net::URLRequestContextBuilder context_builder;
SetSharedURLRequestContextBuilderConfig(&context_builder);
// URLRequestContexts that are bound to a network cannot specify a
// HostResolver in any way (URLRequestContextBuilder will internally pick
// one that support per-network lookups). Hence, if options for this are
// specified in Cronet's configuration, they should apply only to the default
// context.
context_builder.set_host_resolver(nullptr);
context_builder.set_host_mapping_rules(std::string());
context_builder.set_host_resolver_manager(nullptr);
context_builder.set_host_resolver_factory(nullptr);
context_builder.BindToNetwork(network);
// On Android, Cronet doesn't handle PAC URL processing, instead it defers
// that to the OS (which sets up a local proxy configured correctly w.r.t.
// Android settings). See crbug.com/432539.
// TODO(stefanoduo): Confirm if we can keep using this configuration for
// requests bound to a network (otherwise we might have to query that
// network's LinkProperties#getHttpProxy).
// Until then don't support proxies when a network is specified.
context_builder.set_proxy_config_service(
std::make_unique<net::ProxyConfigServiceFixed>(
net::ProxyConfigWithAnnotation::CreateDirect()));
auto context = context_builder.Build();
SetSharedURLRequestContextConfig(context.get());
return context;
}
void CronetContext::NetworkTasks::SetSharedURLRequestContextBuilderConfig(
net::URLRequestContextBuilder* context_builder) {
context_builder->set_network_delegate(
std::make_unique<BasicNetworkDelegate>());
context_builder->set_net_log(g_net_log.Get().net_log());
context_config_->ConfigureURLRequestContextBuilder(context_builder);
// Explicitly disable the persister for Cronet to avoid persistence of dynamic
// HPKP. This is a safety measure ensuring that nobody enables the persistence
// of HPKP by specifying transport_security_persister_file_path in the future.
context_builder->set_transport_security_persister_file_path(base::FilePath());
// Disable net::CookieStore.
context_builder->SetCookieStore(nullptr);
}
void CronetContext::NetworkTasks::SetSharedURLRequestContextConfig(
net::URLRequestContext* context) {
context->set_check_cleartext_permitted(true);
context->set_enable_brotli(context_config_->enable_brotli);
if (context_config_->enable_quic) {
for (const auto& quic_hint : context_config_->quic_hints)
SetQuicHint(context, quic_hint.get());
}
// Iterate through PKP configuration for every host.
for (const auto& pkp : context_config_->pkp_list) {
// Add the host pinning.
context->transport_security_state()->AddHPKP(
pkp->host, pkp->expiration_date, pkp->include_subdomains,
pkp->pin_hashes, GURL::EmptyGURL());
}
context->transport_security_state()
->SetEnablePublicKeyPinningBypassForLocalTrustAnchors(
context_config_->bypass_public_key_pinning_for_local_trust_anchors);
#if BUILDFLAG(ENABLE_REPORTING)
if (context->reporting_service()) {
for (const auto& preloaded_header :
context_config_->preloaded_report_to_headers) {
context->reporting_service()->ProcessReportToHeader(
preloaded_header.origin, net::NetworkIsolationKey(),
preloaded_header.value);
}
}
if (context->network_error_logging_service()) {
for (const auto& preloaded_header :
context_config_->preloaded_nel_headers) {
context->network_error_logging_service()->OnHeader(
net::NetworkIsolationKey(), preloaded_header.origin, net::IPAddress(),
preloaded_header.value);
}
}
#endif // BUILDFLAG(ENABLE_REPORTING)
}
void CronetContext::NetworkTasks::Initialize(
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
std::unique_ptr<net::ProxyConfigService> proxy_config_service) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
DCHECK(!is_default_context_initialized_);
network_task_runner_ = network_task_runner;
file_task_runner_ = file_task_runner;
if (context_config_->network_thread_priority)
SetNetworkThreadPriorityOnNetworkThread(
context_config_->network_thread_priority.value());
base::DisallowBlocking();
effective_experimental_options_ =
base::Value(context_config_->effective_experimental_options);
const net::NetworkChangeNotifier::NetworkHandle default_network =
net::NetworkChangeNotifier::kInvalidNetworkHandle;
contexts_[default_network] =
BuildDefaultURLRequestContext(std::move(proxy_config_service));
default_context_ = contexts_[default_network].get();
if (net::NetworkChangeNotifier::AreNetworkHandlesSupported())
net::NetworkChangeNotifier::AddNetworkObserver(this);
callback_->OnInitNetworkThread();
is_default_context_initialized_ = true;
if (context_config_->enable_network_quality_estimator &&
cronet_prefs_manager_) {
// TODO(crbug.com/758401): Provide a better way for to configure the NQE
// for testing.. Currently, tests rely on posting a task to this network
// thread and hope it executes before the one below does.
network_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CronetContext::NetworkTasks::InitializeNQEPrefs,
base::Unretained(this)));
}
while (!tasks_waiting_for_context_.empty()) {
std::move(tasks_waiting_for_context_.front()).Run();
tasks_waiting_for_context_.pop();
}
}
net::URLRequestContext* CronetContext::NetworkTasks::GetURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
DCHECK(is_default_context_initialized_);
if (network == net::NetworkChangeNotifier::kInvalidNetworkHandle)
return default_context_;
// Non-default contexts are created on the fly.
if (contexts_.find(network) == contexts_.end())
contexts_[network] = BuildNetworkBoundURLRequestContext(network);
return contexts_[network].get();
}
void CronetContext::NetworkTasks::MaybeDestroyURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
// Default network context is never deleted.
if (network == net::NetworkChangeNotifier::kInvalidNetworkHandle)
return;
if (!contexts_.contains(network))
return;
auto& context = contexts_[network];
// For a URLRequestContext to be destroyed, two conditions must be satisfied:
// 1. The network associated to that context must be no longer connected
// 2. There must be no URLRequests associated to that context
if (context->url_requests()->size() == 0 &&
IsNetworkNoLongerConnected(network)) {
contexts_.erase(network);
}
}
// Request context getter for CronetContext.
class CronetContext::ContextGetter : public net::URLRequestContextGetter {
public:
explicit ContextGetter(CronetContext* cronet_context)
: cronet_context_(cronet_context) {
DCHECK(cronet_context_);
}
ContextGetter(const ContextGetter&) = delete;
ContextGetter& operator=(const ContextGetter&) = delete;
net::URLRequestContext* GetURLRequestContext() override {
return cronet_context_->GetURLRequestContext();
}
scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
const override {
return cronet_context_->GetNetworkTaskRunner();
}
private:
// Must be called on the network thread.
~ContextGetter() override { DCHECK(cronet_context_->IsOnNetworkThread()); }
// CronetContext associated with this ContextGetter.
const raw_ptr<CronetContext> cronet_context_;
};
net::URLRequestContextGetter* CronetContext::CreateURLRequestContextGetter() {
DCHECK(IsOnNetworkThread());
return new ContextGetter(this);
}
net::URLRequestContext* CronetContext::GetURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK(IsOnNetworkThread());
return network_tasks_->GetURLRequestContext(network);
}
void CronetContext::PostTaskToNetworkThread(const base::Location& posted_from,
base::OnceClosure callback) {
GetNetworkTaskRunner()->PostTask(
posted_from,
base::BindOnce(&CronetContext::NetworkTasks::RunTaskAfterContextInit,
base::Unretained(network_tasks_), std::move(callback)));
}
void CronetContext::NetworkTasks::RunTaskAfterContextInit(
base::OnceClosure task_to_run_after_context_init) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
if (is_default_context_initialized_) {
DCHECK(tasks_waiting_for_context_.empty());
std::move(task_to_run_after_context_init).Run();
return;
}
tasks_waiting_for_context_.push(std::move(task_to_run_after_context_init));
}
bool CronetContext::IsOnNetworkThread() const {
return GetNetworkTaskRunner()->BelongsToCurrentThread();
}
scoped_refptr<base::SingleThreadTaskRunner>
CronetContext::GetNetworkTaskRunner() const {
return network_task_runner_;
}
bool CronetContext::StartNetLogToFile(const std::string& file_name,
bool log_all) {
#if BUILDFLAG(IS_WIN)
base::FilePath file_path(base::FilePath::FromUTF8Unsafe(file_name));
#else
base::FilePath file_path(file_name);
#endif
base::ScopedFILE file(base::OpenFile(file_path, "w"));
if (!file) {
LOG(ERROR) << "Failed to open NetLog file for writing.";
return false;
}
PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetContext::NetworkTasks::StartNetLog,
base::Unretained(network_tasks_), file_path, log_all));
return true;
}
void CronetContext::StartNetLogToDisk(const std::string& dir_name,
bool log_all,
int max_size) {
PostTaskToNetworkThread(
FROM_HERE,
base::BindOnce(&CronetContext::NetworkTasks::StartNetLogToBoundedFile,
base::Unretained(network_tasks_), dir_name, log_all,
max_size));
}
void CronetContext::StopNetLog() {
DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
PostTaskToNetworkThread(
FROM_HERE, base::BindOnce(&CronetContext::NetworkTasks::StopNetLog,
base::Unretained(network_tasks_)));
}
void CronetContext::MaybeDestroyURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK(IsOnNetworkThread());
network_tasks_->MaybeDestroyURLRequestContext(network);
}
int CronetContext::default_load_flags() const {
return default_load_flags_;
}
base::Thread* CronetContext::GetFileThread() {
DCHECK(OnInitThread());
if (!file_thread_) {
file_thread_ = std::make_unique<base::Thread>("Network File Thread");
file_thread_->Start();
}
return file_thread_.get();
}
void CronetContext::NetworkTasks::OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType effective_connection_type) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
callback_->OnEffectiveConnectionTypeChanged(effective_connection_type);
}
void CronetContext::NetworkTasks::OnRTTOrThroughputEstimatesComputed(
base::TimeDelta http_rtt,
base::TimeDelta transport_rtt,
int32_t downstream_throughput_kbps) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
int32_t http_rtt_ms = http_rtt.InMilliseconds() <= INT32_MAX
? static_cast<int32_t>(http_rtt.InMilliseconds())
: INT32_MAX;
int32_t transport_rtt_ms =
transport_rtt.InMilliseconds() <= INT32_MAX
? static_cast<int32_t>(transport_rtt.InMilliseconds())
: INT32_MAX;
callback_->OnRTTOrThroughputEstimatesComputed(http_rtt_ms, transport_rtt_ms,
downstream_throughput_kbps);
}
void CronetContext::NetworkTasks::OnRTTObservation(
int32_t rtt_ms,
const base::TimeTicks& timestamp,
net::NetworkQualityObservationSource source) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
callback_->OnRTTObservation(
rtt_ms, (timestamp - base::TimeTicks::UnixEpoch()).InMilliseconds(),
source);
}
void CronetContext::NetworkTasks::OnThroughputObservation(
int32_t throughput_kbps,
const base::TimeTicks& timestamp,
net::NetworkQualityObservationSource source) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
callback_->OnThroughputObservation(
throughput_kbps,
(timestamp - base::TimeTicks::UnixEpoch()).InMilliseconds(), source);
}
void CronetContext::NetworkTasks::OnNetworkDisconnected(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
if (!contexts_.contains(network))
return;
auto& context = contexts_[network];
// After `network` disconnects, we can delete the URLRequestContext
// associated with it only if it has no pending URLRequests.
// If there are, their destruction procedure will take care of destroying
// this context (see MaybeDestroyURLRequestContext for more info).
if (context->url_requests()->size() == 0)
contexts_.erase(network);
}
void CronetContext::NetworkTasks::OnNetworkConnected(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
}
void CronetContext::NetworkTasks::OnNetworkSoonToDisconnect(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
}
void CronetContext::NetworkTasks::OnNetworkMadeDefault(
net::NetworkChangeNotifier::NetworkHandle network) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
}
void CronetContext::NetworkTasks::StartNetLog(const base::FilePath& file_path,
bool include_socket_bytes) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
// Do nothing if already logging to a file.
if (net_log_file_observer_)
return;
net::NetLogCaptureMode capture_mode =
include_socket_bytes ? net::NetLogCaptureMode::kEverything
: net::NetLogCaptureMode::kDefault;
net_log_file_observer_ = net::FileNetLogObserver::CreateUnbounded(
file_path, capture_mode, /*constants=*/nullptr);
std::set<net::URLRequestContext*> contexts;
for (auto& iter : contexts_)
contexts.insert(iter.second.get());
CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
}
void CronetContext::NetworkTasks::StartNetLogToBoundedFile(
const std::string& dir_path,
bool include_socket_bytes,
int size) {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
// Do nothing if already logging to a directory.
if (net_log_file_observer_) {
return;
}
// TODO(eroman): The cronet API passes a directory here. But it should now
// just pass a file path.
#if BUILDFLAG(IS_WIN)
base::FilePath file_path(base::FilePath::FromUTF8Unsafe(dir_path));
#else
base::FilePath file_path(dir_path);
#endif
file_path = file_path.AppendASCII("netlog.json");
{
base::ScopedAllowBlocking allow_blocking;
if (!base::PathIsWritable(file_path)) {
LOG(ERROR) << "Path is not writable: " << file_path.value();
}
}
net::NetLogCaptureMode capture_mode =
include_socket_bytes ? net::NetLogCaptureMode::kEverything
: net::NetLogCaptureMode::kDefault;
net_log_file_observer_ = net::FileNetLogObserver::CreateBounded(
file_path, size, capture_mode, /*constants=*/nullptr);
std::set<net::URLRequestContext*> contexts;
for (auto& iter : contexts_)
contexts.insert(iter.second.get());
CreateNetLogEntriesForActiveObjects(contexts, net_log_file_observer_.get());
net_log_file_observer_->StartObserving(g_net_log.Get().net_log());
}
void CronetContext::NetworkTasks::StopNetLog() {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
if (!net_log_file_observer_)
return;
net_log_file_observer_->StopObserving(
base::Value::ToUniquePtrValue(GetNetLogInfo()),
base::BindOnce(&CronetContext::NetworkTasks::StopNetLogCompleted,
base::Unretained(this)));
net_log_file_observer_.reset();
}
void CronetContext::NetworkTasks::StopNetLogCompleted() {
DCHECK_CALLED_ON_VALID_THREAD(network_thread_checker_);
callback_->OnStopNetLogCompleted();
}
base::Value CronetContext::NetworkTasks::GetNetLogInfo() const {
base::Value net_info(base::Value::Type::DICTIONARY);
for (auto& iter : contexts_)
net_info.SetKey(base::NumberToString(iter.first),
net::GetNetInfo(iter.second.get()));
if (!effective_experimental_options_.DictEmpty()) {
net_info.SetKey("cronetExperimentalParams",
effective_experimental_options_.Clone());
}
return net_info;
}
} // namespace cronet

View File

@ -1,394 +0,0 @@
// Copyright 2014 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.
#ifndef COMPONENTS_CRONET_CRONET_CONTEXT_H_
#define COMPONENTS_CRONET_CRONET_CONTEXT_H_
#include <stdint.h>
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "components/prefs/json_pref_store.h"
#include "net/base/network_change_notifier.h"
#include "net/nqe/effective_connection_type.h"
#include "net/nqe/effective_connection_type_observer.h"
#include "net/nqe/network_quality_estimator.h"
#include "net/nqe/network_quality_observation_source.h"
#include "net/nqe/rtt_throughput_estimates_observer.h"
class PrefService;
namespace base {
class SingleThreadTaskRunner;
class TimeTicks;
} // namespace base
namespace net {
enum EffectiveConnectionType;
class ProxyConfigService;
class NetLog;
class URLRequestContext;
class URLRequestContextBuilder;
class URLRequestContextGetter;
class FileNetLogObserver;
} // namespace net
namespace cronet {
class CronetPrefsManager;
class TestUtil;
struct URLRequestContextConfig;
// Wrapper around net::URLRequestContext.
class CronetContext {
public:
// Callback implemented by CronetContext() caller and owned by
// CronetContext::NetworkTasks.
class Callback {
public:
virtual ~Callback() = default;
// Invoked on network thread when initialized.
virtual void OnInitNetworkThread() = 0;
// Invoked on network thread immediately prior to destruction.
virtual void OnDestroyNetworkThread() = 0;
// net::NetworkQualityEstimator::EffectiveConnectionTypeObserver forwarder.
virtual void OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType effective_connection_type) = 0;
// net::NetworkQualityEstimator::RTTAndThroughputEstimatesObserver
// forwarder.
virtual void OnRTTOrThroughputEstimatesComputed(
int32_t http_rtt_ms,
int32_t transport_rtt_ms,
int32_t downstream_throughput_kbps) = 0;
// net::NetworkQualityEstimator::RTTObserver forwarder.
virtual void OnRTTObservation(
int32_t rtt_ms,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) = 0;
// net::NetworkQualityEstimator::RTTObserver forwarder.
virtual void OnThroughputObservation(
int32_t throughput_kbps,
int32_t timestamp_ms,
net::NetworkQualityObservationSource source) = 0;
// Callback for StopNetLog() that signals that it is safe to access
// the NetLog files.
virtual void OnStopNetLogCompleted() = 0;
};
// Constructs CronetContext using |context_config|. The |callback|
// is owned by |this| and is deleted on network thread.
// All |callback| methods are invoked on network thread.
// If the network_task_runner is not assigned, a network thread would be
// created for network tasks. Otherwise the tasks would be running on the
// assigned task runner.
CronetContext(std::unique_ptr<URLRequestContextConfig> context_config,
std::unique_ptr<Callback> callback,
scoped_refptr<base::SingleThreadTaskRunner>
network_task_runner = nullptr);
CronetContext(const CronetContext&) = delete;
CronetContext& operator=(const CronetContext&) = delete;
// Releases all resources for the request context and deletes the object.
// Blocks until network thread is destroyed after running all pending tasks.
virtual ~CronetContext();
// Called on init thread to initialize URLRequestContext.
void InitRequestContextOnInitThread();
// Posts a task that might depend on the context being initialized
// to the network thread.
void PostTaskToNetworkThread(const base::Location& posted_from,
base::OnceClosure callback);
// Returns true if running on network thread.
bool IsOnNetworkThread() const;
// Returns the net::URLRequestContext associated with `network`.
// kInvalidNetworkHandle represent the default context: this one will always
// be present and used whenever a requests doesn't specify a target network
// (currently the only possible behavior).
net::URLRequestContext* GetURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network =
net::NetworkChangeNotifier::kInvalidNetworkHandle);
// Returns a new instance of net::URLRequestContextGetter.
// The net::URLRequestContext and base::SingleThreadTaskRunner that
// net::URLRequestContextGetter returns are owned by `this`.
// The returned getter will always return the default context of `this`.
net::URLRequestContextGetter* CreateURLRequestContextGetter();
// TODO(xunjieli): Keep only one version of StartNetLog().
// Starts NetLog logging to file. This can be called on any thread.
// Return false if |file_name| cannot be opened.
bool StartNetLogToFile(const std::string& file_name, bool log_all);
// Starts NetLog logging to disk with a bounded amount of disk space. This
// can be called on any thread.
void StartNetLogToDisk(const std::string& dir_name,
bool log_all,
int max_size);
// Stops NetLog logging to file. This can be called on any thread. This will
// flush any remaining writes to disk.
void StopNetLog();
// Destroys the URLRequestContext associated to `network` if `network` has
// disconnected and it has no pending URLRequests. This must be called on
// the network thread while destroying a CronetURLRequest as that might
// mark a URLRequestContext as eligible for destruction.
void MaybeDestroyURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network);
// Default net::LOAD flags used to create requests.
int default_load_flags() const;
// Configures the network quality estimator to observe requests to localhost,
// to use smaller responses when estimating throughput, and to disable the
// device offline checks when computing the effective connection type or when
// writing the prefs. This should only be used for testing. This can be
// called only after the network quality estimator has been enabled.
void ConfigureNetworkQualityEstimatorForTesting(bool use_local_host_requests,
bool use_smaller_responses,
bool disable_offline_check);
// Request that RTT and/or throughput observations should or should not be
// provided by the network quality estimator.
void ProvideRTTObservations(bool should);
void ProvideThroughputObservations(bool should);
bool bidi_stream_detect_broken_connection() const {
return bidi_stream_detect_broken_connection_;
}
base::TimeDelta heartbeat_interval() const { return heartbeat_interval_; }
// NetworkTasks performs tasks on the network thread and owns objects that
// live on the network thread.
class NetworkTasks : public net::EffectiveConnectionTypeObserver,
public net::RTTAndThroughputEstimatesObserver,
public net::NetworkQualityEstimator::RTTObserver,
public net::NetworkQualityEstimator::ThroughputObserver,
public net::NetworkChangeNotifier::NetworkObserver {
public:
// Invoked off the network thread.
// `listen_to_network_changes` is a temporary parameter to allow
// multi-network testing for the time being.
NetworkTasks(std::unique_ptr<URLRequestContextConfig> config,
std::unique_ptr<CronetContext::Callback> callback);
NetworkTasks(const NetworkTasks&) = delete;
NetworkTasks& operator=(const NetworkTasks&) = delete;
// Invoked on the network thread.
~NetworkTasks() override;
// Initializes |context_| on the network thread.
void Initialize(
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
std::unique_ptr<net::ProxyConfigService> proxy_config_service);
// Runs a task that might depend on the context being initialized.
void RunTaskAfterContextInit(
base::OnceClosure task_to_run_after_context_init);
// Configures the network quality estimator to observe requests to
// localhost, to use smaller responses when estimating throughput, and to
// disable the device offline checks when computing the effective connection
// type or when writing the prefs. This should only be used for testing.
void ConfigureNetworkQualityEstimatorForTesting(
bool use_local_host_requests,
bool use_smaller_responses,
bool disable_offline_check);
void ProvideRTTObservations(bool should);
void ProvideThroughputObservations(bool should);
// net::NetworkQualityEstimator::EffectiveConnectionTypeObserver
// implementation.
void OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType effective_connection_type) override;
// net::NetworkQualityEstimator::RTTAndThroughputEstimatesObserver
// implementation.
void OnRTTOrThroughputEstimatesComputed(
base::TimeDelta http_rtt,
base::TimeDelta transport_rtt,
int32_t downstream_throughput_kbps) override;
// net::NetworkQualityEstimator::RTTObserver implementation.
void OnRTTObservation(int32_t rtt_ms,
const base::TimeTicks& timestamp,
net::NetworkQualityObservationSource source) override;
// net::NetworkQualityEstimator::ThroughputObserver implementation.
void OnThroughputObservation(
int32_t throughput_kbps,
const base::TimeTicks& timestamp,
net::NetworkQualityObservationSource source) override;
// net::NetworkChangeNotifier::NetworkObserver implementation.
void OnNetworkDisconnected(
net::NetworkChangeNotifier::NetworkHandle network) override;
void OnNetworkConnected(
net::NetworkChangeNotifier::NetworkHandle network) override;
void OnNetworkSoonToDisconnect(
net::NetworkChangeNotifier::NetworkHandle network) override;
void OnNetworkMadeDefault(
net::NetworkChangeNotifier::NetworkHandle network) override;
net::URLRequestContext* GetURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network);
// Same as StartNetLogToDisk.
void StartNetLogToBoundedFile(const std::string& dir_path,
bool include_socket_bytes,
int size);
// Same as StartNetLogToFile, but called only on the network thread.
void StartNetLog(const base::FilePath& file_path,
bool include_socket_bytes);
// Stops NetLog logging.
void StopNetLog();
void MaybeDestroyURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network);
// Callback for StopObserving() that unblocks the client thread and
// signals that it is safe to access the NetLog files.
void StopNetLogCompleted();
// Initializes Network Quality Estimator (NQE) prefs manager on network
// thread.
void InitializeNQEPrefs() const;
void SpawnNetworkBoundURLRequestContextForTesting(
net::NetworkChangeNotifier::NetworkHandle network);
bool DoesURLRequestContextExistForTesting(
net::NetworkChangeNotifier::NetworkHandle network);
private:
friend class TestUtil;
base::Value GetNetLogInfo() const;
// Configures `context_builder` with the settings shared between default
// context and network bound contexts.
void SetSharedURLRequestContextBuilderConfig(
net::URLRequestContextBuilder* context_builder);
// Configures `context` with the settings shared between default context
// and network bound contexts.
void SetSharedURLRequestContextConfig(net::URLRequestContext* context);
// Builds a URLRequestContext specifically bound to `network`.
std::unique_ptr<net::URLRequestContext> BuildNetworkBoundURLRequestContext(
net::NetworkChangeNotifier::NetworkHandle network);
// Builds a URLRequestContext to be used a default context for `this`.
// `proxy_config_service` is injected as it currently cannot be built on the
// network thread.
std::unique_ptr<net::URLRequestContext> BuildDefaultURLRequestContext(
std::unique_ptr<net::ProxyConfigService> proxy_config_service);
std::unique_ptr<net::FileNetLogObserver> net_log_file_observer_;
// A network quality estimator. This member variable has to be destroyed
// after destroying |cronet_prefs_manager_|, which owns
// NetworkQualityPrefsManager that weakly references
// |network_quality_estimator_|.
std::unique_ptr<net::NetworkQualityEstimator> network_quality_estimator_;
// Manages the PrefService and all associated persistence managers
// such as NetworkQualityPrefsManager, HostCachePersistenceManager, etc.
// It should be destroyed before |network_quality_estimator_| and
// after |context_|.
std::unique_ptr<CronetPrefsManager> cronet_prefs_manager_;
// The mapping between networks and their URLRequestContext. The only
// context guaranteed to exist for the entire lifetime of `this` is default
// one, which is associated to kInvalidNetworkHandle.
// For requests not requiring a specific network the default context must be
// used.
base::flat_map<net::NetworkChangeNotifier::NetworkHandle,
std::unique_ptr<net::URLRequestContext>>
contexts_;
// Shorthand for the default context (needed by
// components/cronet/android/test/cronet_test_util.cc).
net::URLRequestContext* default_context_;
bool is_default_context_initialized_;
// Context config is only valid until context is initialized.
std::unique_ptr<URLRequestContextConfig> context_config_;
// Effective experimental options. Kept for NetLog.
base::Value effective_experimental_options_;
// A queue of tasks that need to be run after context has been initialized.
base::queue<base::OnceClosure> tasks_waiting_for_context_;
// Task runner that runs network tasks.
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
// Task runner that runs file tasks.
scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
// Callback implemented by the client.
std::unique_ptr<CronetContext::Callback> callback_;
THREAD_CHECKER(network_thread_checker_);
};
private:
friend class TestUtil;
class ContextGetter;
scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() const;
// Gets the file thread. Create one if there is none.
base::Thread* GetFileThread();
// Whether the connection status of active bidirectional streams should be
// monitored.
bool bidi_stream_detect_broken_connection_;
// If |bidi_stream_detect_broken_connection_| is true, this suggests the
// period of the heartbeat signal.
base::TimeDelta heartbeat_interval_;
const int default_load_flags_;
// File thread should be destroyed last.
std::unique_ptr<base::Thread> file_thread_;
// |network_tasks_| is owned by |this|. It is created off the network thread,
// but invoked and destroyed on network thread.
raw_ptr<NetworkTasks> network_tasks_;
// Network thread is destroyed from client thread.
std::unique_ptr<base::Thread> network_thread_;
// Task runner that runs network tasks.
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
};
} // namespace cronet
#endif // COMPONENTS_CRONET_CRONET_CONTEXT_H_

View File

@ -1,63 +0,0 @@
// Copyright 2017 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.
#ifndef COMPONENTS_CRONET_CRONET_GLOBAL_STATE_H_
#define COMPONENTS_CRONET_CRONET_GLOBAL_STATE_H_
#include <memory>
#include <string>
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
namespace net {
class NetLog;
class ProxyConfigService;
class ProxyResolutionService;
} // namespace net
namespace cronet {
// Returns true when called on the initialization thread.
// May only be called after EnsureInitialized() has returned.
bool OnInitThread();
// Posts a task to run on initialization thread. Blocks until initialization
// thread is started.
void PostTaskToInitThread(const base::Location& posted_from,
base::OnceClosure task);
// Performs one-off initialization of Cronet global state, including creating,
// or binding to an existing thread, to run initialization and process
// network notifications on. The implementation must be thread-safe and
// idempotent, and must complete initialization before returning.
void EnsureInitialized();
// Creates a proxy config service appropriate for this platform that fetches the
// system proxy settings. Cronet will call this API only after a prior call
// to EnsureInitialized() has returned.
// On Android, this must be called on the JNI thread.
std::unique_ptr<net::ProxyConfigService> CreateProxyConfigService(
const scoped_refptr<base::SequencedTaskRunner>& io_task_runner);
// Creates a proxy resolution service appropriate for this platform that fetches
// the system proxy settings. Cronet will call this API only after a prior call
// to EnsureInitialized() has returned.
std::unique_ptr<net::ProxyResolutionService> CreateProxyResolutionService(
std::unique_ptr<net::ProxyConfigService> proxy_config_service,
net::NetLog* net_log);
// Creates default User-Agent request value, combining optional
// |partial_user_agent| with system-dependent values. This API may be invoked
// before EnsureInitialized(), in which case it may trigger initialization
// itself, if necessary.
std::string CreateDefaultUserAgent(const std::string& partial_user_agent);
// Set network thread priority to |priority|. Must be called on the network
// thread. On Android, corresponds to android.os.Process.setThreadPriority()
// values. On iOS, corresponds to NSThread::setThreadPriority values.
void SetNetworkThreadPriorityOnNetworkThread(double priority);
} // namespace cronet
#endif // COMPONENTS_CRONET_CRONET_GLOBAL_STATE_H_

View File

@ -1,83 +0,0 @@
// 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.
#include "components/cronet/cronet_global_state.h"
#include <tuple>
#include "base/at_exit.h"
#include "base/feature_list.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/proxy_resolution/proxy_config_service.h"
// This file provides minimal "stub" implementations of the Cronet global-state
// functions for the native library build, sufficient to have cronet_tests and
// cronet_unittests build.
namespace cronet {
namespace {
scoped_refptr<base::SingleThreadTaskRunner> InitializeAndCreateTaskRunner() {
// Cronet tests sets AtExitManager as part of TestSuite, so statically linked
// library is not allowed to set its own.
#if !defined(CRONET_TESTS_IMPLEMENTATION)
std::ignore = new base::AtExitManager;
#endif
base::FeatureList::InitializeInstance(std::string(), std::string());
// Note that in component builds this ThreadPoolInstance will be shared with
// the calling process, if it also depends on //base. In particular this means
// that the Cronet test binaries must avoid initializing or shutting-down the
// ThreadPoolInstance themselves.
base::ThreadPoolInstance::CreateAndStartWithDefaultParams("cronet");
return base::ThreadPool::CreateSingleThreadTaskRunner({});
}
base::SingleThreadTaskRunner* InitTaskRunner() {
static scoped_refptr<base::SingleThreadTaskRunner> init_task_runner =
InitializeAndCreateTaskRunner();
return init_task_runner.get();
}
} // namespace
void EnsureInitialized() {
std::ignore = InitTaskRunner();
}
bool OnInitThread() {
return InitTaskRunner()->BelongsToCurrentThread();
}
void PostTaskToInitThread(const base::Location& posted_from,
base::OnceClosure task) {
InitTaskRunner()->PostTask(posted_from, std::move(task));
}
std::unique_ptr<net::ProxyConfigService> CreateProxyConfigService(
const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) {
return nullptr;
}
std::unique_ptr<net::ProxyResolutionService> CreateProxyResolutionService(
std::unique_ptr<net::ProxyConfigService> proxy_config_service,
net::NetLog* net_log) {
return nullptr;
}
std::string CreateDefaultUserAgent(const std::string& partial_user_agent) {
return partial_user_agent;
}
void SetNetworkThreadPriorityOnNetworkThread(double priority) {
NOTIMPLEMENTED();
}
} // namespace cronet

Some files were not shown because too many files have changed in this diff Show More