本篇只供学习,本人未用作商业用途。他人若未经允许,也不得使用以及参考本篇中提到的源码以及附件进行谋利,违者必究
由于工作原因,很长时间没搞破解了。几个月前只是简单对某镖游戏dump了il2cpp与meta-data,昨天试玩了一下,竟然要更新,fuck! fuck! fuck!
然后随便在小米应用商店下载了个小游戏,广告太多,就以去广告为目标 练一下手吧。


dex文件查看工具
Jeb(windows)
mac可参考这里
apktool2.5
1 2 3 | java - jar ~ / env / apktool_2. 5.0 .jar d app - debug.apk java - jar ~ / env / apktool_2. 5.0 .jar d - r app - debug.apk / / Do not decode resources. 不反编译资源文件 java - jar ~ / env / apktool_2. 5.0 .jar d - s app - debug.apk / / Do not decode sources. 不反编译源码文件 |
Baksmali-2.5.2/smali-2.5.2
1 2 | java - jar ~ / env / baksmali - 2.5 . 2.jar d classes4.dex / / 反编译dex文件,输出目录默认位out java - jar ~ / env / smali - 2.5 . 2.jar a out / / 将out目录下面的smali源文件编译为dex文件,输出默认为out.dex |
keytool
该cmd是为了生成keystore文件,重签名apk时需要。keytool命令在java安装目录/bin下面
1 | keytool - genkey - alias mine.keystore - keyalg RSA - validity 30000 - keystore mine.keystore |
jarsigner
该cmd是为了将apk进行重签名,重签名需要使用keystore文件。jarsigner命令也在java安装目录/bin下面
1 2 3 4 5 | jarsigner - verbose - keystore mine.keystore - signedjar new.apk old.apk mine.keystore - keystore:keystore 的名称 new.apk:签名后的apk old.apk:签名前的apk |
adb shell dumpsys activity activities top
打印手机上当前的未销毁的activity列表
adb shell dumpsys window windows
打印手机上当前的window,默认Z-order
dumpsys看看当前的广告栏属于哪个activity

activity为com.outfit7.mytalkingtomfree/com.jinke.Main
分析后,发现Main的父类中含有可疑的布局代码

上图中onCreate中调用了initContentLayout函数
1 2 3 4 5 6 7 | private void initContentLayout() { WeakReference v0 = this.banneContainerReference; if (v0 = = null || v0.get() = = null) { this.banneContainerReference = new WeakReference(LayoutInflater. from (this.activityWeakReference.get()).inflate(layout.adjustable_banner_layout, this.findViewById( 0x1020002 ), true)); Log.d(MaoActivity.TAG, "setAdjustableBannerXY create view" ); } } |
这个函数中有一个Log.d,我们可通过查看程序运行日志确认该函数中的if语句是否执行了。
我这边在app冷启动时开始抓log,确实有这个日志
1 | 06 - 16 17 : 44 : 45.360 27784 27784 D com.outfit7.jinke.MaoActivity: setAdjustableBannerXY create view |
或者大家也可以通过打断点调试,确认这里是否执行了。
代码拆分说明:
1 | LayoutInflater. from (this.activityWeakReference.get()) 获取当前显示的activity,然后基于该activity对应的上下文创建一个LayoutInflater |
1 | inflate(layout.adjustable_banner_layout, this.findViewById( 0x1020002 ), true)); |
LayoutInflater.inflate函数,代码加载布局,其函数声明为:
1 | public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) |
所以布局文件为layout.adjustable_banner_layout,这个布局的名字看起来就像是广告位。
1 2 3 4 5 | <?xml version = "1.0" encoding = "utf-8" ?> = "vertical" android: id = "@id/mao_adjustable_container_root" android:layout_width = "fill_parent" android:layout_height = "fill_parent"
xmlns:android = "http://schemas.android.com/apk/res/android" > id = "@id/mao_adjustable_ad" android:layout_width = "wrap_content" android:layout_height = "wrap_content" / >
< / RelativeLayout> |
xml中整个view的layout_height为fill_parent,这个高度值可能太大了,看起来应该不是主页上方广告的布局。

