安装失败,原因是重复权限... C2D_MESSAGE

189

我正在我的应用程序中使用Google通知,到目前为止,在清单文件中我已经完成了以下内容:

<!-- GCM -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Keeps the processor from sleeping when a message is received. --> 
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <!-- This app has permission to register and receive data message. --> 

<!-- Creates a custom permission so only this app can receive its messages. NOTE: APP_PACKAGE.permission.C2D_MESSAGE -->   
<permission android:name="com.myapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.myapp.permission.C2D_MESSAGE" />    
<!-- END GCM -->

在我将Nexus 7更新到Android 5.0之前,它运行得非常完美。
现在当我尝试使用Eclipse在该设备上安装应用程序时,我会遇到以下错误:

INSTALL_FAILED_DUPLICATE_PERMISSION perm=com.myapp.permission.C2D_MESSAGE pkg=com.myapp

我不明白出了什么问题?在Android 5.0之前它一直都能正常运行。
我知道我在两个地方中都使用了C2D_MESSAGE,即在permissionuses-permission中,但我是从原始的Google GCM指南复制了这段代码,所以它应该没问题。


20
你最初认为是一个不明显的错误(嗯,我也是...),实际上是一项新的安全功能,防止签名不同的两个应用程序声明相同的自定义权限并安装到设备上。 - AZ_
@NullPointerException 你能否接受答案,因为它对许多人有帮助。 - Pratik Butani
24个回答

233

我找到了一个适合我的解决方案。

在我的设备(Nexus 7)上,Android 5.0 Lollipop 版本中,我按照以下步骤操作:

卸载应用后,在“已下载”选项卡的应用列表中,您会找到 App Name

  • 进入设置
  • 点击应用
  • 在列表底部,你会看到带有“未安装”的标签的YourApp
  • 打开该应用
  • 点击OptionMenu并选择“为所有用户卸载”

完成这些步骤后,我成功安装了新的应用程序,并且它运行良好。


6
我有一台运行 Android 5.0.1 的 Nexus 9 设备,但我无法看到带有“未安装”标签的应用程序。 - xXJohnRamboXx
这对我有用,谢谢!我的问题略有不同。我正在对我们的移动应用进行自动化测试(Xamarin.UITest),并上传了一个不同版本进行一些手动测试。当我尝试通过VS / Xamarin UITest将应用程序加载到设备上时,我开始看到这个问题。再次感谢。 - LuFaMa
这个应该是正确答案:https://dev59.com/318d5IYBdhLWcg3wNQNg#27767179 - Sharjeel
@Pratik Butani,这个解决方案对我很有效。我想知道出现错误的原因?你能解释一下吗? - Karthikeyan Ve
2
我的应用程序没有出现在带有“未安装”标签的列表底部。你是什么意思? - IgorGanapolsky
显示剩余4条评论

151

移除

<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
<permission
    android:name="${applicationId}.permission.C2D_MESSAGE"
    android:protectionLevel="signature"/>

运行应用程序...

然后再次添加权限并运行应用程序。

完成了!


32
当您尝试在同一设备上安装应用程序的调试版本和发布版本时,常常会出现此问题。这个解决方案可以轻松解决。我不知道您可以在清单文件中使用变量。点赞! - grandouassou
6
如果应用程序已经在生产中,这个答案就不可行了。我不能期望我的应用程序用户在没有权限的情况下下载新版本,并再次安装具有权限的更新版本。 - nilsmagnus
6
如果你的应用程序有不同名称的调试和发布版本,导致发生这种情况,那么卸载应用程序并非必要,只需使用${applicationId}替代硬编码即可使其正常工作。 - yuval
2
使用 ${applicationId} 而不是静态应用程序 ID 解决了我的问题!!!包括在使用Flavors时!!! - JannGabriel
1
@yuval的评论非常准确,为我解决了这个问题。在众多答案中,这个最好地涵盖了这个问题:https://dev59.com/318d5IYBdhLWcg3wNQNg#36992939 - Jonik
显示剩余2条评论

51

我在Android-21上遇到了一个自定义签名权限的问题,并通过确保进行完全卸载来解决它。

这是一种边缘情况,会在以下情况下发生:

  1. 应用程序使用签名级别安全性定义自定义权限
  2. 您尝试使用不同密钥签名的版本更新已安装的应用程序
  3. 测试设备正在运行支持多个用户的Android 21或更高版本

命令行示例

以下是一个命令行演示了此问题以及如何解决它。在此时点上,一个调试版本已经安装好了,而我正在尝试安装一个使用发布密钥签名的生产版本:

