262 lines
11 KiB
Java
262 lines
11 KiB
Java
// Copyright 2017 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package org.chromium.net;
|
|
|
|
import android.content.Context;
|
|
import android.util.Log;
|
|
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.LinkedHashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Provides a factory method to create {@link CronetEngine.Builder} instances. A {@code
|
|
* CronetEngine.Builder} instance can be used to create a specific {@link CronetEngine}
|
|
* implementation. To get the list of available {@link CronetProvider}s call {@link
|
|
* #getAllProviders(Context)}.
|
|
* <p/>
|
|
* <b>NOTE:</b> This class is for advanced users that want to select a particular
|
|
* Cronet implementation. Most users should simply use {@code new} {@link
|
|
* CronetEngine.Builder#CronetEngine.Builder(android.content.Context)}.
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public abstract class CronetProvider {
|
|
/**
|
|
* String returned by {@link CronetProvider#getName} for {@link CronetProvider} that provides
|
|
* native Cronet implementation packaged inside an application. This implementation offers
|
|
* significantly higher performance relative to the fallback Cronet implementations (see {@link
|
|
* #PROVIDER_NAME_FALLBACK}).
|
|
*/
|
|
public static final String PROVIDER_NAME_APP_PACKAGED = "App-Packaged-Cronet-Provider";
|
|
|
|
/**
|
|
* String returned by {@link CronetProvider#getName} for {@link CronetProvider} that provides
|
|
* Cronet implementation based on the system's {@link java.net.HttpURLConnection}
|
|
* implementation. This implementation offers significantly degraded performance relative to
|
|
* native Cronet implementations (see {@link #PROVIDER_NAME_APP_PACKAGED}).
|
|
*/
|
|
public static final String PROVIDER_NAME_FALLBACK = "Fallback-Cronet-Provider";
|
|
|
|
/**
|
|
* The name of an optional key in the app string resource file that contains the class name of
|
|
* an alternative {@code CronetProvider} implementation.
|
|
*/
|
|
private static final String RES_KEY_CRONET_IMPL_CLASS = "CronetProviderClassName";
|
|
|
|
private static final String TAG = CronetProvider.class.getSimpleName();
|
|
|
|
protected final Context mContext;
|
|
|
|
protected CronetProvider(Context context) {
|
|
if (context == null) {
|
|
throw new IllegalArgumentException("Context must not be null");
|
|
}
|
|
mContext = context;
|
|
}
|
|
|
|
/**
|
|
* Creates and returns an instance of {@link CronetEngine.Builder}.
|
|
* <p/>
|
|
* <b>NOTE:</b> This class is for advanced users that want to select a particular
|
|
* Cronet implementation. Most users should simply use {@code new} {@link
|
|
* CronetEngine.Builder#CronetEngine.Builder(android.content.Context)}.
|
|
*
|
|
* @return {@code CronetEngine.Builder}.
|
|
* @throws IllegalStateException if the provider is not enabled (see {@link #isEnabled}.
|
|
*/
|
|
public abstract CronetEngine.Builder createBuilder();
|
|
|
|
/**
|
|
* Returns the provider name. The well-know provider names include:
|
|
* <ul>
|
|
* <li>{@link #PROVIDER_NAME_APP_PACKAGED}</li>
|
|
* <li>{@link #PROVIDER_NAME_FALLBACK}</li>
|
|
* </ul>
|
|
*
|
|
* @return provider name.
|
|
*/
|
|
public abstract String getName();
|
|
|
|
/**
|
|
* Returns the provider version. The version can be used to select the newest available provider
|
|
* if multiple providers are available.
|
|
*
|
|
* @return provider version.
|
|
*/
|
|
public abstract String getVersion();
|
|
|
|
/**
|
|
* Returns whether the provider is enabled and can be used to instantiate the Cronet engine. A
|
|
* provider being out-of-date (older than the API) and needing updating is one potential reason
|
|
* it could be disabled. Please read the provider documentation for enablement procedure.
|
|
*
|
|
* @return {@code true} if the provider is enabled.
|
|
*/
|
|
public abstract boolean isEnabled();
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "["
|
|
+ "class="
|
|
+ getClass().getName()
|
|
+ ", "
|
|
+ "name="
|
|
+ getName()
|
|
+ ", "
|
|
+ "version="
|
|
+ getVersion()
|
|
+ ", "
|
|
+ "enabled="
|
|
+ isEnabled()
|
|
+ "]";
|
|
}
|
|
|
|
/** Name of the HttpEngine Native {@link CronetProvider} class. */
|
|
private static final String HTTPENGINE_NATIVE_PROVIDER_CLASS =
|
|
"org.chromium.net.impl.HttpEngineNativeProvider";
|
|
|
|
/** Name of the Java {@link CronetProvider} class. */
|
|
private static final String JAVA_CRONET_PROVIDER_CLASS =
|
|
"org.chromium.net.impl.JavaCronetProvider";
|
|
|
|
/** Name of the native {@link CronetProvider} class. */
|
|
private static final String NATIVE_CRONET_PROVIDER_CLASS =
|
|
"org.chromium.net.impl.NativeCronetProvider";
|
|
|
|
/** {@link CronetProvider} class that is packaged with Google Play Services. */
|
|
private static final String PLAY_SERVICES_CRONET_PROVIDER_CLASS =
|
|
"com.google.android.gms.net.PlayServicesCronetProvider";
|
|
|
|
/**
|
|
* {@link CronetProvider} a deprecated class that may be packaged with some old versions of
|
|
* Google Play Services.
|
|
*/
|
|
private static final String GMS_CORE_CRONET_PROVIDER_CLASS =
|
|
"com.google.android.gms.net.GmsCoreCronetProvider";
|
|
|
|
/**
|
|
* Returns an unmodifiable list of all available {@link CronetProvider}s. The providers are
|
|
* returned in no particular order. Some of the returned providers may be in a disabled state
|
|
* and should be enabled by the invoker. See {@link CronetProvider#isEnabled()}.
|
|
*
|
|
* @return the list of available providers.
|
|
*/
|
|
public static List<CronetProvider> getAllProviders(Context context) {
|
|
// Use LinkedHashSet to preserve the order and eliminate duplicate providers.
|
|
Set<CronetProvider> providers = new LinkedHashSet<>();
|
|
addCronetProviderFromResourceFile(context, providers);
|
|
addCronetProviderImplByClassName(
|
|
context, PLAY_SERVICES_CRONET_PROVIDER_CLASS, providers, false);
|
|
addCronetProviderImplByClassName(context, GMS_CORE_CRONET_PROVIDER_CLASS, providers, false);
|
|
addCronetProviderImplByClassName(context, NATIVE_CRONET_PROVIDER_CLASS, providers, false);
|
|
addCronetProviderImplByClassName(
|
|
context, HTTPENGINE_NATIVE_PROVIDER_CLASS, providers, false);
|
|
addCronetProviderImplByClassName(context, JAVA_CRONET_PROVIDER_CLASS, providers, false);
|
|
return Collections.unmodifiableList(new ArrayList<>(providers));
|
|
}
|
|
|
|
/**
|
|
* Attempts to add a new provider referenced by the class name to a set.
|
|
*
|
|
* @param className the class name of the provider that should be instantiated.
|
|
* @param providers the set of providers to add the new provider to.
|
|
* @return {@code true} if the provider was added to the set; {@code false} if the provider
|
|
* couldn't be instantiated.
|
|
*/
|
|
private static boolean addCronetProviderImplByClassName(
|
|
Context context, String className, Set<CronetProvider> providers, boolean logError) {
|
|
ClassLoader loader = context.getClassLoader();
|
|
try {
|
|
Class<? extends CronetProvider> providerClass =
|
|
loader.loadClass(className).asSubclass(CronetProvider.class);
|
|
Constructor<? extends CronetProvider> ctor =
|
|
providerClass.getConstructor(Context.class);
|
|
providers.add(ctor.newInstance(context));
|
|
return true;
|
|
} catch (InstantiationException e) {
|
|
logReflectiveOperationException(className, logError, e);
|
|
} catch (InvocationTargetException e) {
|
|
logReflectiveOperationException(className, logError, e);
|
|
} catch (NoSuchMethodException e) {
|
|
logReflectiveOperationException(className, logError, e);
|
|
} catch (IllegalAccessException e) {
|
|
logReflectiveOperationException(className, logError, e);
|
|
} catch (ClassNotFoundException e) {
|
|
logReflectiveOperationException(className, logError, e);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* De-duplicates exception handling logic in {@link #addCronetProviderImplByClassName}. It
|
|
* should be removed when support of API Levels lower than 19 is deprecated.
|
|
*/
|
|
private static void logReflectiveOperationException(
|
|
String className, boolean logError, Exception e) {
|
|
if (logError) {
|
|
Log.e(TAG, "Unable to load provider class: " + className, e);
|
|
} else {
|
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
Log.d(
|
|
TAG,
|
|
"Tried to load "
|
|
+ className
|
|
+ " provider class but it wasn't"
|
|
+ " included in the app classpath");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to add a provider specified in the app resource file to a set.
|
|
*
|
|
* @param providers the set of providers to add the new provider to.
|
|
* @return {@code true} if the provider was added to the set; {@code false} if the app resources
|
|
* do not include the string with {@link #RES_KEY_CRONET_IMPL_CLASS} key.
|
|
* @throws RuntimeException if the provider cannot be found or instantiated.
|
|
*/
|
|
// looking up resources from other apps requires the use of getIdentifier()
|
|
@SuppressWarnings("DiscouragedApi")
|
|
private static boolean addCronetProviderFromResourceFile(
|
|
Context context, Set<CronetProvider> providers) {
|
|
int resId =
|
|
context.getResources()
|
|
.getIdentifier(
|
|
RES_KEY_CRONET_IMPL_CLASS, "string", context.getPackageName());
|
|
// Resource not found
|
|
if (resId == 0) {
|
|
// The resource wasn't included in the app; therefore, there is nothing to add.
|
|
return false;
|
|
}
|
|
String className = context.getResources().getString(resId);
|
|
|
|
// If the resource specifies a well known provider, don't load it because
|
|
// there will be an attempt to load it anyways.
|
|
if (className == null
|
|
|| className.equals(PLAY_SERVICES_CRONET_PROVIDER_CLASS)
|
|
|| className.equals(GMS_CORE_CRONET_PROVIDER_CLASS)
|
|
|| className.equals(JAVA_CRONET_PROVIDER_CLASS)
|
|
|| className.equals(NATIVE_CRONET_PROVIDER_CLASS)) {
|
|
return false;
|
|
}
|
|
|
|
if (!addCronetProviderImplByClassName(context, className, providers, true)) {
|
|
Log.e(
|
|
TAG,
|
|
"Unable to instantiate Cronet implementation class "
|
|
+ className
|
|
+ " that is listed as in the app string resource file under "
|
|
+ RES_KEY_CRONET_IMPL_CLASS
|
|
+ " key");
|
|
}
|
|
return true;
|
|
}
|
|
}
|