直接查看当前Activity的DecorView,查看其children,发现0,0 1080,206这个boundary就是广告显示的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class b extends FrameLayout { public class com.miui.zeus.mimo.sdk.ad.banner.b$ 1 implements View$OnClickListener { public com.miui.zeus.mimo.sdk.ad.banner.b$ 1 (b arg1) { this.a = arg1; super (); } public void onClick(View arg2) { b v2 = this.a; a v0 = v2.e; if (v0 ! = null) { v0.a(b.a(v2)); } } } ... ... ... } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | / / com.miui.zeus.mimo.sdk.ad.banner.b private void a(View arg3) { if (this.d()) { this.b = arg3.findViewById(l.c( "mimo_banner_view_summary" )); this.f = arg3.findViewById(l.c( "mimo_banner_border" )); Glide.with(this.i).load(Integer.valueOf(l.b( "mimo_banner_border" ))).into(this.f); this.k = arg3.findViewById(l.c( "mimo_banner_view_flipper" )); } else { this.a = arg3.findViewById(l.c( "mimo_banner_view_image" )); } this.c = arg3.findViewById(l.c( "mimo_banner_view_ad_mark" )); this.d = arg3.findViewById(l.c( "mimo_banner_view_close" )); this.h = new d(); this.d.setOnClickListener(new View$OnClickListener() { public void onClick(View arg1) { a v1 = this.a.e; if (v1 ! = null) { v1.b(); } } }); ((FrameLayout)this).setOnClickListener(this.g); } |
jeb中查看交叉引用非常方便,选中函数a,然后按下快捷键x,发现只有一个地方调用了a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | / / com.miui.zeus.mimo.sdk.ad.banner.b public void a(c arg3) { this.m = arg3; int v0 = com.miui.zeus.mimo.sdk.utils.a.a(arg3.Y()); this.j = v0; if (v0 = = 0 ) { this.j = l.a( "mimo_banner_view_layout" ); } this.a(LayoutInflater. from (this.i).inflate(this.j, ((ViewGroup)this))); String v3 = arg3.M(); if (TextUtils.isEmpty(((CharSequence)v3))) { this.b(); return ; } this.a(v3); } |
这个函数中使用了LayoutInflator。 YES!!!!!无疑了,这个调用链肯定初始化了主页上方的广告位。
继续查找a(c arg3)的调用者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | / / com.miui.zeus.mimo.sdk.ad.banner public void a(com.miui.zeus.mimo.sdk.server.api.c arg3, ViewGroup arg4, BannerInteractionListener arg5) { this.l = System.currentTimeMillis(); String v0 = "BannerUIController" ; j.a(v0, "showBanner" ); this.e = arg3; this.h = arg5; if (arg3 = = null) { this.a(com.miui.zeus.mimo.sdk.utils.error.a.e.au, com.miui.zeus.mimo.sdk.utils.error.a.e.av); j.b(v0, "Empty splash ad info view arguments" ); return ; } this.f = arg4; this.c.post(new Runnable(arg3) { public void run() { try { c.a(this.b, new b(c.a(this.b))); c.b(this.b).a(this.b); c.b(this.b).a(this.a); } catch(Exception v0) { j.b( "BannerUIController" , "Failed to create view" , ((Throwable)v0)); this.b.a(); } } }); } |
这个函数中的字符很给力,showBanner, Failed to create view等
a(com.miui.zeus.mimo.sdk.server.api.c arg3, ViewGroup arg4, BannerInteractionListener arg5)的调用者
1 2 3 4 5 6 7 | / / com.miui.zeus.mimo.sdk.ad.banner public void a(ViewGroup arg3, BannerInteractionListener arg4) { this.f = arg3; b v3 = new b(this, arg4); this.q = v3; this.g.a(this.d, this.f, ((BannerInteractionListener)v3)); } |
a(ViewGroup arg3, BannerInteractionListener arg4)的调用者
1 2 3 4 5 6 7 8 | / / com.miui.zeus.mimo.sdk.BannerAd public void showAd(ViewGroup arg3, BannerInteractionListener arg4) { if (arg3 = = null) { j.b( "BannerAd" , "showAd failed, container can not be null" ); } this.mAdImpl.a(arg3, arg4); } |
showAd(ViewGroup arg3, BannerInteractionListener arg4)的调用者
1 2 3 4 5 6 7 8 9 10 11 12 13 | public void onBannerAdLoadSuccess() { String v0 = "MiMoAdBannerAdapter" ; MLog.w(v0, "load banner ad success ,start render" ); if (MiMoAdBannerAdapter.access$ 100 (this.a) ! = null) { MLog.w(v0, "start show banner ad" ); MiMoAdBannerAdapter.access$ 100 (this.a).showAd(MiMoAdBannerAdapter.access$ 200 (this.a), MiMoAdBannerAdapter.access$ 300 (this.a)); } else { MLog.w(v0, "load banner ad success,but banner ad is null" ); this.a.notifyLoadError(new MMAdError( - 2000 )); this.a.trackDspLoad(String.valueOf( - 2000 ), null); } } |
上述调用栈关系也可以使用动态调试的方式,一步到位。我没有使用调试的方法,因为这个广告在我的手机上出现的概率太低,想调试一次太难了,大家理解一下哈。
源头去除: onBannerAdLoadSuccess函数中,showAd调用移除。缺点:可能影响了app原本逻辑
尾部去除:设置view的visibility。优点:基本不影响app原本逻辑
昨天调试的时候保存了广告栏显示的代码源头。大家可以自行分析一下,看是否跟我找到的一样。
1 2 3 4 5 | / / com.xiaomi.ad.mediation.mimo.MiMoAdBannerAdapter public void loadAndShow(AdInternalConfig arg1, AdLoadAndShowListener arg2, AdLoadAndShowInteractionListener arg3) { super .loadAndShow(arg1, arg2, arg3); AndroidUtils.runOnMainThread(this.mMainHandler, new MiMoAdBannerAdapter$a(this, arg1)); } |
1 2 3 4 5 6 7 8 9 10 11 | public class MiMoAdBannerAdapter$a implements Runnable { public MiMoAdBannerAdapter$a(MiMoAdBannerAdapter arg1, AdInternalConfig arg2) { this.b = arg1; this.a = arg2; super (); } public void run() { this.b.loadBannerAd(this.a); } } |
1 2 3 4 5 6 7 | java - jar ~ / env / baksmali - 2.5 . 2.jar d classes2.dex cd out / com / miui / zeus / mimo / sdk / ad / banner vim a\\$ 3.smali / / 去掉invoke - virtual。相当于run()函数什么都没有做 |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | admin@C02D7132MD6R bin % adb shell dumpsys activity activities ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities) Display #0 (activities from top to bottom): Stack #1: ... ... * Hist #1: ActivityRecord{1f34e49 u0 com.outfit7.mytalkingtomfree/com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity t3} packageName = com.outfit7.mytalkingtomfree processName = com.outfit7.mytalkingtomfree launchedFromUid = 10139 launchedFromPackage = com.outfit7.mytalkingtomfree userId = 0 app = ProcessRecord{ 382ce36 4930 :com.outfit7.mytalkingtomfree / u0a139} Intent { cmp = com.outfit7.mytalkingtomfree / com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity (has extras) } ... ... resizeMode = RESIZE_MODE_RESIZEABLE * Hist #0: ActivityRecord{64ced92 u0 com.outfit7.mytalkingtomfree/com.jinke.Main t3} packageName = com.outfit7.mytalkingtomfree processName = com.outfit7.mytalkingtomfree launchedFromUid = 10139 launchedFromPackage = com.outfit7.mytalkingtomfree userId = 0 app = ProcessRecord{ 382ce36 4930 :com.outfit7.mytalkingtomfree / u0a139} Intent { flg = 0x10000000 cmp = com.outfit7.mytalkingtomfree / com.jinke.Main (has extras) } |
先挂断点查看startActivity调用栈, 我断在了instrumentation->execSartActivity方法

