问题描述
Vicuna项目使用google play无法搜索到Tango应用(该应用在墨西哥非常受欢迎),导致无法下载。通过其他方式下载APK安装后该应用可正常使用。根据客户需求,我们需要Vicuna项目能从google play上搜索到并下载。
[编辑] 分析过程
对比TRIANGLE能够在google play上搜索到Tango应用并成功下载使用,而Vicuna不能搜索到的表现,怀疑google play本身在应用搜索上有一定的过滤机制,通过查看google官方文档,得到google play有如下一套应用过滤机制:
google play会分析应用的AndroidManifest.xml的信息得到其所需的软硬件要求与当前访问google play的手机系统的软硬件进行对比,通过对比结果返回是否在搜索时显示该应用。
OK,我们现在来大胆猜测下Google play应用完成过滤动作的大致实现步骤(纯属个人无证据的逻辑性猜测):
1.Google play服务器会在应用提交时分析应用AndroidManifest.xml的如下信息,并保存在服务器存储上。
a.<use-sdk>标签
该标签表示应用使用的SDK,如果当前手机提供的SDK未达到此版本,应用搜索不会显示出来,这个很好理解。
b.<use-feature>标签
该标签表示应用会使用到的硬件特性和软件特性。如果手机未达到这些特性要求,应用也不会显示出来。标签内有一个属性“android:required”,如果此标签为false,意为此硬件特性非必须。具体有哪些特性请参考google官方文档 http://developer.android.com/guide/topics/manifest/uses-feature-element.html
c.<use-permission>标签
该标签标示应用会用的权限,但在Android中该标签实际隐含有需支持一些硬件特性,如android:permission:CAMERA就隐含标示需要有android.hardware.camera and android.hardware.camera.autofocus两个硬件feature。其它内容也请查看google官方文档:http://developer.android.com/guide/topics/manifest/uses-feature-element.html
但是服务器上存储的极可能不是permisson,而是经过分析得到的需要包含的硬件特性属性,使用appt工具可以查看没有混淆的应用所需要的 feature,以Tango为例,输入aapt dump badging XXX.APK可以获取当前应用的packageName, versionCode, ApplicationLabel, LauncherActivity, permission, use-feature, locate, screen-densities, native-code等详细信息,截图如下:
如上,我们可以清楚的看到该应用需要的硬件特性:
android.hardware.microphone
android.hardware.wifi
android.hardware.touchscreen
注意以上appt列出的硬件特性不是AndroidManifest.xml里面的<use-feature>标签,而是经过分析后的最终需要的硬软件特性(去除use-feature里android:required属性为false的项目和通过permission隐性得到的必须支持的特性)。
2.在用户启动google play应用时,google play会通过开放API获取如下一些手机软硬件信息: a.手机的SDK版本信息,通过android.os.Build.VERSION.SDK获取; b.获取手机支持的软硬件特性,通过PackageManager.getSystemAvailableFeatures()函数获取;获取到这些信息后,Google play会暂时将此存储于内存中。之后用户为了寻觅应用,开始输入关键字进行搜索了,google play本地此时会将从手机中获取的信息传送到google服务器,服务器会进行如下操作:
3.先匹配出该关键字的应用list,然后将搜索出的应用需求的软硬件特性和sdk版本号与上传上来的手机信息进行匹配,一旦有一项不符,就将此应用从list中去掉,最后返回限制了的list给google play,goolge play负责显示出来。
[编辑] Permission从哪里来?feature从哪里来?
作为开发人员,只知道调用API是件很掉价的事情,所以让我们来看看feature是在哪里读进去的。
从我们调用的PackageManager.getSystemAvailableFeatures()可得知其主要从 mAvailableFeatures变量中获取feature信息,mAvailableFeatures是在 readPermissionsFromXml处放入数据的:
...... } else if ("feature".equals(name)) { String fname = parser.getAttributeValue(null, "name"); if (fname == null) { Slog.w(TAG, "<feature> without name at " + parser.getPositionDescription()); } else { //Log.i(TAG, "Got feature " + fname); FeatureInfo fi = new FeatureInfo(); fi.name = fname; mAvailableFeatures.put(fname, fi); } XmlUtils.skipCurrentTag(parser); continue; } ......
调用readPermissionsFromXml函数的是readPermissions,此函数主要就是遍历system/etc /permissions/目录,获取所有的权限文件XXX.xml然后调用readPermissionsFromXml将他们分类存储起来:
void readPermissions() { // Read permissions from .../etc/permission directory. File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions"); if (!libraryDir.exists() || !libraryDir.isDirectory()) { Slog.w(TAG, "No directory " + libraryDir + ", skipping"); return; } if (!libraryDir.canRead()) { Slog.w(TAG, "Directory " + libraryDir + " cannot be read"); return; } // Iterate over the files in the directory and scan .xml files for (File f : libraryDir.listFiles()) { // We'll read platform.xml last if (f.getPath().endsWith("etc/permissions/platform.xml")) { continue; } if (!f.getPath().endsWith(".xml")) { Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring"); continue; } if (!f.canRead()) { Slog.w(TAG, "Permissions library file " + f + " cannot be read"); continue; } readPermissionsFromXml(f); } // Read permissions from .../etc/permissions/platform.xml last so it will take precedence final File permFile = new File(Environment.getRootDirectory(), "etc/permissions/platform.xml"); readPermissionsFromXml(permFile);
调用readPermissions函数的地方在PackagemanagerService初始化的地方。这就是Permission和feature的来源。
[编辑] Vicuna的确认和结果
通过查看system/etc/permissions/目录下的所有权限文件XXXX.xml发现都没有定义 android.hardware.microphone特性,所以在framework/base/data/etc/添加如下一支内容的文件 android.hardware.microphone.xml即可:
<?xml version="1.0" encoding="utf-8"?> <!-- This is the standard feature indicating that the device includes microphone. --> <permissions> <feature name="android.hardware.microphone" /> </permissions>
这样当手机SDK版本号和硬软件特性均满足当前应用,我们就可以从google play上搜索到并下载。