记录一次失败需求的经历, hack 终究是不归路。

168 天前
 jeesk

首先我们有个需求, 就是能够在 WebView 中适配控制 media 的播放控制, 但是查看 api 发现,webview 根本没有暴露 mediasssion 这方面的 api. 怎么办? 只能去看看源码。

通过搜索, 发现 chromium 项目中有 MediaSession 这个接口,然后找到了这个类的实现 org.chromium.content.browser.MediaSessionImpl

,发现这个类有一个静态方法。

@CalledByNative private static MediaSessionImpl create(long nativeMediaSession),  这个时候肯定是 native 调用了 java 的代码。

那么继续搜索 MediaSessionImpl_create 在 chromium 源码中, 继续搜索

➜  src git:(123.0.6312.121) ✗ grep -rn "MediaSessionImpl" --include="*.cc"  |grep create
content/browser/media/session/media_session_android.cc:38:      Java_MediaSessionImpl_create(env, reinterpret_cast<intptr_t>(this));

继续往下面看

MediaSessionAndroid::MediaSessionAndroid(MediaSessionImpl* session)
    : media_session_(session) {
  DCHECK(media_session_);

  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jobject> j_media_session =
      Java_MediaSessionImpl_create(env, reinterpret_cast<intptr_t>(this));
  j_media_session_ = JavaObjectWeakGlobalRef(env, j_media_session);

  WebContentsImpl* contents =
      static_cast<WebContentsImpl*>(media_session_->web_contents());
  if (contents) {
    web_contents_android_ = contents->GetWebContentsAndroid();
    DCHECK(web_contents_android_);
    web_contents_android_->SetMediaSession(j_media_session);
  }

  session->AddObserver(observer_receiver_.BindNewPipeAndPassRemote());
}

发现了 mediaSession 被设置到 WebContents, 通过搜索发现 WebContents 是一个接口, 继续找到该类的子类 WebContentsImpl.java , 发现里面有一行代码

    @CalledByNative
    private final void setMediaSession(MediaSessionImpl mediaSession) {
        mMediaSession = mediaSession;
    }

, 哈哈惊喜过度, 对象 mediaSession 竟然还在。 那还不简单, 直接通过反射去拿这个对象?

这里直接反射的过程简单说一下, 首先我们通过 WebView 的 classloader 拿到 WebView 的 Class 对象 ,再反射拿到 WebView 类里面的 mProvider 对象, 这个 mProdiver 其实就是 WebViewChromium.java, 在通过 WebViewChromium.java 对象里面的 AwContents 拿到对象 WebContens ,找到 WebContens 对象后, 就能反射拿到 mediaSession 了, 这个里面有很多坑,反射通过字段很多拿不到,名字和类名被混淆了,所以需要写很多 hack 代码。

这个时候直接反射出了 mMediaSession , 但是发现这个字段怎么都不存在, 惊呆了我的下巴, 继续测试,发现 setMediaSession 这个方法存在的。 这是怎么回事儿了? 二进制和源代码不一致? 这个时候不得不使用一些反编译工具了, 反编译出 MediaSessionImpl.java 文件,发现 mMediaSession 字段不存在, 并且 mMediaSession = mediaSession; 代码也消失了。

这这这这这? 心里一万个草泥马。 经过多次测试,发现这段代码被编译器忽略掉了。

认真分析 media_session_android.cc , 发现 setMediaSession 这个方法肯定是调用了的。 这个时候又去查询了一些资料,发现 native 方法的调用的原理是反射, 这个时候就只能去修改 classloader 里面的 class 文件内容了, 奇奇怪怪的折腾。

未完待续, 后面会继续分析。

4996 次点击
所在节点    Android
9 条回复
anbus
168 天前
这么抽象的吗
z836454898
168 天前
hack chromium 内核这是在给后人留坑啊,后人一更新 app 里的 chromium 程序就崩了,除非你的 app 像小程序一样有深度定制内核需求
jeesk
168 天前
@z836454898 自己处理好兼容性就行了.
jeesk
168 天前
@anbus 没办法, 坑太多了. 只有像苹果这种闭源, hack 的可能性就很小
jeesk
168 天前
@z836454898 我们自己定制了内核. 不过有些场景不需要定制内核 , 主要是为了减少体积.
HtPM
168 天前
“这个时候直接反射出了 mMediaSession , 但是发现这个字段怎么都不存在, 惊呆了我的下巴, 继续测试,发现 setMediaSession 这个方法存在的”
这句话什么意思?你是说反射出了 MediaSession 的这个对象,但是字段 mMediaSession 不存在?这正常啊,如果做了代码混淆,mMediaSession 不就不存在了?变成其它名称了
HtPM
168 天前
“认真分析 media_session_android.cc , 发现 setMediaSession 这个方法肯定是调用了的。 这个时候又去查询了一些资料,发现 native 方法的调用的原理是反射”
还有这句话是什么意思?你是说 setMediaSession 这个 Java 函数是被 native 方法反射调用的?
jeesk
168 天前
@HtPM setMediaSession 就是被 native 代码调用了, native 如何知道调用哪个方法? 不就是使用反射吗?
jeesk
168 天前
@HtPM setMediaSession() 这个方法存在, 但是 里面的 this.mMediaSession = mediaSession; 和 private mMediaSession 代码被编译丢弃了.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1047502

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX