mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-23 22:06:12 +03:00
Compare commits
No commits in common. "fa421a5c800e0b8db25fb94fd38f471fb4e180a7" and "e775d7b29de161c6228adf723655ec3608f44fbc" have entirely different histories.
fa421a5c80
...
e775d7b29d
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
@ -93,17 +93,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
arch: [x64, x86, arm64, arm, mipsel, mips64el]
|
||||||
- arch: x64
|
|
||||||
- arch: x86
|
|
||||||
- arch: arm64
|
|
||||||
- arch: arm
|
|
||||||
- arch: mipsel
|
|
||||||
extra: chrome_pgo_phase=0
|
|
||||||
- arch: mips64el
|
|
||||||
extra: chrome_pgo_phase=0
|
|
||||||
env:
|
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 }}'
|
BUNDLE: 'naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@ -130,8 +122,8 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.ccache
|
path: ~/.ccache
|
||||||
key: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-${{ steps.ccache-timestamp.outputs.date }}
|
key: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
|
||||||
restore-keys: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-
|
restore-keys: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
|
||||||
- run: sudo apt update
|
- run: sudo apt update
|
||||||
- run: sudo apt install ninja-build pkg-config qemu-user ccache
|
- run: sudo apt install ninja-build pkg-config qemu-user ccache
|
||||||
# libc6-i386 interferes with x86 build
|
# libc6-i386 interferes with x86 build
|
||||||
@ -142,7 +134,6 @@ jobs:
|
|||||||
- run: ccache -s
|
- run: ccache -s
|
||||||
- run: ../tests/basic.sh out/Release/naive
|
- run: ../tests/basic.sh out/Release/naive
|
||||||
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
|
- 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 }}
|
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
|
||||||
- uses: actions/upload-release-asset@v1
|
- uses: actions/upload-release-asset@v1
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
@ -213,7 +204,6 @@ jobs:
|
|||||||
# qemu-user segfaults with x64 or x86 android builds here.
|
# qemu-user segfaults with x64 or x86 android builds here.
|
||||||
if: ${{ matrix.arch != 'x64' && matrix.arch != 'x86' }}
|
if: ${{ matrix.arch != 'x64' && matrix.arch != 'x86' }}
|
||||||
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
|
- 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 }}
|
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
|
||||||
- uses: actions/upload-release-asset@v1
|
- uses: actions/upload-release-asset@v1
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
@ -279,7 +269,6 @@ jobs:
|
|||||||
# No real or emulated environment is available to test this.
|
# No real or emulated environment is available to test this.
|
||||||
if: ${{ matrix.arch != 'arm64' }}
|
if: ${{ matrix.arch != 'arm64' }}
|
||||||
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive.exe config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
|
- 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 }}
|
- run: 7z a ../${{ env.BUNDLE }}.zip ${{ env.BUNDLE }}
|
||||||
- uses: actions/upload-release-asset@v1
|
- uses: actions/upload-release-asset@v1
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
@ -334,7 +323,6 @@ jobs:
|
|||||||
# No real or emulated environment is available to test this.
|
# No real or emulated environment is available to test this.
|
||||||
if: ${{ matrix.arch != 'arm64' }}
|
if: ${{ matrix.arch != 'arm64' }}
|
||||||
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
|
- 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 }}
|
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
|
||||||
- uses: actions/upload-release-asset@v1
|
- uses: actions/upload-release-asset@v1
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
@ -476,8 +464,8 @@ jobs:
|
|||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.ccache
|
path: ~/.ccache
|
||||||
key: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-${{ steps.ccache-timestamp.outputs.date }}
|
key: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
|
||||||
restore-keys: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-4-
|
restore-keys: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
|
||||||
- run: sudo apt update
|
- run: sudo apt update
|
||||||
- run: sudo apt install ninja-build pkg-config qemu-user ccache
|
- run: sudo apt install ninja-build pkg-config qemu-user ccache
|
||||||
# libc6-i386 interferes with x86 build
|
# libc6-i386 interferes with x86 build
|
||||||
@ -488,8 +476,6 @@ jobs:
|
|||||||
- run: ccache -s
|
- run: ccache -s
|
||||||
- run: ../tests/basic.sh out/Release/naive
|
- run: ../tests/basic.sh out/Release/naive
|
||||||
- run: mkdir ${{ env.BUNDLE }} && cp out/Release/naive config.json ../LICENSE ../USAGE.txt ${{ env.BUNDLE }}
|
- 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 }}
|
- run: tar cJf ../${{ env.BUNDLE }}.tar.xz ${{ env.BUNDLE }}
|
||||||
- uses: actions/upload-release-asset@v1
|
- uses: actions/upload-release-asset@v1
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
|
@ -53,7 +53,6 @@ group("gn_all") {
|
|||||||
deps = [
|
deps = [
|
||||||
":gn_visibility",
|
":gn_visibility",
|
||||||
"//net",
|
"//net",
|
||||||
"//components/cronet",
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,9 +68,4 @@ export DEPOT_TOOLS_WIN_TOOLCHAIN=0
|
|||||||
|
|
||||||
./gn/out/gn gen "$out" --args="$flags $EXTRA_FLAGS" --script-executable=$PYTHON
|
./gn/out/gn gen "$out" --args="$flags $EXTRA_FLAGS" --script-executable=$PYTHON
|
||||||
|
|
||||||
cronet_targets="cronet cronet_static"
|
ninja -C "$out" naive
|
||||||
if echo "$EXTRA_FLAGS" | grep -q "build_static=true"; then
|
|
||||||
cronet_targets=
|
|
||||||
fi
|
|
||||||
|
|
||||||
ninja -C "$out" naive $cronet_targets
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
* <meta> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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 {}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -347,7 +347,7 @@ default_compiler_configs = [
|
|||||||
"//build/config/compiler:prevent_unsafe_narrowing",
|
"//build/config/compiler:prevent_unsafe_narrowing",
|
||||||
"//build/config/compiler:runtime_library",
|
"//build/config/compiler:runtime_library",
|
||||||
"//build/config/compiler:thin_archive",
|
"//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/compiler/pgo:default_pgo_flags",
|
||||||
"//build/config/coverage:default_coverage",
|
"//build/config/coverage:default_coverage",
|
||||||
"//build/config/sanitizers:default_sanitizer_flags",
|
"//build/config/sanitizers:default_sanitizer_flags",
|
||||||
|
@ -12,6 +12,4 @@ clang_toolchain(target_cpu) {
|
|||||||
current_cpu = target_cpu
|
current_cpu = target_cpu
|
||||||
current_os = "openwrt"
|
current_os = "openwrt"
|
||||||
}
|
}
|
||||||
|
|
||||||
strip = rebase_path("$clang_base_path/bin/llvm-strip", root_build_dir)
|
|
||||||
}
|
}
|
||||||
|
@ -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" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
include_rules = [
|
|
||||||
"+components/grpc_support",
|
|
||||||
"+components/metrics",
|
|
||||||
"+components/prefs",
|
|
||||||
"+net",
|
|
||||||
"+third_party/metrics_proto",
|
|
||||||
"+third_party/zlib",
|
|
||||||
]
|
|
@ -1,5 +0,0 @@
|
|||||||
monorail {
|
|
||||||
component: "Internals>Network>Library"
|
|
||||||
}
|
|
||||||
|
|
||||||
team_email: "net-dev@chromium.org"
|
|
@ -1,5 +0,0 @@
|
|||||||
cleborgne@google.com
|
|
||||||
danstahr@google.com
|
|
||||||
sporeba@google.com
|
|
||||||
torne@chromium.org
|
|
||||||
file://net/OWNERS
|
|
@ -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)
|
|
@ -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
@ -1,5 +0,0 @@
|
|||||||
include_rules = [
|
|
||||||
"+components/metrics",
|
|
||||||
"+crypto",
|
|
||||||
"+jni",
|
|
||||||
]
|
|
@ -1 +0,0 @@
|
|||||||
per-file lint-*.xml=*
|
|
@ -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:
|
|
||||||
*;
|
|
||||||
};
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:keep="@string/CronetProviderClassName" />
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -1 +0,0 @@
|
|||||||
# Proguard config for apps that depend on cronet_impl_common_java.jar.
|
|
@ -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);
|
|
||||||
}
|
|
@ -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
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -1 +0,0 @@
|
|||||||
15
|
|
@ -1 +0,0 @@
|
|||||||
14
|
|
@ -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
|
|
@ -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_
|
|
@ -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; \
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; \
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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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.
|
|
@ -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>
|
|
@ -1,4 +0,0 @@
|
|||||||
# Proguard settings for CronetSampleTestApk.
|
|
||||||
-keep class org.chromium.cronet_sample_apk.CronetSampleActivity {
|
|
||||||
*;
|
|
||||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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();
|
|
||||||
}
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -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>
|
|
@ -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
|
|
@ -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()
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
||||||
```
|
|
@ -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
|
|
@ -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_
|
|
@ -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`.
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -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
Loading…
Reference in New Issue
Block a user