android: add AndroidChannelBuilder (#4172)

This commit is contained in:
Eric Gribkoff 2018-04-23 11:49:36 -07:00 committed by GitHub
parent a47266ea4a
commit ba86a86c77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 739 additions and 1 deletions

View File

@ -63,6 +63,7 @@ them before continuing, and set them again when resuming.
$ MAJOR=1 MINOR=7 PATCH=0 # Set appropriately for new release $ MAJOR=1 MINOR=7 PATCH=0 # Set appropriately for new release
$ VERSION_FILES=( $ VERSION_FILES=(
build.gradle build.gradle
android/build.gradle
android-interop-testing/app/build.gradle android-interop-testing/app/build.gradle
core/src/main/java/io/grpc/internal/GrpcUtil.java core/src/main/java/io/grpc/internal/GrpcUtil.java
cronet/build.gradle cronet/build.gradle

53
android/build.gradle Normal file
View File

@ -0,0 +1,53 @@
apply plugin: 'com.android.library'
description = 'gRPC: Android'
buildscript {
repositories {
google()
jcenter()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
}
}
apply plugin: "net.ltgt.errorprone"
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 14
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
lintOptions {
abortOnError false
}
}
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
implementation 'io.grpc:grpc-core:1.12.0-SNAPSHOT' // CURRENT_GRPC_VERSION
testImplementation 'io.grpc:grpc-okhttp:1.12.0-SNAPSHOT' // CURRENT_GRPC_VERSION
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:3.7.1'
testImplementation 'com.google.truth:truth:0.39'
}

View File

@ -0,0 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.grpc.android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

View File

@ -0,0 +1,299 @@
/*
* Copyright 2018, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.android;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.os.Build;
import android.util.Log;
import com.google.common.annotations.VisibleForTesting;
import io.grpc.CallOptions;
import io.grpc.ClientCall;
import io.grpc.ConnectivityState;
import io.grpc.ExperimentalApi;
import io.grpc.ForwardingChannelBuilder;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.MethodDescriptor;
import io.grpc.internal.GrpcUtil;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
/**
* Builds a {@link ManagedChannel} that, when provided with a {@link Context}, will automatically
* monitor the Android device's network state to smoothly handle intermittent network failures.
*
* <p>Requires the Android ACCESS_NETWORK_STATE permission.
*
* @since 1.12.0
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4056")
public final class AndroidChannelBuilder extends ForwardingChannelBuilder<AndroidChannelBuilder> {
private static final String LOG_TAG = "AndroidChannelBuilder";
@Nullable private static final Class<?> OKHTTP_CHANNEL_BUILDER_CLASS = findOkHttp();
private static final Class<?> findOkHttp() {
try {
return Class.forName("io.grpc.okhttp.OkHttpChannelBuilder");
} catch (ClassNotFoundException e) {
return null;
}
}
private final ManagedChannelBuilder delegateBuilder;
@Nullable private Context context;
public static final AndroidChannelBuilder forTarget(String target) {
return new AndroidChannelBuilder(target);
}
public static AndroidChannelBuilder forAddress(String name, int port) {
return forTarget(GrpcUtil.authorityFromHostAndPort(name, port));
}
private AndroidChannelBuilder(String target) {
if (OKHTTP_CHANNEL_BUILDER_CLASS == null) {
throw new UnsupportedOperationException("No ManagedChannelBuilder found on the classpath");
}
try {
delegateBuilder =
(ManagedChannelBuilder)
OKHTTP_CHANNEL_BUILDER_CLASS
.getMethod("forTarget", String.class)
.invoke(null, target);
} catch (Exception e) {
throw new RuntimeException("Failed to create ManagedChannelBuilder", e);
}
}
/** Enables automatic monitoring of the device's network state. */
public AndroidChannelBuilder context(Context context) {
this.context = context;
return this;
}
@Override
protected ManagedChannelBuilder<?> delegate() {
return delegateBuilder;
}
@Override
public ManagedChannel build() {
return new AndroidChannel(delegateBuilder.build(), context);
}
/**
* Wraps an OkHttp channel and handles invoking the appropriate methods (e.g., {@link
* ManagedChannel#resetConnectBackoff}) when the device network state changes.
*/
@VisibleForTesting
static final class AndroidChannel extends ManagedChannel {
private final ManagedChannel delegate;
@Nullable private final Context context;
@Nullable private final ConnectivityManager connectivityManager;
private final Object lock = new Object();
@GuardedBy("lock")
private Runnable unregisterRunnable;
@VisibleForTesting
AndroidChannel(final ManagedChannel delegate, @Nullable Context context) {
this.delegate = delegate;
this.context = context;
if (context != null) {
connectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
configureNetworkMonitoring();
} else {
connectivityManager = null;
}
}
@GuardedBy("lock")
private void configureNetworkMonitoring() {
// Eagerly check current network state to verify app has required permissions
NetworkInfo currentNetwork;
try {
currentNetwork = connectivityManager.getActiveNetworkInfo();
} catch (SecurityException e) {
Log.w(
LOG_TAG,
"Failed to configure network monitoring. Does app have ACCESS_NETWORK_STATE"
+ " permission?",
e);
return;
}
// Android N added the registerDefaultNetworkCallback API to listen to changes in the device's
// default network. For earlier Android API levels, use the BroadcastReceiver API.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && connectivityManager != null) {
// The connection status may change before registration of the listener is complete, but
// this will at worst result in invoking resetConnectBackoff() instead of enterIdle() (or
// vice versa) on the first network change.
boolean isConnected = currentNetwork != null && currentNetwork.isConnected();
final DefaultNetworkCallback defaultNetworkCallback =
new DefaultNetworkCallback(isConnected);
connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback);
unregisterRunnable =
new Runnable() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
connectivityManager.unregisterNetworkCallback(defaultNetworkCallback);
}
};
} else {
final NetworkReceiver networkReceiver = new NetworkReceiver();
IntentFilter networkIntentFilter =
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(networkReceiver, networkIntentFilter);
unregisterRunnable =
new Runnable() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
context.unregisterReceiver(networkReceiver);
}
};
}
}
private void unregisterNetworkListener() {
synchronized (lock) {
if (unregisterRunnable != null) {
unregisterRunnable.run();
unregisterRunnable = null;
}
}
}
@Override
public ManagedChannel shutdown() {
unregisterNetworkListener();
return delegate.shutdown();
}
@Override
public boolean isShutdown() {
return delegate.isShutdown();
}
@Override
public boolean isTerminated() {
return delegate.isTerminated();
}
@Override
public ManagedChannel shutdownNow() {
unregisterNetworkListener();
return delegate.shutdownNow();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return delegate.awaitTermination(timeout, unit);
}
@Override
public <RequestT, ResponseT> ClientCall<RequestT, ResponseT> newCall(
MethodDescriptor<RequestT, ResponseT> methodDescriptor, CallOptions callOptions) {
return delegate.newCall(methodDescriptor, callOptions);
}
@Override
public String authority() {
return delegate.authority();
}
@Override
public ConnectivityState getState(boolean requestConnection) {
return delegate.getState(requestConnection);
}
@Override
public void notifyWhenStateChanged(ConnectivityState source, Runnable callback) {
delegate.notifyWhenStateChanged(source, callback);
}
@Override
public void resetConnectBackoff() {
delegate.resetConnectBackoff();
}
@Override
public void enterIdle() {
delegate.enterIdle();
}
/** Respond to changes in the default network. Only used on API levels 24+. */
@TargetApi(Build.VERSION_CODES.N)
private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
private boolean isConnected = false;
private DefaultNetworkCallback(boolean isConnected) {
this.isConnected = isConnected;
}
@Override
public void onAvailable(Network network) {
if (isConnected) {
delegate.enterIdle();
} else {
delegate.resetConnectBackoff();
}
isConnected = true;
}
@Override
public void onLost(Network network) {
isConnected = false;
}
}
/** Respond to network changes. Only used on API levels < 24. */
private class NetworkReceiver extends BroadcastReceiver {
private boolean isConnected = false;
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager conn =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = conn.getActiveNetworkInfo();
boolean wasConnected = isConnected;
isConnected = networkInfo != null && networkInfo.isConnected();
if (isConnected && !wasConnected) {
delegate.resetConnectBackoff();
}
}
}
}
}

