1. 需求
获取框中的手机名称,注意这里与其他文章不一样的地方,这个手机名称可以修改,并且会作用于蓝牙以及热点等功能。
2. 问题分析
首先查阅了网上关于获取手机名称或者设备名称的解决方案,一般包括两种:
2.1 Build.MODEL
显然这个名称与目标完全不符,自行尝试
2.2 BluetoothAdapter.getName()
这个是一个解决方法,但是当关闭蓝牙后再修改手机名称的话,通过这个方式获取的是旧名称,修改后的名称获取不到
也就是说,网上给出的解决方法已经到了尽头,需要自己找个解决方案,那么我的分析思路:
- 先查看Build.MODEL源码,发现最后是通过SystemProperties.get(property, UNKNOWN)得到属性,传入的值是属性对应的键,比如MODEL对应ro.product.model,那么手机名称是否也是类似的键值属性呢;
- 搜索ro.product.model和SystemProperties相关的内容,我了解了它确实是可以获取系统属性的,我感觉手机名称也属于系统属性,因此继续搜索下去;
- 后来查到了通过命令
adb shell getprop > prop.txt
可以导出系统属性,那么我来看看结果是什么,果然,这个文件中包括了“小米手机”这个名称,然后修改名称再看一次,发现确实是对应的,那么我们就获得了key:persist.sys.device_name
,同时找到ro.product.model
对应的值,这样就确定可以通过SystemProperties得到结果; - 又因为SystemProperties不能直接使用,那么寻找其他办法,显然可以通过反射使用get方法,那么就尝试一下,当然还有另一个方式就是通过代码执行cmd
getprop
,这里就仅用反射吧。
[persist.sys.dalvik.vm.lib.2]: [libart.so]
[persist.sys.datastall.detect]: [0]
[persist.sys.device_name]: [小米手机]
[persist.sys.enable_inputopts]: [true]
[persist.sys.enable_ioprefetch]: [false]
3. 代码
通过反射将key传入,得到deviceName,最终任务完成,搞定
String deviceName = "";
try{Class<?> cls = Class.forName("android.os.SystemProperties");Object object = (Object) cls.newInstance();Method getName = cls.getDeclaredMethod("get", String.class);deviceName = (String) getName.invoke(object, "persist.sys.device_name");
} catch (Exception e){e.printStackTrace();
}
4. 测试
MIUI可以,其他系统GG,其他系统类似华为、三星都失败了,从prop.txt文件来看,并没有将谁被名称存在系统配置文件中。那么问题来了,这个属性去哪了?
5. 再分析
如果从目标往前走已经是死路了,那么不妨从前往后走,我们从设置手机名称这个地方寻找它调用了什么方法,说不定就能发现一些东西。
显然,Android开源的优势体现出来,在查询了Google关于Android的源码后我发现,在关于手机这个界面,保存和获取手机名称都是在一个类叫做DeviceManager中实现的,具体相关的两部分代码为
package com.android.tv.settings.name;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.provider.Settings;
public class DeviceManager {/*** Retrieves the name from Settings.Global.DEVICE_NAME** @param context A context that can access Settings.Global* @return The device name.*/public static String getDeviceName(Context context) {return Settings.Global.getString(context.getContentResolver(), Settings.Global.DEVICE_NAME);}/*** Sets the system device name.** For now it will explicitly call the different discoverable services that haven't been ported* to use the Settings.Global.DEVICE_NAME entry.** @param context A context that can access Settings.Global* @param name The new device name.*/public static void setDeviceName(Context context, String name) {Settings.Global.putString(context.getContentResolver(), Settings.Global.DEVICE_NAME, name);BluetoothAdapter.getDefaultAdapter().setName(name);}
}
由上面的代码可知,原生Android在保存设备名称时会同时保存蓝牙的名称,而获取名称时是从Settings.Global中获取的,因此如果我们要在代码里得到设备名称,理论上应该也可以通过Settings.Global得到。
但是,根据我的测试,小米/三星/华为这三家都将Settings.Global设置为一个定值,也就是说,他们修改了这里的存储逻辑,在不知道系统源码的情况下我们应该是拿不到设别名称,除非通过蓝牙得到(但是蓝牙获取有Bug),而小米是个例外,他存储设备名称的位置被我碰巧找到了,所以这个需求还是没办法完美的实现,主要是各个Android厂商背锅。