Android 12应用安装失败:权限组android.permission-group.MESSAGES不存在
问题:Android 12安装应用失败,提示权限组不存在
问题现象
- 应用在Android 8、10、11设备上运行正常,编译时TargetVersion设为34
- 安装到Android 12设备时失败,报错信息:
Installation failed due to: 'Failed to commit install session 558053483 with command cmd package install-commit 558053483. Error: -127: Package com.xxxxx.xxxxx.free.debug attempting to declare permission com.xxxxx.xxxxx.free.debug.permission.DELETE_MESSAGES in non-existing group android.permission-group.MESSAGES'
- 编译Release版本后仍出现相同报错
相关Manifest代码
自定义权限声明
<permission android:name="${applicationId}.permission.DELETE_MESSAGES" android:description="@string/delete_messages_desc" android:label="@string/delete_messages_label" android:permissionGroup="android.permission-group.MESSAGES" android:protectionLevel="dangerous"/>
ContentProvider配置
<provider android:name="com.xxxxx.xxxxx.external.MessageProvider" android:authorities="${applicationId}.messageprovider" android:exported="false" android:grantUriPermissions="true" android:multiprocess="true" android:readPermission="${applicationId}.permission.READ_MESSAGES" android:writePermission="${applicationId}.permission.DELETE_MESSAGES"/>
补充代码:MessageProvider实现
public class MessageProvider extends ContentProvider { public static String AUTHORITY = BuildConfig.APPLICATION_ID + ".messageprovider"; public static Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); private static final String[] DEFAULT_MESSAGE_PROJECTION = new String[] { MessageColumns._ID, MessageColumns.SEND_DATE, MessageColumns.SENDER, MessageColumns.SUBJECT, MessageColumns.PREVIEW, MessageColumns.ACCOUNT, MessageColumns.URI, MessageColumns.DELETE_URI, MessageColumns.SENDER_ADDRESS }; private static final String[] DEFAULT_ACCOUNT_PROJECTION = new String[] { AccountColumns.ACCOUNT_NUMBER, AccountColumns.ACCOUNT_NAME, }; private static final String[] UNREAD_PROJECTION = new String[] { UnreadColumns.ACCOUNT_NAME, UnreadColumns.UNREAD }; private UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); private List<QueryHandler> queryHandlers = new ArrayList<>(); /** * How many simultaneous cursors we can afford to expose at once */ Semaphore semaphore = new Semaphore(1); ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1); @Override public boolean onCreate() { registerQueryHandler(new ThrottlingQueryHandler(new AccountsQueryHandler())); registerQueryHandler(new ThrottlingQueryHandler(new MessagesQueryHandler())); registerQueryHandler(new ThrottlingQueryHandler(new UnreadQueryHandler())); return true; } public static void init() { Timber.v("Registering content resolver notifier"); final Context context = DI.get(Context.class); MessagingController messagingController = DI.get(MessagingController.class); messagingController.addListener(new SimpleMessagingListener() { @Override public void folderStatusChanged(Account account, String folderServerId, int unreadMessageCount) { context.getContentResolver().notifyChange(CONTENT_URI, null); } }); } @Override public String getType(Uri uri) { Timber.v("MessageProvider/getType: %s", uri); return null; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Timber.v("MessageProvider/query: %s", uri); int code = uriMatcher.match(uri); if (code == -1) { throw new IllegalStateException("Unrecognized URI: " + uri); } Cursor cursor; try { QueryHandler handler = queryHandlers.get(code); cursor = handler.query(uri, projection, selection, selectionArgs, sortOrder); } catch (Exception e) { Timber.e(e, "Unable to execute query for URI: %s", uri); return null; } return cursor; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { Timber.v("MessageProvider/delete: %s", uri); // Note: can only delete a message List<String> segments = uri.getPathSegments(); int accountId = Integer.parseInt(segments.get(1)); String folderServerId = segments.get(2); String msgUid = segments.get(3); // get account Account myAccount = null; for (Account account : Preferences.getPreferences(getContext()).getAccounts()) { if (account.getAccountNumber() == accountId) { myAccount = account; if (!account.isAvailable(getContext())) { Timber.w("not deleting messages because account is unavailable at the moment"); return 0; } } } if (myAccount == null) { Timber.e("Could not find account with id %d", accountId); } if (myAccount != null) { MessageReference messageReference = new MessageReference(myAccount.getUuid(), folderServerId, msgUid, null); MessagingController controller = MessagingController.getInstance(getContext()); controller.deleteMessage(messageReference, null); } // FIXME return the actual number of deleted messages return 0; } @Override public Uri insert(Uri uri, ContentValues values) { Timber.v("MessageProvider/insert: %s", uri); return null; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Timber.v("MessageProvider/update: %s", uri); // TBD return 0; } /** * Register a {@link QueryHandler} to handle a certain {@link Uri} for * {@link #query(Uri, String[], String, String[], String)} */ protected void registerQueryHandler(QueryHandler handler) { if (queryHandlers.contains(handler)) { return; } queryHandlers.add(handler); int code = queryHandlers.indexOf(handler); uriMatcher.addURI(AUTHORITY, handler.getPath(), code); } public static class ReverseDateComparator implements Comparator<MessageInfoHolder> { @Override public int compare(MessageInfoHolder object2, MessageInfoHolder object1) { if (object1.compareDate == null) { return (object2.compareDate == null ? 0 : 1); } else if (object2.compareDate == null) { return -1; } else { return object1.compareDate.compareTo(object2.compareDate); } } } protected interface QueryHandler { /** * The path this instance is able to respond to. */ String getPath(); Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws Exception; } /** * Extracts a value from an object. */ public interface FieldExtractor<T, K> { K getField(T source); } public static class DeleteUriExtractor implements FieldExtractor<MessageInfoHolder, String> { @Override public String getField(MessageInfoHolder source) { LocalMessage message = source.message; int accountNumber = message.getAccount().getAccountNumber(); return CONTENT_URI.buildUpon() .appendPath("delete_message") .appendPath(Integer.toString(accountNumber)) .appendPath(message.getFolder().getServerId()) .appendPath(message.getUid()) .build() .toString(); } }
解决方案
报错核心原因:android.permission-group.MESSAGES是Android 13(API 33)才新增的系统权限组,Android 12(API 31/32)系统中没有这个权限组的定义,因此安装时系统无法识别,导致失败。
解决步骤
- 修改Manifest中的自定义权限声明:
- 直接移除
android:permissionGroup="android.permission-group.MESSAGES"属性,系统会自动为权限分配默认组; - 或者替换为Android 12及以下系统已存在的权限组,比如
android.permission-group.SMS(如果权限和短信操作相关)或android.permission-group.CONTACTS。
- 直接移除
修改后的权限声明示例(移除权限组):
<permission android:name="${applicationId}.permission.DELETE_MESSAGES" android:description="@string/delete_messages_desc" android:label="@string/delete_messages_label" android:protectionLevel="dangerous"/>
- 重新编译应用,再尝试安装到Android 12设备即可解决问题。
内容的提问来源于stack exchange,提问作者Mark Anderson
相关产品推荐
相关产品推荐