View File

@ -0,0 +1,373 @@
/*
* Copyright 2018, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.android;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.N;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.RuntimeEnvironment.getApiLevel;
import static org.robolectric.Shadows.shadowOf;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import io.grpc.CallOptions;
import io.grpc.ClientCall;
import io.grpc.ManagedChannel;
import io.grpc.MethodDescriptor;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowConnectivityManager;
import org.robolectric.shadows.ShadowNetwork;
import org.robolectric.shadows.ShadowNetworkInfo;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {AndroidChannelBuilderTest.ShadowDefaultNetworkListenerConnectivityManager.class})
public final class AndroidChannelBuilderTest {
private static final NetworkInfo WIFI_CONNECTED =
ShadowNetworkInfo.newInstance(
NetworkInfo.DetailedState.CONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, true);
private static final NetworkInfo WIFI_DISCONNECTED =
ShadowNetworkInfo.newInstance(
NetworkInfo.DetailedState.DISCONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, false);
private final NetworkInfo MOBILE_CONNECTED =
ShadowNetworkInfo.newInstance(
NetworkInfo.DetailedState.CONNECTED,
ConnectivityManager.TYPE_MOBILE,
ConnectivityManager.TYPE_MOBILE_MMS,
true,
true);
private final NetworkInfo MOBILE_DISCONNECTED =
ShadowNetworkInfo.newInstance(
NetworkInfo.DetailedState.DISCONNECTED,
ConnectivityManager.TYPE_MOBILE,
ConnectivityManager.TYPE_MOBILE_MMS,
true,
false);
private ConnectivityManager connectivityManager;
private ShadowConnectivityManager shadowConnectivityManager;
@Before
public void setUp() {
connectivityManager =
(ConnectivityManager)
RuntimeEnvironment.application.getSystemService(Context.CONNECTIVITY_SERVICE);
shadowConnectivityManager = shadowOf(connectivityManager);
}
@Test
public void channelBuilderClassFoundReflectively() {
// This should not throw with OkHttpChannelBuilder on the classpath
AndroidChannelBuilder.forTarget("target");
}
@Test
@Config(sdk = 23)
public void nullContextDoesNotThrow_api23() {
TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel = new AndroidChannelBuilder.AndroidChannel(delegateChannel, null);
// Network change and shutdown should be no-op for the channel without an Android Context
shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
androidChannel.shutdown();
assertThat(delegateChannel.resetCount).isEqualTo(0);
}
@Test
@Config(sdk = 24)
public void nullContextDoesNotThrow_api24() {
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_DISCONNECTED);
TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel = new AndroidChannelBuilder.AndroidChannel(delegateChannel, null);
// Network change and shutdown should be no-op for the channel without an Android Context
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
androidChannel.shutdown();
assertThat(delegateChannel.resetCount).isEqualTo(0);
assertThat(delegateChannel.enterIdleCount).isEqualTo(0);
}
@Test
@Config(sdk = 23)
public void resetConnectBackoff_api23() {
TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel =
new AndroidChannelBuilder.AndroidChannel(
delegateChannel, RuntimeEnvironment.application.getApplicationContext());
assertThat(delegateChannel.resetCount).isEqualTo(0);
// On API levels < 24, the broadcast receiver will invoke resetConnectBackoff() on the first
// connectivity action broadcast regardless of previous connection status
shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
assertThat(delegateChannel.resetCount).isEqualTo(1);
// The broadcast receiver may fire when the active network status has not actually changed
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
assertThat(delegateChannel.resetCount).isEqualTo(1);
// Drop the connection
shadowConnectivityManager.setActiveNetworkInfo(null);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
assertThat(delegateChannel.resetCount).isEqualTo(1);
// Notify that a new but not connected network is available
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_DISCONNECTED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
assertThat(delegateChannel.resetCount).isEqualTo(1);
// Establish a connection
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
assertThat(delegateChannel.resetCount).isEqualTo(2);
// Disconnect, then shutdown the channel and verify that the broadcast receiver has been
// unregistered
shadowConnectivityManager.setActiveNetworkInfo(null);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
androidChannel.shutdown();
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
assertThat(delegateChannel.resetCount).isEqualTo(2);
// enterIdle is not called on API levels < 24
assertThat(delegateChannel.enterIdleCount).isEqualTo(0);
}
@Test
@Config(sdk = 24)
public void resetConnectBackoffAndEnterIdle_api24() {
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_DISCONNECTED);
TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel =
new AndroidChannelBuilder.AndroidChannel(
delegateChannel, RuntimeEnvironment.application.getApplicationContext());
assertThat(delegateChannel.resetCount).isEqualTo(0);
assertThat(delegateChannel.enterIdleCount).isEqualTo(0);
// Establish an initial network connection
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(1);
assertThat(delegateChannel.enterIdleCount).isEqualTo(0);
// Switch to another network to trigger enterIdle()
shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(1);
assertThat(delegateChannel.enterIdleCount).isEqualTo(1);
// Switch to an offline network and then to null
shadowConnectivityManager.setActiveNetworkInfo(WIFI_DISCONNECTED);
shadowConnectivityManager.setActiveNetworkInfo(null);
assertThat(delegateChannel.resetCount).isEqualTo(1);
assertThat(delegateChannel.enterIdleCount).isEqualTo(1);
// Establish a connection
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(2);
assertThat(delegateChannel.enterIdleCount).isEqualTo(1);
// Disconnect, then shutdown the channel and verify that the callback has been unregistered
shadowConnectivityManager.setActiveNetworkInfo(null);
androidChannel.shutdown();
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(2);
assertThat(delegateChannel.enterIdleCount).isEqualTo(1);
}
@Test
@Config(sdk = 24)
public void newChannelWithConnection_entersIdleOnConnectionChange_api24() {
shadowConnectivityManager.setActiveNetworkInfo(MOBILE_CONNECTED);
TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel =
new AndroidChannelBuilder.AndroidChannel(
delegateChannel, RuntimeEnvironment.application.getApplicationContext());
shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(0);
assertThat(delegateChannel.enterIdleCount).isEqualTo(1);
androidChannel.shutdown();
}
@Test
@Config(sdk = 23)
public void shutdownNowUnregistersBroadcastReceiver_api23() {
TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel =
new AndroidChannelBuilder.AndroidChannel(
delegateChannel, RuntimeEnvironment.application.getApplicationContext());
shadowConnectivityManager.setActiveNetworkInfo(null);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
androidChannel.shutdownNow();
shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED);
RuntimeEnvironment.application.sendBroadcast(
new Intent(ConnectivityManager.CONNECTIVITY_ACTION));
assertThat(delegateChannel.resetCount).isEqualTo(0);
}
@Test
@Config(sdk = 24)
public void shutdownNowUnregistersNetworkCallback_api24() {
shadowConnectivityManager.setActiveNetworkInfo(null);
TestChannel delegateChannel = new TestChannel();
ManagedChannel androidChannel =
new AndroidChannelBuilder.AndroidChannel(
delegateChannel, RuntimeEnvironment.application.getApplicationContext());
androidChannel.shutdownNow();
shadowConnectivityManager.setActiveNetworkInfo(WIFI_CONNECTED);
assertThat(delegateChannel.resetCount).isEqualTo(0);
}
/**
* Extends Robolectric ShadowConnectivityManager to handle Android N's
* registerDefaultNetworkCallback API.
*/
@Implements(value = ConnectivityManager.class)
public static class ShadowDefaultNetworkListenerConnectivityManager
extends ShadowConnectivityManager {
private HashSet<ConnectivityManager.NetworkCallback> defaultNetworkCallbacks = new HashSet<>();
public ShadowDefaultNetworkListenerConnectivityManager() {
super();
}
@Override
public void setActiveNetworkInfo(NetworkInfo activeNetworkInfo) {
if (getApiLevel() >= N) {
NetworkInfo previousNetworkInfo = getActiveNetworkInfo();
if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
notifyDefaultNetworkCallbacksOnAvailable(
ShadowNetwork.newInstance(activeNetworkInfo.getType() /* use type as network ID */));
} else if (previousNetworkInfo != null) {
notifyDefaultNetworkCallbacksOnLost(
ShadowNetwork.newInstance(
previousNetworkInfo.getType() /* use type as network ID */));
}
}
super.setActiveNetworkInfo(activeNetworkInfo);
}
private void notifyDefaultNetworkCallbacksOnAvailable(Network network) {
for (ConnectivityManager.NetworkCallback networkCallback : defaultNetworkCallbacks) {
networkCallback.onAvailable(network);
}
}
private void notifyDefaultNetworkCallbacksOnLost(Network network) {
for (ConnectivityManager.NetworkCallback networkCallback : defaultNetworkCallbacks) {
networkCallback.onLost(network);
}
}
@Implementation(minSdk = N)
protected void registerDefaultNetworkCallback(
ConnectivityManager.NetworkCallback networkCallback) {
defaultNetworkCallbacks.add(networkCallback);
}
@Implementation(minSdk = LOLLIPOP)
@Override
public void unregisterNetworkCallback(ConnectivityManager.NetworkCallback networkCallback) {
if (getApiLevel() >= N) {
if (networkCallback != null || defaultNetworkCallbacks.contains(networkCallback)) {
defaultNetworkCallbacks.remove(networkCallback);
}
}
super.unregisterNetworkCallback(networkCallback);
}
}
private static class TestChannel extends ManagedChannel {
int resetCount;
int enterIdleCount;
@Override
public ManagedChannel shutdown() {
return null;
}
@Override
public boolean isShutdown() {
return false;
}
@Override
public boolean isTerminated() {
return false;
}
@Override
public ManagedChannel shutdownNow() {
return null;
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public <RequestT, ResponseT> ClientCall<RequestT, ResponseT> newCall(
MethodDescriptor<RequestT, ResponseT> methodDescriptor, CallOptions callOptions) {
return null;
}
@Override
public String authority() {
return null;
}
@Override
public void resetConnectBackoff() {
resetCount++;
}
@Override
public void enterIdle() {
enterIdleCount++;
}
}
}

View File

@ -23,13 +23,19 @@ ln -s "/tmp/protobuf-${PROTOBUF_VERSION}/$(uname -s)-$(uname -p)" /tmp/protobuf
./gradlew install ./gradlew install
# Build Cronet # Build grpc-cronet
pushd cronet pushd cronet
./cronet_deps.sh ./cronet_deps.sh
../gradlew build ../gradlew build
popd popd
# Build grpc-android
pushd android
../gradlew build
popd
# Build examples # Build examples
cd ./examples/android/clientcache cd ./examples/android/clientcache