1.activity确实被拦截掉了,但是页面会卡在一个view中,显示加载中...
2.按返回键尝试将卡view的界面关掉,礼物盒没有正常开启奖励,肯定是影响了游戏逻辑。
奖励没拿到。。。。毛用都没有
广告加载完之后,右上角有一个X按钮,会显示出来。未加载完的时候隐藏。所以就在Activity中找找有没有相关的close或者onClick关键字
jeb中直接找到RewardVideoAdActivity, 确实搜索到了close与onclick关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | / / com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity private void k() { int v0 = com.miui.zeus.mimo.sdk.utils.a.c(this.i.Y()); if (v0 = = 0 ) { v0 = l.a( "mimo_reward_view_end_page_landscape" ); } this.h = LayoutInflater. from (((Context)this)).inflate(v0, this.g, true); String v0_1 = this.i.M(); String v1 = this.i.L(); Bitmap v0_2 = BitmapFactory.decodeFile(v0_1, this.o); Bitmap v1_1 = BitmapFactory.decodeFile(v1, this.o); this.h.findViewById(l.c( "mimo_reward_flv_video" )).setImageBitmap(v0_2); this.h.findViewById(l.c( "mimo_reward_icon" )).setImageBitmap(v1_1); this.h.findViewById(l.c( "mimo_reward_title" )).setText(this.i.h()); this.h.findViewById(l.c( "mimo_reward_summary" )).setText(this.i.g()); this.h.findViewById(l.c( "mimo_reward_dsp" )).setText(this.i.i()); View v0_3 = this.h.findViewById(l.c( "mimo_reward_jump_btn" )); ((TextView)v0_3).setText(this.i.O()); b v1_2 = new b(); this.y = v1_2; ((com.miui.zeus.mimo.sdk.anim.a)v1_2).b(v0_3).a( 1200 ).a( - 1 ).b( 1 ).a(new AccelerateDecelerateInterpolator()).c(); this.h.findViewById(l.c( "mimo_reward_close_img" )).setOnClickListener(((View$OnClickListener)this)); this.g.setOnClickListener(((View$OnClickListener)this)); } |
void k()这个函数中使用了LayoutInflater,“mimo_reward_close_img”, setOnClickListener(this)。太明显了,毫无压力。
直接搜关键字setVisibility,该函数会将一个view显示出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | / / com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity private void p() { int v0; if (this.i.c()) { this.n(); if (!TextUtils.isEmpty(this.i.u())) { v0 = 1 ; } else { goto label_11; } } else { label_11: v0 = 0 ; } if (v0 ! = 0 ) { this.m(); } else { this.f.setVisibility( 8 ); this.k.setVisibility( 8 ); this.g.setVisibility( 0 ); b v0_1 = this.y; if (v0_1 ! = null) { ((com.miui.zeus.mimo.sdk.anim.a)v0_1).c(); } ViewFlipper v0_2 = this.u; if (v0_2 ! = null) { v0_2.stopFlipping(); } com.miui.zeus.mimo.sdk.utils.analytics.b.a(this.i.m(), this.i, "END_PAGE_VIEW" , "load_success" , 0 , ""); } } |
搜到了,就在RewardVideoAdActivity代码中,代码还是那么的明显,毫无压力。
this.g.setVisibility(0),这里的0是一个flag, 就是将view显示出来。

