★ 起因
『网络链接较慢,建议检查网络设置或稍后重试。』这个字符串可能很多人都不陌生。我的手机里在一段时间内一直在弹这个提示,而且是在任意界面中都会提示,样子是这样的:
(为了显示完整的字符串,我截了个横屏的图)
我选择在WPS office中截图,但这个提示显然不是WPS office提示的(因为在WPS office完全不运行时也会提示这个界面)。
总是提示这个界面,确实挺烦人的。搜了一下必应,遇到此问题的人很多,各种手机型号都有,所以初步排除了是某个手机厂商开发的应用导致的,必应搜索结果如下(部分结果):
心急的,可以直接点此看结果
★ 思路
对于我的user版的手机来说,我自己的小应用是第三方应用,我的手机没有root权限。
所以,能够利用的接口必须是第三方应用可以使用的。
注:本文中『接口』是指双方约定好的可以调用的函数或方法,是宽泛的概念。如果提到『Java Interface接口』会以『Java接口』来指代。
♦ createPackageContext()
此接口可以根据package name 获取其他应用的代码和资源。
♦ getInstalledPackages()
此接口获取手机中所有安装的应用的信息。
♦ 反射的方法 获取字符串的ID
一个应用中的字符串资源会编译到R.java中,所以我的目标是如何获取R.java中的string内部类的信息。
通常,字符串资源会在 pkgName + ".R$string"
类中。
还有一些应用是加固过的,所以不遵守这个规则,例如某app的com.cmbchina.ccd.pluto.cmbActivity.R$string
这个类是没有的,因为它加固了,取而代之的是com.secneo.apkwrapper.R$string
和com.tencent.txproxy.R$string
。但是这两个类中已经没有什么字符串了,应用原始的字符串已经被保护起来了。
能看到的字符串,如下:
R class name: com.secneo.apkwrapper.R$string
id (hex): 7f050001, id: 2131034113
str: res/anim/abc_fade_out.xml
id (hex): 7f050000, id: 2131034112
str: res/anim/abc_fade_in.xml
id (hex): 7f050002, id: 2131034114
str: res/anim/abc_grow_fade_in_from_bottom.xml
R class name: com.tencent.txproxy.R$string
id (hex): 7f030000, id: 2130903040
str: res/mipmap-xxhdpi-v4/ic_launcher.png
为了获取 com.secneo.apkwrapper.R$string
和com.tencent.txproxy.R$string
这类的与包名不一致的R$string
类,我选择了通过反射的方法。
废话少说,上代码。
★ 代码
这里只贴出关键代码:
void checkStringsAsync() {
PackageManager pm = getPackageManager();
ArrayList<PackageInfo> thirdPartyAppList = new ArrayList<>();
List<PackageInfo> pkgList = pm.getInstalledPackages(0);
if (pkgList != null && pkgList.size() > 0) {
for (PackageInfo pi : pkgList) {
Log.e(TAG, "pkg name: " + pi.packageName);
Log.e(TAG, "dir: " + pi.applicationInfo.publicSourceDir);
// 我使用的是坚果Pro,所以把系统app都排除掉了。
if (pi.applicationInfo.publicSourceDir.startsWith("/data/app/")
&& !pi.packageName.startsWith("com.smartisanos.")) {
thirdPartyAppList.add(pi);
}
}
}
ArrayList<String> RClassNameList = new ArrayList<>();
if (thirdPartyAppList.size() > 0) {
for (PackageInfo pi : thirdPartyAppList) {
String pkgName = pi.packageName;
try {
// 获取其他apk的代码和资源
Context context = createPackageContext(pkgName, CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY);
ClassLoader pathClassLoader = context.getClassLoader();
Class baseDexClassLoaderCls = Class.forName("dalvik.system.BaseDexClassLoader");
Field pathListField = baseDexClassLoaderCls.getDeclaredField("pathList");
pathListField.setAccessible(true);
Object dexPathListObj = pathListField.get(pathClassLoader);
Class dexPahtListCls = dexPathListObj.getClass();
Field dexElementsField = dexPahtListCls.getDeclaredField("dexElements");
dexElementsField.setAccessible(true);
Object elementArrayObj = dexElementsField.get(dexPathListObj);
int len = Array.getLength(elementArrayObj);
RClassNameList.clear();
for (int i = 0; i < len; i++) {
Object elementObj = Array.get(elementArrayObj, i);
Class ElementCls = elementObj.getClass();
Field dexFileField = ElementCls.getDeclaredField("dexFile");
dexFileField.setAccessible(true);
Object dexFileObj = dexFileField.get(elementObj);
if (dexFileObj != null) {
Class dexFileCls = dexFileObj.getClass();
Method DexFile_entries = dexFileCls.getMethod("entries");
Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFileObj);
while (e.hasMoreElements()) {
String className = e.nextElement();
// 通过反射的方式,获取所有R$string类
if (className.contains(".R$string")) {
Log.e(TAG, className);
RClassNameList.add(className);
}
}
}
}
Resources resources = context.getResources();
if (RClassNameList.size() > 0) {
for (String RClassName : RClassNameList) {
String Rclass = RClassName; //pkgName + ".R$string";
if (Rclass.equals("android.support.v7.appcompat.R$string")) {// 可以排除一些系统的类
continue;
}
Log.e(TAG, "R class name: " + Rclass);
Class RClassObj = context.getClassLoader().loadClass(Rclass);
ReflectUtils reflectUtils = new ReflectUtils(RClassObj);
List<Integer> ids = reflectUtils.getAllIntValues();
if (ids != null && ids.size() > 0) {
for (Integer id : ids) {
Log.e(TAG, "id (hex): " + Integer.toHexString(id) + ", id: " + id);
try {
String str = resources.getString(id);
Log.e(TAG, "str: " + str);
if (str.contains("网络链接较慢,建议检查网络设置或稍后重试。")) {
Log.e(TAG, pi.toString());
Log.e(TAG, "+++++ package: " + pi.packageName);
}
} catch ...
}
}
}
}
} catch ...
}
}
}
public List<Integer> getAllIntValues() {
List<Integer> ids = new ArrayList<Integer>();
Field[] fields = mClassObj.getDeclaredFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
String name = field.getName();
try {
int val = field.getInt(null);
ids.add(val);
} catch ...
}
return ids;
} else {
Log.d(LOG_TAG, "No fields.");
return ids;
}
}
★ 结果
id (hex): 7f0b23dd, id: 2131436509
str: 网络链接较慢,建议检查网络设置或稍后重试。
PackageInfo{23e574e com.tencent.mobileqq}
+++++ package: com.tencent.mobileqq
是 QQ 弹的提示界面。
我用的QQ一直没有更新,是7.0.0.676版本。
versionCode: '676'
versionName: 7.0.0
更新QQ后,到目前为止,不再弹出那个烦人的提示界面了。
执行上面的代码,也没有匹配到任何应用含有『网络链接较慢,建议检查网络设置或稍后重试。』这个字符串。
新版QQ 7.7.0.884 :
versionCode: '884'
versionName: 7.7.0