# This fails because the debug version defines the custom permission signed with a different key:

[root@localhost svn-android-apps]# . androidbuildscripts/my-adb-install Example release
920 KB/s (2211982 bytes in 2.347s)
        pkg: /data/local/tmp/Example-release.apk
Failure [INSTALL_FAILED_DUPLICATE_PERMISSION perm=com.example.android.example.PERMISSION_EXAMPLE_PLUGIN pkg=com.example.android.example]

# I use uninstall -k because apparently that is similar to uninstalling as a user
# by dragging the app out of the app tray:

[root@localhost svn-android-apps]# /android-sdk-linux/platform-tools/adb uninstall -k com.example.android.example
The -k option uninstalls the application while retaining the data/cache.
At the moment, there is no way to remove the remaining data.
You will have to reinstall the application with the same signature, and fully uninstall it.
If you truly wish to continue, execute 'adb shell pm uninstall -k com.example.android.example'

# Let's go ahead and do that:

[root@localhost svn-android-apps]# /android-sdk-linux/platform-tools/adb shell pm uninstall -k com.example.android.example
Success

# This fails again because the custom permission apparently is part of the data/cache
# that was not uninstalled:

[root@localhost svn-android-apps]# . androidbuildscripts/my-adb-install Example release
912 KB/s (2211982 bytes in 2.367s)
        pkg: /data/local/tmp/Example-release.apk
Failure [INSTALL_FAILED_DUPLICATE_PERMISSION perm=com.example.android.example.PERMISSION_EXAMPLE_PLUGIN pkg=com.example.android.example]

# In spite of the warning above, simply doing a full uninstall at this point turned out to 
# work (for me):

[root@localhost svn-android-apps]# /android-sdk-linux/platform-tools/adb uninstall com.example.android.example
Success

# Release version now successfully installs:

[root@localhost svn-android-apps]# . androidbuildscripts/my-adb-install Example release
898 KB/s (2211982 bytes in 2.405s)
        pkg: /data/local/tmp/Example-release.apk
Success

[root@localhost svn-android-apps]# 

Eclipse示例

如果您尝试在已经安装了发布版本的情况下从Eclipse安装调试版本,您会看到以下对话框:

Eclipse reinstall dialog

此时,您只需回答“是”,安装就会成功。

设备示例

正如另一个答案中所指出的那样,您还可以在设备设置中转到应用程序信息页面,点击溢出菜单,并选择“卸载所有用户”来防止此错误。


谢谢,但你应该从设备设置中的应用信息页面开始回答,点击溢出菜单,然后选择“卸载所有用户”以防止此错误。 - yajnesh
请嫁给我。根据您的修改,我进入“设置”>“应用程序”,触摸旧版本的应用程序,并在选项菜单中选择“卸载所有用户”。 - Rudolf Real
你的前置条件列表中的第二个条件不一定是真实的。在我的情况下,我并没有更新应用程序,而是按照此过程安装了一个具有不同包名称的相同应用程序的调试版本。https://dev59.com/RmMl5IYBdhLWcg3wVltf#21006552。这是一个我看不到解决方案的问题。 - Nilzor

38

我成功解决了这个问题,而不需要首先卸载备用的apk(真是太麻烦了,对吧?)。要成功安装调试和发布版本的apk,只需在AndroidManifest.xml中使用gradle内置的${applicationId}占位符,在编译时修改权限的android:name值即可。

build.gradle文件片段:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
        ...
    }
}

AndroidStudio.xml 文件片段:

<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
<permission
    android:name="${applicationId}.permission.C2D_MESSAGE"
    android:protectionLevel="signature"/>

您可以使用 aapt l -a app-debug.apk 命令检查修改后的 AndroidManifest.xml 文件,以确保占位符已经正确应用。如果您使用多种产品口味,我相信您可以使用此方法的变体来适应您的需求。


似乎如果我在两个不同的“产品风味(productFlavors)”中定义不同的 applicationId,并使用 ${applicationId},则效果将相同。 - Robert
如果需要多种选择,我认为这是更健壮的解决方案。 - Robert
考虑到不是每个同事都能够进行一些hack来简单构建项目并安装apk。 - goemic
谢谢,@jackpile,这个应用程序在我的电脑上运行得非常好,只有一个问题,如果同时安装了发布版和调试版,则在安装调试版后,会打开发布版。 - Prateek Surana
请将以下与编程有关的内容从英语翻译成中文。仅返回已翻译的文本:应将其标记为正确答案.. 谢谢@Jackpile - Ahmed Garhy

