Android SDK API-19 Kitkat compat SSL patching

Online playback on android devices with api level <= 19 (kitkat or lower) might not work because of an outdated security provider used for securing network connections.

SSL algorithms have seen lot of updates since the release of Android Kitkat. SSLv3/TLSv1 which was used at that time has since been deprecated because of security vulnerabilities. It is recommended to not support SSLv3 for HTTPS for high-security requirements. Our license exchange and media delivery networks do not support SSLv3. In Android Kitkat, the new TLS1.1 and above is not enabled by default.

Network requests may fail with an exception like:

javax.net.ssl.SSLHandshakeException:
javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error

This can be corrected by patching the security provider on device using the play services auth library before making any network requests.

More details regarding the error and the solution are available here:

  1. https://stackoverflow.com/a/36892715/1615721

  2. https://developer.android.com/training/articles/security-gms-provider.html

Sample code to patch SSL configuration in Android:

To accomplish this patching asynchronously, follow the below instructions. More details and sample in above developer.android.com link.

First, add the required dependency to your app module's build.gradle:

compile 'com.google.android.gms:play-services-auth:16.0.1'

In your PlayerActivity:

/**
* Player activity.
*/
public class PlayerActivity extends Activity
  implements ProviderInstaller.ProviderInstallListener {

private static final int ERROR_DIALOG_REQUEST_CODE = 1;

private boolean retryProviderInstall;

//Update the security provider when the activity is created.
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // onCreate implementation ...
  // ...


  if (Build.VERSION.SDK_INT <= 19) {
    ProviderInstaller.installIfNeededAsync(this, this);
  } else {
    // continue to loading video
    loadVideo();
  }
}

private void loadVideo() {
  // initialize VdoPlayerFragment ...
}

/**
  * This method is only called if the provider is successfully updated
  * (or is already up-to-date).
  */
@Override
protected void onProviderInstalled() {
  // Provider is up-to-date, app can make secure network calls.
  loadVideo();
}

/**
  * This method is called if updating fails; the error code indicates
  * whether the error is recoverable.
  */
@Override
protected void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
  GoogleApiAvailability availability = GoogleApiAvailability.getInstance();
  if (availability.isUserRecoverableError(errorCode)) {
    // Recoverable error. Show a dialog prompting the user to
    // install/update/enable Google Play services.
    availability.showErrorDialogFragment(
        this,
        errorCode,
        ERROR_DIALOG_REQUEST_CODE,
        new DialogInterface.OnCancelListener() {
          @Override
          public void onCancel(DialogInterface dialog) {
            // The user chose not to take the recovery action
            onProviderInstallerNotAvailable();
          }
        });
  } else {
    // Google Play services is not available.
    onProviderInstallerNotAvailable();
  }
}

@Override
protected void onActivityResult(int requestCode, int resultCode,
    Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if (requestCode == ERROR_DIALOG_REQUEST_CODE) {
    // Adding a fragment via GoogleApiAvailability.showErrorDialogFragment
    // before the instance state is restored throws an error. So instead,
    // set a flag here, which will cause the fragment to delay until
    // onPostResume.
    retryProviderInstall = true;
  }
}

/**
  * On resume, check to see if we flagged that we need to reinstall the
  * provider.
  */
@Override
protected void onPostResume() {
  super.onPostResume();
  if (retryProviderInstall) {
    // We can now safely retry installation.
    ProviderInstaller.installIfNeededAsync(this, this);
  }
  retryProviderInstall = false;
}

private void onProviderInstallerNotAvailable() {
  // This is reached if the provider cannot be updated for some reason.
  // Playback may not work
  // Indicate error to user
}
}