魔改Sony Headphone Connect强制升级国行WH-1000XM2
前一阵子京东入了个大法的WH-1000XM2,感觉程序有一些小毛病,而且那中文的语音实在是…作为一个不折腾会死星人,拿到手之后不久就研究能否刷机更新固件。上网谷歌了一圈,貌似已经发布了2.0.1的固件,但配套程序并没有更新提示,于是就怀疑是国行的问题。上网再查了一圈貌似并没有人折腾这玩意的固件,也没有人搞强制刷机,那只能自己动手丰衣足食了。
最终更新/FINAL UPDATE
这篇文章是1年半之前写的,Sony Headphone Connect已经更新迭代了数个版本,XM2/XM3等机子的更新也迭代了数个版本。这个文章的内容已经 不再适用 。鉴于我现在已经没有使用索尼的耳机(去年因为一些质量问题退回给京东了),我也没有继续研究下去的想法。Reddit上有一些新版本的教程,请善用Reddit的站内搜索和Google。
In English: This article was drafted back in 2018 and since then, both Sony headphones’ firmware and the Sony Headphone Connect app has been updated for times. Any information in this article is outdated, and since I have returned my WH-1000XM2 back to JD (a Chinese shopping platform where I bought the headphone), I have no thoughts to continue my research and update this article. I observed that there are some other modified versions of this tutorial on Reddit. Please make use of Reddit and Google for the latest way to change the region of your headphones’ firmware.
更新:WH-1000XM3(但不是XM2)的国行4.1.3版本带有语音语言切换功能。如果您WH-1000XM3的使用者并且仅对本功能有需求,我建议您不要看本教程,通过正规渠道升级到4.1.3即可。
另一条更新:这种修改逻辑不适用于2.0.1(国际版)更新到4.1.3(国内版),根据贴吧大佬的分析,原因在此。如果你按照本贴教程升级到了2.0.1但后悔了,想回滚到国内版本的4.1.3,或许你可以尝试这大佬的解决方案(我本人对刷回国行没有需求所以没有试过,请尝试的人仔细阅读他所提及的注意事项)。
提醒及警告
这篇博文讲的任何内容并不受Sony官方支持,本人也并不对此造成的保修作废、耳机变砖、耳机电池爆炸等等等等的任何问题负责。尝试本博文的方法带来的任何后果自负。本文所提供的内容仅适用于WH-1000XM2,其余型号请自行研究。本文所提供的内容仅针对Sony Headphone Connect 3.2.0版本。
根据LvWind的博客的文章,Headphone Connect 4.0+版本已经改掉了这些检测逻辑,需要修改的位置可能跟本教程不同。如果你需要WI-1000X或者WH-1000XM3的各版本MDRID,你也可以参考LvWind的文章。
准备工具
- Apktool (旧版的可能有问题,建议最新的2.3.3)
- 一个靠谱的文本编辑器
我的研究过程
提醒:你用别的方法反编译后类、方法、命名空间等的命名不一定与我的相同,建议善用文本搜索
TL,DR:如果你不想看我的研究过程只想看教程或者下载我打包的文件,请跳至页尾
首先上网找了个在线反编译的网站反编译了一遍,下到本地。用IJ尝试重新编译成APK,发现失败,于是放弃用在线的反编译工具,仅把这反编译的代码作为参考。然后用Apktool拆包了一下,看到一堆smali指令码,也没什么头绪从何入手。
然后我在手机那抓了个包,发现软件启动的时候请求http://info.update.sony.net/HP001/MDRID289202/info/info.xml
,居然返回的是404。不负责任的猜测了一下MDRID289202是机身的内部型号(包括版本号)。全局搜索了一下源码发现info.update.sony.net
在com.sony.songpal.mdr.automagic.AutoMagicManager
里面。
private URL m9721d(String str, String str2) { String str3 = "info.update.sony.net"; if (str == null) { throw new IllegalStateException(String.format(Locale.getDefault(), "%s:code=%d", new Object[]{"AutoMagicManagerErrorDomain", Integer.valueOf(AutoMagicError.NoCategoryID.ordinal())})); } else if (str2 == null) { throw new IllegalStateException(String.format(Locale.getDefault(), "%s:code=%d", new Object[]{"AutoMagicManagerErrorDomain", Integer.valueOf(AutoMagicError.NoServiceID.ordinal())})); } else { try { return new URL(String.format(Locale.getDefault(), "http://%s/%s/%s/info/%s", new Object[]{str3, str, str2, "info.xml"})); } catch (Throwable e) { SpLog.m12039a(f5487a, e); return null; } } }
这玩意用了两个字符串类的参数,然后在URL里面就是HP001和MDRID289202,于是猜测假如我篡改这个参数的获取程序使其获取成国外行货的代码,大概就能强制更新了(前提是硬件完全一样)。顺藤摸瓜检查m9271d这方法的调用,查到了保存这个参数的类com.sony.songpal.mdr.application.p061b.C1702a
。
void m7942a(C2503b c2503b) { if (Command.fromByteCode(c2503b.m11224a()) == Command.UPDT_RET_PARAM) { cm cmVar = (cm) c2503b; if (cmVar.m11579e() != this.f4438n) { SpLog.m12040b(f4425a, this.f4438n + " expected, but received " + cmVar.m11579e()); return; } af f = cmVar.m11580f(); switch (cmVar.m11579e()) { case CATEGORY_ID: this.f4429e = ((ag) f).m11716a(); break; case SERVICE_ID: this.f4430f = ((ag) f).m11716a(); break; case NATION_CODE: this.f4431g = ((ag) f).m11716a(); break; case LANGUAGE: this.f4432h = ((ag) f).m11716a(); break; case SERIAL_NUMBER: this.f4433i = ((ag) f).m11716a(); break; case BATTERY_POWER_THRESHOLD: this.f4434j = ((ae) f).m11711a(); break; default: throw new IllegalStateException("Invalid inquired type " + cmVar.m11579e() + " was expected"); } if (this.f4437m != null) { this.f4437m.countDown(); } } }
SMALI指令码对应如下(com.sony.songpal.mdr.application.b.a
)
.line 156 :pswitch_1 check-cast v0, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag; invoke-virtual {v0}, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;->a()Ljava/lang/String; move-result-object v0 iput-object v0, p0, Lcom/sony/songpal/mdr/application/b/a;->f:Ljava/lang/String; goto :goto_1
把这玩意的SERVICE_ID那改成外国行货的ID就好了。比如
case SERVICE_ID: this.f4430f = "MDRID123456"; break;
但问题来了,不知道外国行货的ID是多少,于是@Lucifer提出了可能ID是连着的,于是我试了试MDRID289200,确认有这个型号。于是修改Apktool解包的SMALI为
.line 156 :pswitch_1 #check-cast v0, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag; #invoke-virtual {v0}, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;->a()Ljava/lang/String; #move-result-object v0 const-string v0, "MDRID289200" iput-object v0, p0, Lcom/sony/songpal/mdr/application/b/a;->f:Ljava/lang/String; goto :goto_1
重新用Apktool打包,然后用自己的证书签名。传到手机那,成功检测到更新。然后发给勇士@Lucifer来试了一下(我有点怂不敢试,毕竟如果变砖了我寄回国内可是相当麻烦)。实测貌似没啥毛病,更新成了国外版本的2.0.1固件并且语音变成了英文。于是自己也试了一下,更新后貌似确实没啥毛病。
装回原版程序后再次用抓包抓了一下,发现请求的service id已经变成了MDRID289200。大概不用担心以后又变回国行固件的问题。
简要教程
- 用Apktool解包:
apktool d a.apk
(我为了方便文件名改成了a.apk),这一步会创建一个名为a的文件夹 - 进入文件夹,全局搜索
invoke-virtual {v0}, Lcom/sony/songpal/tandemfamily/message/mdr/param/ag;->a()Ljava/lang/String;
,打开包含这一行的文件。 - 跳至
.line 156
,把里面的内容修改成我上面所述的内容 - 重新打包:
apktool b a -o b.apk
,这会生成b.apk - 给b.apk签名,这个网上都有教程我就不写了
- 把原本的App卸掉,安装这个APK,然后连接耳机,如果没毛病的话应该会见到底部的更新提示,按部就班更新就行
- 更新完后卸掉这个魔改的App,安装回原版的App,enjoy
我打包的程序
我知道肯定有人会懒得自己动手,于是就把自己打包的给发上来了。警告:仅适用于WH-1000XM2。
最终更新 :这个打包了的apk已经不再适用,请勿尝试。
百度网盘:https://pan.baidu.com/s/1KrbW6yb0fJho4u7VjtGr0A,密码:5qgc。