Fix proguard configurations

1. When we try and load something via reflection and find the
   class, always throw rather than failing silently if we
   subsequently fail to instantiate an instance. This is
   indicative of a broken proguard setup, and failing silently
   makes it hard to spot.
2. Add library/core proguard configuration to ensure extension
   renderer constructors that we access via reflection are kept.
3. Add demos/main proguard configuration to ensure ImaAdsLoader
   constructor that we access via reflection is kept.
4. Added IMA proguard file to hopefully fix #3723, although I
   wasn't actually able to reproduce the issue.

Issue: #3723

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=183648187
This commit is contained in:
olly 2018-01-29 04:50:15 -08:00 committed by Oliver Woodman
parent e26dc3990d
commit 1f6d161d4d
11 changed files with 171 additions and 75 deletions

View File

@ -27,7 +27,10 @@ android {
release { release {
shrinkResources true shrinkResources true
minifyEnabled true minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt') proguardFiles = [
"proguard-rules.txt",
getDefaultProguardFile('proguard-android.txt')
]
} }
debug { debug {
jniDebuggable = true jniDebuggable = true

View File

@ -0,0 +1,6 @@
# Proguard rules specific to the main demo app.
# Constructor accessed via reflection in PlayerActivity
-keepclassmembers class com.google.android.exoplayer2.ext.ima.ImaAdsLoader {
<init>(android.content.Context, android.net.Uri);
}

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer2.demo; package com.google.android.exoplayer2.demo;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
@ -76,6 +75,7 @@ import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.EventLogger; import com.google.android.exoplayer2.util.EventLogger;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.lang.reflect.Constructor;
import java.net.CookieHandler; import java.net.CookieHandler;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.CookiePolicy; import java.net.CookiePolicy;
@ -353,9 +353,10 @@ public class PlayerActivity extends Activity
releaseAdsLoader(); releaseAdsLoader();
loadedAdTagUri = adTagUri; loadedAdTagUri = adTagUri;
} }
try { MediaSource adsMediaSource = createAdsMediaSource(mediaSource, Uri.parse(adTagUriString));
mediaSource = createAdsMediaSource(mediaSource, Uri.parse(adTagUriString)); if (adsMediaSource != null) {
} catch (Exception e) { mediaSource = adsMediaSource;
} else {
showToast(R.string.ima_not_loaded); showToast(R.string.ima_not_loaded);
} }
} else { } else {
@ -463,19 +464,21 @@ public class PlayerActivity extends Activity
.buildHttpDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null); .buildHttpDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null);
} }
/** /** Returns an ads media source, reusing the ads loader if one exists. */
* Returns an ads media source, reusing the ads loader if one exists. private @Nullable MediaSource createAdsMediaSource(MediaSource mediaSource, Uri adTagUri) {
*
* @throws Exception Thrown if it was not possible to create an ads media source, for example, due
* to a missing dependency.
*/
private MediaSource createAdsMediaSource(MediaSource mediaSource, Uri adTagUri) throws Exception {
// Load the extension source using reflection so the demo app doesn't have to depend on it. // Load the extension source using reflection so the demo app doesn't have to depend on it.
// The ads loader is reused for multiple playbacks, so that ad playback can resume. // The ads loader is reused for multiple playbacks, so that ad playback can resume.
try {
Class<?> loaderClass = Class.forName("com.google.android.exoplayer2.ext.ima.ImaAdsLoader"); Class<?> loaderClass = Class.forName("com.google.android.exoplayer2.ext.ima.ImaAdsLoader");
if (adsLoader == null) { if (adsLoader == null) {
adsLoader = (AdsLoader) loaderClass.getConstructor(Context.class, Uri.class) // Full class names used so the LINT.IfChange rule triggers should any of the classes move.
.newInstance(this, adTagUri); // LINT.IfChange
Constructor<? extends AdsLoader> loaderConstructor =
loaderClass
.asSubclass(AdsLoader.class)
.getConstructor(android.content.Context.class, android.net.Uri.class);
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
adsLoader = loaderConstructor.newInstance(this, adTagUri);
adUiViewGroup = new FrameLayout(this); adUiViewGroup = new FrameLayout(this);
// The demo app has a non-null overlay frame layout. // The demo app has a non-null overlay frame layout.
playerView.getOverlayFrameLayout().addView(adUiViewGroup); playerView.getOverlayFrameLayout().addView(adUiViewGroup);
@ -496,6 +499,12 @@ public class PlayerActivity extends Activity
}; };
return new AdsMediaSource( return new AdsMediaSource(
mediaSource, adMediaSourceFactory, adsLoader, adUiViewGroup, mainHandler, eventLogger); mediaSource, adMediaSourceFactory, adsLoader, adUiViewGroup, mainHandler, eventLogger);
} catch (ClassNotFoundException e) {
// IMA extension not loaded.
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
private void releaseAdsLoader() { private void releaseAdsLoader() {

View File

@ -0,0 +1,6 @@
# Proguard rules specific to the main demo app.
# Constructor accessed via reflection in PlayerActivity
-keepclassmembers class com.google.android.exoplayer2.ext.ima.ImaAdsLoader {
<init>(android.content.Context, android.net.Uri);
}

View File

@ -0,0 +1,6 @@
# Proguard rules specific to the IMA extension.
-keep class com.google.ads.interactivemedia.** { *; }
-keep interface com.google.ads.interactivemedia.** { *; }
-keep class com.google.obf.** { *; }
-keep interface com.google.obf.** { *; }

View File

@ -0,0 +1,6 @@
# Proguard rules specific to the IMA extension.
-keep class com.google.ads.interactivemedia.** { *; }
-keep interface com.google.ads.interactivemedia.** { *; }
-keep class com.google.obf.** { *; }
-keep interface com.google.obf.** { *; }

View File

@ -21,6 +21,7 @@ android {
defaultConfig { defaultConfig {
minSdkVersion project.ext.minSdkVersion minSdkVersion project.ext.minSdkVersion
targetSdkVersion project.ext.targetSdkVersion targetSdkVersion project.ext.targetSdkVersion
consumerProguardFiles 'proguard-rules.txt'
} }
// Workaround to prevent circular dependency on project :testutils. // Workaround to prevent circular dependency on project :testutils.

View File

@ -0,0 +1,25 @@
# Proguard rules specific to the core module.
# Constructors accessed via reflection in DefaultRenderersFactory
-keepclassmembers class com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer {
<init>(boolean, long, android.os.Handler, com.google.android.exoplayer2.video.VideoRendererEventListener, int);
}
-keepclassmembers class com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer {
<init>(android.os.Handler, com.google.android.exoplayer2.audio.AudioRendererEventListener, com.google.android.exoplayer2.audio.AudioProcessor[]);
}
-keepclassmembers class com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer {
<init>(android.os.Handler, com.google.android.exoplayer2.audio.AudioRendererEventListener, com.google.android.exoplayer2.audio.AudioProcessor[]);
}
-keepclassmembers class com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer {
<init>(android.os.Handler, com.google.android.exoplayer2.audio.AudioRendererEventListener, com.google.android.exoplayer2.audio.AudioProcessor[]);
}
# Constructors accessed via reflection in DefaultExtractorsFactory
-keepclassmembers class com.google.android.exoplayer2.ext.flac.FlacExtractor {
<init>();
}
# Constructors accessed via reflection in DefaultDataSource
-keepclassmembers class com.google.android.exoplayer2.ext.rtmp.RtmpDataSource {
<init>();
}

View File

@ -185,18 +185,32 @@ public class DefaultRenderersFactory implements RenderersFactory {
} }
try { try {
Class<?> clazz = // Full class names used for constructor args so the LINT rule triggers if any of them move.
Class.forName("com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer"); // LINT.IfChange
Constructor<?> constructor = clazz.getConstructor(boolean.class, long.class, Handler.class, Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer");
VideoRendererEventListener.class, int.class); Constructor<?> constructor =
Renderer renderer = (Renderer) constructor.newInstance(true, allowedVideoJoiningTimeMs, clazz.getConstructor(
eventHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY); boolean.class,
long.class,
android.os.Handler.class,
com.google.android.exoplayer2.video.VideoRendererEventListener.class,
int.class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
(Renderer)
constructor.newInstance(
true,
allowedVideoJoiningTimeMs,
eventHandler,
eventListener,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
out.add(extensionRendererIndex++, renderer); out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibvpxVideoRenderer."); Log.i(TAG, "Loaded LibvpxVideoRenderer.");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
// Expected if the app was built without the extension. // Expected if the app was built without the extension.
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); // The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating VP9 extension", e);
} }
} }
@ -230,48 +244,67 @@ public class DefaultRenderersFactory implements RenderersFactory {
} }
try { try {
Class<?> clazz = // Full class names used for constructor args so the LINT rule triggers if any of them move.
Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer"); // LINT.IfChange
Constructor<?> constructor = clazz.getConstructor(Handler.class, Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
AudioRendererEventListener.class, AudioProcessor[].class); Constructor<?> constructor =
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener, clazz.getConstructor(
audioProcessors); android.os.Handler.class,
com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
com.google.android.exoplayer2.audio.AudioProcessor[].class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
(Renderer) constructor.newInstance(eventHandler, eventListener, audioProcessors);
out.add(extensionRendererIndex++, renderer); out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibopusAudioRenderer."); Log.i(TAG, "Loaded LibopusAudioRenderer.");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
// Expected if the app was built without the extension. // Expected if the app was built without the extension.
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); // The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating Opus extension", e);
} }
try { try {
Class<?> clazz = // Full class names used for constructor args so the LINT rule triggers if any of them move.
Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer"); // LINT.IfChange
Constructor<?> constructor = clazz.getConstructor(Handler.class, Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
AudioRendererEventListener.class, AudioProcessor[].class); Constructor<?> constructor =
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener, clazz.getConstructor(
audioProcessors); android.os.Handler.class,
com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
com.google.android.exoplayer2.audio.AudioProcessor[].class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
(Renderer) constructor.newInstance(eventHandler, eventListener, audioProcessors);
out.add(extensionRendererIndex++, renderer); out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibflacAudioRenderer."); Log.i(TAG, "Loaded LibflacAudioRenderer.");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
// Expected if the app was built without the extension. // Expected if the app was built without the extension.
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); // The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating FLAC extension", e);
} }
try { try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
// LINT.IfChange
Class<?> clazz = Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer"); Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(Handler.class, Constructor<?> constructor =
AudioRendererEventListener.class, AudioProcessor[].class); clazz.getConstructor(
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener, android.os.Handler.class,
audioProcessors); com.google.android.exoplayer2.audio.AudioRendererEventListener.class,
com.google.android.exoplayer2.audio.AudioProcessor[].class);
// LINT.ThenChange(../../../../../../../proguard-rules.txt)
Renderer renderer =
(Renderer) constructor.newInstance(eventHandler, eventListener, audioProcessors);
out.add(extensionRendererIndex++, renderer); out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded FfmpegAudioRenderer."); Log.i(TAG, "Loaded FfmpegAudioRenderer.");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
// Expected if the app was built without the extension. // Expected if the app was built without the extension.
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); // The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating FFmpeg extension", e);
} }
} }

View File

@ -55,13 +55,17 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
static { static {
Constructor<? extends Extractor> flacExtractorConstructor = null; Constructor<? extends Extractor> flacExtractorConstructor = null;
try { try {
// LINT.IfChange
flacExtractorConstructor = flacExtractorConstructor =
Class.forName("com.google.android.exoplayer2.ext.flac.FlacExtractor") Class.forName("com.google.android.exoplayer2.ext.flac.FlacExtractor")
.asSubclass(Extractor.class).getConstructor(); .asSubclass(Extractor.class)
.getConstructor();
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
// Extractor not found. // Expected if the app was built without the FLAC extension.
} catch (NoSuchMethodException e) { } catch (Exception e) {
// Constructor not found. // The FLAC extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating FLAC extension", e);
} }
FLAC_EXTRACTOR_CONSTRUCTOR = flacExtractorConstructor; FLAC_EXTRACTOR_CONSTRUCTOR = flacExtractorConstructor;
} }

View File

@ -21,7 +21,6 @@ import android.util.Log;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
/** /**
* A {@link DataSource} that supports multiple URI schemes. The supported schemes are: * A {@link DataSource} that supports multiple URI schemes. The supported schemes are:
@ -193,18 +192,16 @@ public final class DefaultDataSource implements DataSource {
private DataSource getRtmpDataSource() { private DataSource getRtmpDataSource() {
if (rtmpDataSource == null) { if (rtmpDataSource == null) {
try { try {
// LINT.IfChange
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.rtmp.RtmpDataSource"); Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.rtmp.RtmpDataSource");
rtmpDataSource = (DataSource) clazz.getDeclaredConstructor().newInstance(); rtmpDataSource = (DataSource) clazz.getConstructor().newInstance();
// LINT.ThenChange(../../../../../../../../proguard-rules.txt)
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
// Expected if the app was built without the RTMP extension.
Log.w(TAG, "Attempting to play RTMP stream without depending on the RTMP extension"); Log.w(TAG, "Attempting to play RTMP stream without depending on the RTMP extension");
} catch (InstantiationException e) { } catch (Exception e) {
Log.e(TAG, "Error instantiating RtmpDataSource", e); // The RTMP extension is present, but instantiation failed.
} catch (IllegalAccessException e) { throw new RuntimeException("Error instantiating RTMP extension", e);
Log.e(TAG, "Error instantiating RtmpDataSource", e);
} catch (NoSuchMethodException e) {
Log.e(TAG, "Error instantiating RtmpDataSource", e);
} catch (InvocationTargetException e) {
Log.e(TAG, "Error instantiating RtmpDataSource", e);
} }
if (rtmpDataSource == null) { if (rtmpDataSource == null) {
rtmpDataSource = baseDataSource; rtmpDataSource = baseDataSource;