Android 使得google play store 中不能搜索到Netflix

1.需求

有时候在开发中,为了版权问题,有的应用虽然在应用商城(google play store)中可以搜到,但是因为没有给OEM 方授权,故在OEM的Device 中不能使用(预制以及在商城下载)此应用。例如,吊炸天的Netflix。以前我总是想不通,能上架应用商城的应用,把不得用户多多下载。可是为啥人家Netflix 就这么叼呢。唉,这个不是本文重点。

2. 解决这个问题

要解决这个问题,需要提前了解 android 中的uses-feature 属性。具体可以参考这篇文章—AndroidManifest配置之uses-feature

uses-feature声明

AndroidManifest中的uses-feature配置用来声明一个app在运行时所依赖的外部的硬件或软件特征(feature),uses-feature还提供了一个required属性配置,表示此项依赖的软硬件特征是否是必须的,当它设置为true表示此app运行时必须使用此项特征,如果没有则无法工作,如果它设置为false,表示应用在运行时需要用到这些特征,但如果没有,应用可能会有一部分功能会受到影响,但大部分功能还是可以正常工作。例如一个拍照app,它使用时必须开启设备的摄像头,在没有摄像头的机器上任何功能都无法使用,这就需要通过uses-feature来声明该应用需要摄像头,并将required设置为true。再比如一个支付app,它支持扫码支付的功能,这项功能同样需要开启设备的摄像头,因此需要通过uses-feature声明该应用需要摄像头,但如果一个设备没有摄像头,仅意味着扫码支付的功能无法使用,其他支付方式仍然可以使用,这时就可以设置required属性为false,表明此项feature的需求不是必须的。

uses-feature的作用

uses-feature只是对外提供了这样一组信息,表明它所依赖的软硬件特征,这个信息通常是给应用市场使用的,应用市场会读取app的uses-feature设置,然后只给那些满足这组软硬件特征的设备分发这个app。
Android系统在应用安装时并不会使用这里的信息。例如一个app在uses-feature中声明了需要摄像头,且required为true,那么应用市场在分发app时就不会将该app分发给那些没有摄像头的设备上,一个没有摄像头的设备也不能通过应用市场搜索和下载到该app。但是如果用户通过其他渠道(例如官网)下载该app对应的apk文件到某个设备上,在该设备上安装此apk时,系统并不会检查uses-feature所声明的软硬件特征是否满足,如果该设备没有摄像头,同样可以安装该app。

不仅如此,Android系统在应用运行时也不会使用这里的信息,所以uses-feature声明对应用的运行也不会产生影响(除非应用自己在代码中去判断某项feature是否满足)。这点和uses-permission是不一样的,Android系统会读取应用的uses-permission,并对应用的运行产生影响。同样以摄像头为例,如果应用某项功能需要用到摄像头,无论uses-feature如何声明,都不会影响到摄像头是否能够使用。虽然如果设备没有摄像头这个硬件,这项功能就无法使用,但是这和uses-feature声明无关。但如果没有通过uses-permission声明摄像头的权限,那么即使有摄像头硬件,也是无法使用摄像头的。

这里的因果关系是这样的:由于应用没有这项feature就无法工作,所以需要在AndroidManifest中配置uses-feature,让应用市场在分发app时自动过滤掉那些不支持的设备,不是由于配置了uses-feature,导致应用在没有这项feature的设备上无法工作。

Google Play app过滤机制

当用户打开Google Play浏览或查找应用时,Google Play会对用户可见的app进行过滤,用户在Google Play上只能够看到和下载到那些能够和用户设备相兼容的app。根据uses-feature声明的软硬件特征来过滤app是其中的一种方式。

当开发者上传apk文件到Google Play后,Google Play会读取apk文件中AndroidManifest,根据uses-feature,uses-sdk,uses-permission,uses-library等配置信息来评估此app所需要的features,然后根据这些信息生成一份元数据和该apk版本关联起来,保存到数据库中。

当用户打开设备上的Google Play应用时,Google Play应用会通过getSystemAvailableFeatures()获取到当前设备所支持的软硬件特征,然后将此列表发送给Google Play,之后当用户在这台设备上通过Google Play浏览或查找应用时,Google Play会将每个应用所需要的feature和用户设备支持的feature做比较。只有那些所有需要的feature在当前设备上都支持的app才会显示出来,如果一个app所需要的某个feature不能被当前设备满足,那么在这台设备上就不会显示这个app。

———————————————————————————————————————————
知道了 uses-feature的作用后,我们就可以来过滤Netflix了,可是有一个问题,就是我们该使用那一个uses-feature 来过滤Netflix呢。Netflix 专门有一个特殊的uses-feature。来标识其。
即:nrdp.modelgroup
这里可以看看Netflix 的AndroidManifest.xml 中的配置:

以下为nrdp.modelgroup的解释

The Netflix Ready Device Platform (NRDP) is a software development kit used by internal and external partners to integrate Netflix into CE (consumer electronics) devices. The technologies, features, tools, and documentation in the NRDP are continually improved to enable the best possible Netflix experience while simplifying and accelerating the partner integration process.

