public final class

CronetUtil

extends java.lang.Object

 java.lang.Object

↳androidx.media3.datasource.cronet.CronetUtil

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-datasource-cronet', version: '1.0.0-alpha03'

  • groupId: androidx.media3
  • artifactId: media3-datasource-cronet
  • version: 1.0.0-alpha03

Artifact androidx.media3:media3-datasource-cronet:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

Cronet utility methods.

Summary

Methods
public static CronetEnginebuildCronetEngine(Context context)

Builds a suitable for use with CronetDataSource.

public static CronetEnginebuildCronetEngine(Context context, java.lang.String userAgent, boolean preferGooglePlayServices)

Builds a suitable for use with CronetDataSource.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

public static CronetEngine buildCronetEngine(Context context)

Builds a suitable for use with CronetDataSource. When choosing a to build the , disabled providers are not considered. Neither are fallback providers, since it's more efficient to use DefaultHttpDataSource than it is to use CronetDataSource with a fallback .

Note that it's recommended for applications to create only one instance of , so if your application already has an instance for performing other networking, then that instance should be used and calling this method is unnecessary. See the Android developer guide to learn more about using Cronet for network operations.

Parameters:

context: A context.

Returns:

The , or null if no suitable engine could be built.

public static CronetEngine buildCronetEngine(Context context, java.lang.String userAgent, boolean preferGooglePlayServices)

Builds a suitable for use with CronetDataSource. When choosing a to build the , disabled providers are not considered. Neither are fallback providers, since it's more efficient to use DefaultHttpDataSource than it is to use CronetDataSource with a fallback .

Note that it's recommended for applications to create only one instance of , so if your application already has an instance for performing other networking, then that instance should be used and calling this method is unnecessary. See the Android developer guide to learn more about using Cronet for network operations.

Parameters:

context: A context.
userAgent: A default user agent, or null to use a default user agent of the .
preferGooglePlayServices: Whether Cronet from Google Play Services should be preferred over Cronet Embedded, if both are available.

Returns:

The , or null if no suitable engine could be built.

Source

/*
 * Copyright 2021 The Android Open Source Project
 *
 * 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 androidx.media3.datasource.cronet;

import static java.lang.Math.min;

import android.content.Context;
import androidx.annotation.Nullable;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DefaultHttpDataSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.chromium.net.CronetEngine;
import org.chromium.net.CronetProvider;

/** Cronet utility methods. */
@UnstableApi
public final class CronetUtil {

  private static final String TAG = "CronetUtil";

  /**
   * Builds a {@link CronetEngine} suitable for use with {@link CronetDataSource}. When choosing a
   * {@link CronetProvider Cronet provider} to build the {@link CronetEngine}, disabled providers
   * are not considered. Neither are fallback providers, since it's more efficient to use {@link
   * DefaultHttpDataSource} than it is to use {@link CronetDataSource} with a fallback {@link
   * CronetEngine}.
   *
   * <p>Note that it's recommended for applications to create only one instance of {@link
   * CronetEngine}, so if your application already has an instance for performing other networking,
   * then that instance should be used and calling this method is unnecessary. See the <a
   * href="https://developer.android.com/guide/topics/connectivity/cronet/start">Android developer
   * guide</a> to learn more about using Cronet for network operations.
   *
   * @param context A context.
   * @return The {@link CronetEngine}, or {@code null} if no suitable engine could be built.
   */
  @Nullable
  public static CronetEngine buildCronetEngine(Context context) {
    return buildCronetEngine(context, /* userAgent= */ null, /* preferGooglePlayServices= */ false);
  }