源头是通过onCompletion调过来的, com.miui.zeus.mimo.sdk.video.TextureVideoView$3实现了MediaPlayer.OnCompletionListener接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class com.miui.zeus.mimo.sdk.video.TextureVideoView$ 3 implements MediaPlayer$OnCompletionListener { public com.miui.zeus.mimo.sdk.video.TextureVideoView$ 3 (TextureVideoView arg1) { this.a = arg1; super (); } public void onCompletion(MediaPlayer arg2) { TextureVideoView.c(this.a, 5 ); TextureVideoView.d(this.a, 5 ); if (TextureVideoView.e(this.a) ! = null) { TextureVideoView.e(this.a).hide(); } if (TextureVideoView.h(this.a) ! = null) { TextureVideoView.h(this.a).onCompletion(TextureVideoView.d(this.a)); } } } |
原来广告是通过MediaPlayer播放的,然后播放是否完成 是 通过MediaPlayer的回掉接口掉过来的。
首先要拿到MediaPlayer实例
手有点疼了。。。。
主要是通过jeb,查看交叉引用关系
MediaPlayer是TextureVideoView的成员
1 2 3 4 5 6 | / / com / miui / zeus / mimo / sdk / video / TextureVideoView public Map m; public int n; public int o; public Surface p; public MediaPlayer q; |
TextureVideoView是VideoAdView的成员
1 2 3 4 5 6 | / / com.miui.zeus.mimo.sdk.video.VideoAdView public TextureVideoView a; public ImageView b; public FrameLayout c; public boolean d; public static final String e = "VideoAdView" ; |
VideoAdView是RewardVideoActivity的成员
1 2 3 4 5 6 7 | public class RewardVideoAdActivity extends Activity implements View$OnClickListener, a { public static final String a = "key_baseadinfo" ; public static final String b = "RewardVideoAdActivity" ; public static final String c = "key_exposure" ; public static final long d = 60000 ; public EventRecordFrameLayout e; public VideoAdView f; |
结论: 可以通过拿到RewardVideoActivity实例,然后反射一步步拿到MediaPlayer实例
如何拿到RewardVideoActivity实例
这可能是个老生常谈的问题。方式可能有好几种吧, 下面是我能想到的
1: ActivityThread中保存了当前进程的activity列表
1 | final ArrayMap mActivities = new ArrayMap<>(); |
2.拦截ActivityThread.H,这玩意是一个Handler,第一时间通过system_server将activity的生命周期,回掉过来。
3.通过frida或者其他hook工具,在activity->onCreate或者onResume这里下手
4.通过注入Instrumentation的方式,管控activity生命周期,可以拦截到onCreate以及onResume等
我使用了最后一种方案,用java写了点代码,最后编译为smali文件后,通过静态代码注入重打包了apk。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static void initInstrumentation() { final String CLASS_ACTIVITYTHREAD = "android.app.ActivityThread" ; RefUtils.MethodRef< Object > currentActivityThreadRef = new RefUtils.MethodRef< Object >(CLASS_ACTIVITYTHREAD, true, "currentActivityThread" , new Class[ 0 ]); Object activityThread = currentActivityThreadRef.invoke(null, new Object [ 0 ]); RefUtils.FieldRef instrumentationFieldRef = new RefUtils.FieldRef(CLASS_ACTIVITYTHREAD, false, "mInstrumentation" ); Instrumentation appInstru = instrumentationFieldRef.get(activityThread); HackerInstrumentation.INSTANCE().setOriginInstru(appInstru); instrumentationFieldRef. set (activityThread, HackerInstrumentation.INSTANCE()); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public class HackerInstrumentation extends Instrumentation { private Instrumentation mOrigin = null; public static HackerInstrumentation INSTANCE() { return DefaultHackerInstru.get(); } private HackerInstrumentation(){} private static class DefaultHackerInstru { private static HackerInstrumentation sInstancce = new HackerInstrumentation(); static HackerInstrumentation get() { return sInstancce; } } public void setOriginInstru(Instrumentation instru) { mOrigin = instru; } @Override public void callActivityOnCreate(Activity activity, Bundle icicle) { mOrigin.callActivityOnCreate(activity, icicle); TalkingTomHacker.afterCreateActivity(activity); } @RequiresApi (api = Build.VERSION_CODES.LOLLIPOP) @Override public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) { mOrigin.callActivityOnCreate(activity, icicle, persistentState); TalkingTomHacker.afterCreateActivity(activity); } @Override public void callActivityOnDestroy(Activity activity) { mOrigin.callActivityOnDestroy(activity); TalkingTomHacker.afterCreateActivity(activity); } @Override public void callActivityOnResume(Activity activity) { mOrigin.callActivityOnResume(activity); TalkingTomHacker.afterResumeActivity(activity); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | public static void afterCreateActivity(Activity activity) { Log.d(TAG, "afterCreateActivity " + activity); try { disableRewardActivityMediaPlayer(activity); } catch (Exception e) { e.printStackTrace(); } } private static void disableRewardActivityMediaPlayer(Activity activity) throws Exception { final String REWARD_ACTIVITY_NAME = "com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity" ; if (!TextUtils.equals(activity.getLocalClassName(), REWARD_ACTIVITY_NAME)) { return ; } final String VIDEO_AD_VIEW_FIELD_TYPE = "com.miui.zeus.mimo.sdk.video.VideoAdView" ; Field tmpRef = null; Field[] fields = activity.getClass().getDeclaredFields(); for (Field field : fields) { if (TextUtils.equals(field.getType().getName(), VIDEO_AD_VIEW_FIELD_TYPE)) { tmpRef = field; break ; } } if (tmpRef = = null) { return ; } tmpRef.setAccessible(true); Object videoAdView = tmpRef.get(activity); if (videoAdView = = null) { Log.e(TAG, "videoAdView is null" ); return ; } final String TEXTURE_VIDEO_VIEW_TYPE = "com.miui.zeus.mimo.sdk.video.TextureVideoView" ; tmpRef = null; fields = videoAdView.getClass().getDeclaredFields(); for (Field field : fields) { if (TextUtils.equals(field.getType().getName(), TEXTURE_VIDEO_VIEW_TYPE)) { tmpRef = field; break ; } } if (tmpRef = = null) { return ; } tmpRef.setAccessible(true); TextureView textureVideoView = (TextureView) tmpRef.get(videoAdView); if (textureVideoView = = null) { Log.e(TAG, "textureVideoView null" ); return ; } final String MEDIA_PLAYER_TYPE = "android.media.MediaPlayer" ; tmpRef = null; fields = textureVideoView.getClass().getDeclaredFields(); for (Field field : fields) { if (TextUtils.equals(field.getType().getName(), MEDIA_PLAYER_TYPE)) { tmpRef = field; break ; } } if (tmpRef = = null) { return ; } TextureView.SurfaceTextureListener originListener = textureVideoView.getSurfaceTextureListener(); if (originListener = = null) { Log.e(TAG, "origin surface texure Listener null" ); return ; } setOldTextureListener(originListener); textureVideoView.setSurfaceTextureListener(sSurfaceTextureListener); final Field mediaPlayerRef = tmpRef; ... ... } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | HandlerUtils.post(new Runnable() { @Override public void run() { sModifyMediaPlayerCondition.block(); sModifyMediaPlayerCondition.close(); mediaPlayerRef.setAccessible(true); MediaPlayer player = null; try { player = (MediaPlayer) mediaPlayerRef.get(textureVideoView); } catch (IllegalAccessException e) { e.printStackTrace(); } if (player = = null) { Log.e(TAG, "MediaPlayer is null" ); return ; } player.pause(); try { / / 模拟MediaPlayer播放完成 pretendCompleteMediaPlayer(player); } catch (Exception e) { e.printStackTrace(); } player.release(); } }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | private static void disableRewardActivityResume(Activity activity) { final String REWARD_ACTIVITY_NAME = "com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivity" ; if (!TextUtils.equals(activity.getLocalClassName(), REWARD_ACTIVITY_NAME)) { return ; } synchronized (TalkingTomHacker. class ) { if (activity.isDestroyed() || activity.isFinishing()) { return ; } pretendClickCloseActivity(activity, "mimo_reward_close_img" ); } } private static void pretendClickCloseActivity(Activity activity, String resStr) { if (activity instanceof View.OnClickListener) { int resId = activity.getApplicationContext().getResources().getIdentifier(resStr, "id" , activity.getPackageName()); if (resId < = 0 ) { Log.e(TAG, "pretendClickCloseActivity failed for res " + resStr + " not found" ); return ; } View closeLikelyView = new View(activity.getApplicationContext()); closeLikelyView.setId(resId); ((View.OnClickListener) activity).onClick(closeLikelyView); Log.d(TAG, "pretendClickCloseActivity success" ); } else { Log.e(TAG, "pretendClickCloseActivity failed for not OnClickListener instance" ); } } |