其实大致意思就是NRDP是Netflix 提供的一种机制。不用纠结。

好了,知道了以上,那么我们就可以在我们的系统中做一些操作,删除掉支持nrdp.modelgroup 属性use-feature即可。删除后,google play store 就检测到我们的设备不支持nrdp.modelgroup,则也就搜索不到Netflix了。这里提供两种方法:

方法1 在配置文件中直接删除:
device\OEM\device.mk
# Permission files
PRODUCT_COPY_FILES += \$(DEVICE_FOLDER)/configs/tv_features.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/tv_features.xml \$(DEVICE_FOLDER)/configs/libs_permissions.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/libs_permissions.xml

通过device.mk中可以找到tv_features.xml 的定义

   device\OEM\config\tv_features.xml<permissions><!-- These are the hardware components that the television devicessupport in addtion to the basic TV teatures.
-->
<feature name="android.hardware.bluetooth" />
<feature name="android.hardware.bluetooth_le" />
<feature name="android.hardware.ethernet" />
<feature name="android.hardware.hdmi.cec" />
<feature name="android.hardware.camera.any" />
<feature name="android.hardware.camera.front" />
<!--<feature name="android.hardware.microphone" />-->
<feature name="android.hardware.usb.accessory" />
<feature name="android.hardware.usb.host" />
<feature name="android.hardware.wifi" />
<feature name="android.hardware.wifi.direct" />
<!--<feature name="android.hardware.camera" />-->
<feature name="android.software.input_methods" />
<feature name="android.software.webview" />
<feature name="android.software.cant_save_state" />
<feature name="android.software.verified_boot" />
<feature name="nrdp.modelgroup" />    // 删除掉此属性即可
<feature name="android.software.app_widgets" />
<!-- Configure backup services which are supported by system.-->
<backup-transport-whitelisted-service service="android/com.android.internal.backup.LocalTransportService" />
<backup-transport-whitelisted-service service="com.google.android.gms/com.google.android.gms.backup.BackupTransportService" /></permissions>
方法2 在代码中修改

可以在解析tv_feature.xml 的代码中做一些操作。