  /**
   * Builds a {@link CronetEngine} suitable for use with {@link CronetDataSource}. When choosing a
   * {@link CronetProvider Cronet provider} to build the {@link CronetEngine}, disabled providers
   * are not considered. Neither are fallback providers, since it's more efficient to use {@link
   * DefaultHttpDataSource} than it is to use {@link CronetDataSource} with a fallback {@link
   * CronetEngine}.
   *
   * <p>Note that it's recommended for applications to create only one instance of {@link
   * CronetEngine}, so if your application already has an instance for performing other networking,
   * then that instance should be used and calling this method is unnecessary. See the <a
   * href="https://developer.android.com/guide/topics/connectivity/cronet/start">Android developer
   * guide</a> to learn more about using Cronet for network operations.
   *
   * @param context A context.
   * @param userAgent A default user agent, or {@code null} to use a default user agent of the
   *     {@link CronetEngine}.
   * @param preferGooglePlayServices Whether Cronet from Google Play Services should be preferred
   *     over Cronet Embedded, if both are available.
   * @return The {@link CronetEngine}, or {@code null} if no suitable engine could be built.
   */
  @Nullable
  public static CronetEngine buildCronetEngine(
      Context context, @Nullable String userAgent, boolean preferGooglePlayServices) {
    List<CronetProvider> cronetProviders = new ArrayList<>(CronetProvider.getAllProviders(context));
    // Remove disabled and fallback Cronet providers from list.
    for (int i = cronetProviders.size() - 1; i >= 0; i--) {
      if (!cronetProviders.get(i).isEnabled()
          || CronetProvider.PROVIDER_NAME_FALLBACK.equals(cronetProviders.get(i).getName())) {
        cronetProviders.remove(i);
      }
    }
    // Sort remaining providers by type and version.
    CronetProviderComparator providerComparator =
        new CronetProviderComparator(preferGooglePlayServices);
    Collections.sort(cronetProviders, providerComparator);
    for (int i = 0; i < cronetProviders.size(); i++) {
      String providerName = cronetProviders.get(i).getName();
      try {
        CronetEngine.Builder cronetEngineBuilder = cronetProviders.get(i).createBuilder();
        if (userAgent != null) {
          cronetEngineBuilder.setUserAgent(userAgent);
        }
        CronetEngine cronetEngine = cronetEngineBuilder.build();
        Log.d(TAG, "CronetEngine built using " + providerName);
        return cronetEngine;
      } catch (SecurityException e) {
        Log.w(
            TAG,
            "Failed to build CronetEngine. Please check that the process has "
                + "android.permission.ACCESS_NETWORK_STATE.");
      } catch (UnsatisfiedLinkError e) {
        Log.w(
            TAG,
            "Failed to link Cronet binaries. Please check that native Cronet binaries are"
                + "bundled into your app.");
      }
    }
    Log.w(TAG, "CronetEngine could not be built.");
    return null;
  }

  private CronetUtil() {}

  private static class CronetProviderComparator implements Comparator<CronetProvider> {

    /*
     * Copy of com.google.android.gms.net.CronetProviderInstaller.PROVIDER_NAME. We have our own
     * copy because GMSCore CronetProvider classes are unavailable in some (internal to Google)
     * build configurations.
     */
    private static final String GOOGLE_PLAY_SERVICES_PROVIDER_NAME =
        "Google-Play-Services-Cronet-Provider";

    private final boolean preferGooglePlayServices;

    public CronetProviderComparator(boolean preferGooglePlayServices) {
      this.preferGooglePlayServices = preferGooglePlayServices;
    }

    @Override
    public int compare(CronetProvider providerLeft, CronetProvider providerRight) {
      int providerComparison = getPriority(providerLeft) - getPriority(providerRight);
      if (providerComparison != 0) {
        return providerComparison;
      }
      return -compareVersionStrings(providerLeft.getVersion(), providerRight.getVersion());
    }

    /**
     * Returns the priority score for a Cronet provider, where a smaller score indicates higher
     * priority.
     */
    private int getPriority(CronetProvider provider) {
      String providerName = provider.getName();
      if (CronetProvider.PROVIDER_NAME_APP_PACKAGED.equals(providerName)) {
        return 1;
      } else if (GOOGLE_PLAY_SERVICES_PROVIDER_NAME.equals(providerName)) {
        return preferGooglePlayServices ? 0 : 2;
      } else {
        return 3;
      }
    }

    /** Compares version strings of format "12.123.35.23". */
    private static int compareVersionStrings(
        @Nullable String versionLeft, @Nullable String versionRight) {
      if (versionLeft == null || versionRight == null) {
        return 0;
      }
      String[] versionStringsLeft = Util.split(versionLeft, "\\.");
      String[] versionStringsRight = Util.split(versionRight, "\\.");
      int minLength = min(versionStringsLeft.length, versionStringsRight.length);
      for (int i = 0; i < minLength; i++) {
        if (!versionStringsLeft[i].equals(versionStringsRight[i])) {
          try {
            int versionIntLeft = Integer.parseInt(versionStringsLeft[i]);
            int versionIntRight = Integer.parseInt(versionStringsRight[i]);
            return versionIntLeft - versionIntRight;
          } catch (NumberFormatException e) {
            return 0;
          }
        }
      }
      return 0;
    }
  }
}