You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)系统中没有这个权限组的定义,因此安装时系统无法识别,导致失败。

解决步骤

  1. 修改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"/>
  1. 重新编译应用,再尝试安装到Android 12设备即可解决问题。

内容的提问来源于stack exchange,提问作者Mark Anderson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:21:01