frameworks / base/core/java/com/android/server/SystemConfig.javaprivate void readPermissionsFromXml(File permFile, int permissionFlag) {FileReader permReader = null;try {permReader = new FileReader(permFile);} catch (FileNotFoundException e) {Slog.w(TAG, "Couldn't find or open permissions file " + permFile);return;}final boolean lowRam = ActivityManager.isLowRamDeviceStatic();try {XmlPullParser parser = Xml.newPullParser();parser.setInput(permReader);int type;while ((type=parser.next()) != parser.START_TAG&& type != parser.END_DOCUMENT) {;}if (type != parser.START_TAG) {throw new XmlPullParserException("No start tag found");}if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {throw new XmlPullParserException("Unexpected start tag in " + permFile+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");}boolean allowAll = permissionFlag == ALLOW_ALL;boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;while (true) {XmlUtils.nextElement(parser);if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {break;}String name = parser.getName();if ("group".equals(name) && allowAll) {String gidStr = parser.getAttributeValue(null, "gid");if (gidStr != null) {int gid = android.os.Process.getGidForName(gidStr);mGlobalGids = appendInt(mGlobalGids, gid);} else {Slog.w(TAG, "<group> without gid in " + permFile + " at "+ parser.getPositionDescription());}XmlUtils.skipCurrentTag(parser);continue;} else if ("permission".equals(name) && allowPermissions) {String perm = parser.getAttributeValue(null, "name");if (perm == null) {Slog.w(TAG, "<permission> without name in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}perm = perm.intern();readPermission(parser, perm);} else if ("assign-permission".equals(name) && allowPermissions) {String perm = parser.getAttributeValue(null, "name");if (perm == null) {Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}String uidStr = parser.getAttributeValue(null, "uid");if (uidStr == null) {Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}int uid = Process.getUidForName(uidStr);if (uid < 0) {Slog.w(TAG, "<assign-permission> with unknown uid \""+ uidStr + "  in " + permFile + " at "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;}perm = perm.intern();ArraySet<String> perms = mSystemPermissions.get(uid);if (perms == null) {perms = new ArraySet<String>();mSystemPermissions.put(uid, perms);}perms.add(perm);XmlUtils.skipCurrentTag(parser);} else if ("library".equals(name) && allowLibs) {String lname = parser.getAttributeValue(null, "name");String lfile = parser.getAttributeValue(null, "file");if (lname == null) {Slog.w(TAG, "<library> without name in " + permFile + " at "+ parser.getPositionDescription());} else if (lfile == null) {Slog.w(TAG, "<library> without file in " + permFile + " at "+ parser.getPositionDescription());} else {//Log.i(TAG, "Got library " + lname + " in " + lfile);mSharedLibraries.put(lname, lfile);}XmlUtils.skipCurrentTag(parser);continue;} else if ("feature".equals(name) && allowFeatures) {String fname = parser.getAttributeValue(null, "name");int fversion = XmlUtils.readIntAttribute(parser, "version", 0);boolean allowed;if (!lowRam) {allowed = true;} else {String notLowRam = parser.getAttributeValue(null, "notLowRam");allowed = !"true".equals(notLowRam);}if (fname == null) {Slog.w(TAG, "<feature> without name in " + permFile + " at "+ parser.getPositionDescription());} else if (allowed) {if("nrdp.modelgroup".equals(fname) )    // 如果xml 中包含此属性,则skip 掉此tag的属性。{Slog.w(TAG, "ConfigData ignore Google play store Netflix.");XmlUtils.skipCurrentTag(parser);continue;}addFeature(fname, fversion);}XmlUtils.skipCurrentTag(parser);continue;

到此在google play store中就搜索不到Netflix应用了。

Android 设备上使得google play store 应用市场中不能搜索到Netflix应用相关推荐

  1. 如何在Android设备上的Google Play商店中清除搜索和应用历史记录

    Your Google Play Store search history includes previously searched for apps, movies, books, music, e ...

  2. [转载] 如何在Android设备之间共享Google Play应用,音乐等

    参考链接: 使用super访问Java祖父母的成员 We recently showed you how to configure your iOS devices for app and media ...

  3. 15款android设备上的代码编辑器,超级方便!

    如果你希望你的Android设备,如智能手机和平板电脑,在任何时间和任何地方都能够编写代码,那么,不妨看看下面我将介绍的15款Android代码编辑器,它们必将成为你的理想工具. 1.Deuter I ...

  4. android初学者_初学者:如何在Android设备上的打开的应用程序之间切换

    android初学者 When you run an app on your Android device, you can minimize it and return to the home sc ...

  5. 如何在Android设备之间共享Google Play应用,音乐等

    We recently showed you how to configure your iOS devices for app and media sharing; more than a few ...

  6. 如何在Android手机上进行Google Map的开发。

    1.题记 提起谷歌Map相信大家都不会陌生,那进入我们今天的话题,如何在Android手机上进行Google Map的开发. 2.Map应用程序的开发 2.1 准备工作 2.1.1 申请Android ...

  7. 如何查看Android设备上的分区信息

    Android设备上,一般都会存在一块eMMC存储芯片来存放系统和用户数据,甚至部分的引导程序. 一般设备出厂时,各个厂商都会将这块存储芯片分成很多的分区,每个分区内存放不同的内容.具体分区的布局每个 ...

  8. (转)在ios android设备上使用 Protobuf (使用dll方式)

    自:http://game.ceeger.com/forum/read.php?tid=13479 如果你的工程可以以.Net 2.0 subset模式运行,请看这个帖子中的方法. 地址:http:/ ...

  9. 红橙Darren视频笔记 热更新 bsdiff bspatch 在Android设备上的应用 架构篇1完结篇

    概述 当时红橙的视频讲解就差不多90分钟,但是真正自己做出来热更新的demo还是花了八九个晚上,期间遇到各种各样的问题,什么叫台上一分钟 台下十年功是深有体会了. 本节会涉及一部分NDK的知识 推荐阅 ...

最新文章

  1. linux bash shell 常用快捷键
  2. [内存管理] linux kernel内存映射实例分析
  3. 【学习Python】的网站
  4. 各种图(流程图,思维导图,UML,拓扑图,ER图)简介
  5. mysql innodb索引原理
  6. elasticsearch 去重计数
  7. mariadb mysql 5.7_MariaDB 10.1 和 MySQL 5.7 在普通商用硬件上的表现
  8. 【渝粤教育】电大中专学前儿童社会教育作业 题库
  9. journalctl命令使用
  10. android4.2.2+手机管家,深度清理手机垃圾 腾讯手机管家V4.2评测
  11. 武汉大学计算机在职,武汉大学计算机技术工程硕士专业在职研究生 学位论文工作开展步骤概述...
  12. 还是忍不住说说特斯拉的恐怖
  13. 【Shiro权限管理】22.Shiro之记住我
  14. 运动垫的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  15. Python实战——外星人入侵游戏
  16. OpenCV训练分类器制作xml文档
  17. AGI (Analytical Graphics Inc.)
  18. Package pdftex.def Error: PDF mode expected, but DVI mode detected!
  19. 灌篮青春完结篇----灌篮.青春
  20. 微信 3.6.0 正式版来了,上班族狂喜!

热门文章

  1. Instant Client Light:unsupported server character set ZHS16GBK
  2. 滴滴青桔单车跨端技术方案和业务技术架构,及框架设计和性能提升实践
  3. 深圳软件测试培训:测试当中用到的性能指标
  4. Elasticsearch的这几个概念你还不知道啥意思呢?
  5. 什么是HBase?终于有人讲明白了
  6. 旅行照片剪辑--呼伦贝尔篇--9.18
  7. IBM Spectrum LSF社区版下载
  8. StartSonar.bat启动闪退问题
  9. 【积跬步以至千里】App Crashed - WriteMiniDump
  10. python简单小动画