34

从您的清单文件中删除任何“硬编码”的包名称引用。

即使您不使用productFlavors,这也是最佳实践。

例如,如果您的清单文件包含以下内容:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="com.yourpackage.name.permission.C2D_MESSAGE"/>

<permission
    android:name="com.yourpackage.name.permission.C2D_MESSAGE"
    android:protectionLevel="signature"/>
<permission
    android:name="com.yourpackage.name.permission.MAPS_RECEIVE"
    android:protectionLevel="signature"/>

转换为:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>

<permission
    android:name="${applicationId}.permission.C2D_MESSAGE"
    android:protectionLevel="signature"/>
<permission
    android:name="${applicationId}.permission.MAPS_RECEIVE"
    android:protectionLevel="signature"/>

然后,在您的模块gradle文件中,设置相关的applicationId

signingConfigs {
    stage {
        storeFile file('keystore/stage.keystore')
        storePassword 'android'
        keyAlias 'androiddebugkey'
        keyPassword 'android'
    }
    production {
        storeFile file('keystore/playstore.keystore')
        storePassword store_password
        keyAlias key_alias
        keyPassword key_password
    }
}

productFlavors {
    staging {
        signingConfig signingConfigs.staging
        applicationId defaultConfig.applicationId + ".staging"
        versionName defaultConfig.versionName + "-staging"
    }

    production {
        signingConfig signingConfigs.production
    }
}

你可以按照这个教程了解更多信息


3
使用 ${applicationId} 解决了我的问题! - Andrew
1
这如何解决所提到的问题? - Vincent

19

尝试使用adb卸载应用程序:

adb uninstall com.yourpackage

1
很奇怪,我在我的Nexus 6上不得不这样做,尽管我已经通过系统界面卸载了我的APK,并且它也没有显示在已安装的应用程序下面。 - Jon Willis
它给了我“Failure [DELETE_FAILED_INTERNAL_ERROR]”。可能的原因是什么? - Reaz Murshed
1
这对我来说非常完美,因为其他方法都不起作用。感谢您的分享。 - BobbyMick

17

当出现此错误时,它会清楚地提到应用程序的包名称,因此被拒绝授权。仅卸载应用程序将无法解决问题。为了解决问题,我们需要执行以下步骤:

  1. 进入设置
  2. 进入应用程序
  3. 进入已下载的应用程序列表
  4. 您可以在列表中看到已卸载应用程序
  5. 单击该应用程序,进入更多选项
  6. 单击卸载所有用户选项

问题解决 :D


8
在OS 5.0中安装应用程序时,我收到了以下消息:
INSTALL_FAILED_DUPLICATE_PERMISSION perm=com.myapp.permission.C2D_MESSAGE pkg=com.myapp

没有重复的包,我们可以通过手动卸载旧应用程序或使用adb来解决此问题:

adb uninstall com.yourpackage


6

以上方法对我都无效。在Lollipop之前,我的应用程序一切正常。但是当我在Lollipop上测试它时,出现了上述错误。它拒绝安装。我没有安装任何先前版本,所以所有以上解决方案在我的情况下都无效。但幸好有这个SO解决方案,现在它运行正常。就像大多数开发者一样,我遵循谷歌的误导教程,通过复制和粘贴添加权限,像这样:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="com.google.android.permission.C2D_MESSAGE" 
            android:protectionLevel="signature" />

这适用于低于Lollipop版本的旧版。现在我已经改为:
<uses-permission android:name="com.mycompany.myappname.c2dm.permission.RECEIVE" />
<permission android:name="com.mycompany.myappname.permission.C2D_MESSAGE" 
            android:protectionLevel="signature" />

4
CommonsWare是正确的,但在我看来,这种说法有点糟糕:"设备上安装的apk与您尝试安装的新apk签名不同"。过去通常会因为错误的证书要求是否卸载设备上的应用程序,所以这可能是一个新的错误。
虽然解决方案很痛苦,但最好的方法是手动卸载该应用程序。此外,为了团队开发的目的,我们将调试密钥库添加到我们的存储库中,并指示gradle使用它,如下所示:
android {
    ...
    signingConfigs {
        debug {
            storeFile file("../certificates/debug.keystore")
        }
    }

    ...

    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
    }

    ...
}

现在,当我们在团队成员之间传递设备时,我们都使用相同的调试证书,因此没有任何问题。:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接