Android 应用添加系统签名权限的几种方式实现介绍

15 篇文章 0 订阅

Android 应用添加系统签名权限的几种方式实现介绍

一、前言

Android 应用添加系统签名就能获取到系统权限调用一些系统接口,

添加系统签名的方式主要包括:

在Android Studio中配置签名文件生成apk 和 在源码目录编译添加系统签名生成apk。

本文介绍的都是一些基础的签名知识,后续延伸介绍相关权限内容。有兴趣的可以进行了解。

二、Android 应用添加系统签名权限的几种方式介绍

1、在Android Studio添加系统签名文件

源码中生成 jks 签名文件:

https://blog.csdn.net/wenzhi20102321/article/details/134898404

Studio 在对应需要签名的module(默认是app)的build.gradle中添加如下代码:

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.0"
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
。。。
    //证书信息在这里配置
    signingConfigs {
        main {
            storeFile file("./platform.jks") //签名文件路径,根目录
            storePassword "skg202302"
            keyAlias "skg"
            keyPassword "skg202302"
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            signingConfig signingConfigs.main //添加这一行
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
            signingConfig signingConfigs.main //添加这一行
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

只是添加了系统签名是不行的,还需要在AndroidManifest.xml中声明系统权限

AndroidManifest.xml 配置uid系统权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    coreApp="true"
    package="com.xxx.xxx"
    android:sharedUserId="android.uid.system">

2、源码编译apk添加系统签名

如果apk没有签名文件和为添加签名信息,那么添加uid.system后,该应用是无法安装到对应系统上的;

如果要安装可以在源码里面编译,生成的apk就会加入签名的信息的。

Android.mk
# 编译的目录,默认写法
include $(CLEAR_VARS)

# 设置模块名
LOCAL_MODULE := mymodule

# 设置预构建文件的源路径和目标路径
LOCAL_SRC_FILES := path/file.apk

# 设置系统权限和目录
LOCAL_CERTIFICATE := platform
LOCAL_PRIVATE_PLATFORM_APIS := true
# 设置是否编译在 priv-app,如果没有指定目录默认为 system/priv-app
LOCAL_PRIVILEGED_MODULE := true 

include $(BUILD_PREBUILT)

Android.bp
android_app_import {
    name: "FileManager",
    apk: "FileManager.apk",
    //生成到priv-app目录下
    privileged: true,
    //使用系统签名
    certificate: "platform",
}

3、源码编译app代码添加系统签名

Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

# 设置模块名为myapp
LOCAL_MODULE := myapp
# 添加需要编译的Java源文件
LOCAL_SRC_FILES := $(wildcard *.java)
# 添加需要编译的资源文件
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
# 设置输出APK的路径和名称
LOCAL_PACKAGE_NAME := myapp

# 设置系统权限和目录
LOCAL_CERTIFICATE := platform
LOCAL_PRIVATE_PLATFORM_APIS := true
# 设置是否编译在 priv-app,如果没有指定目录默认为 system/app
LOCAL_PRIVILEGED_MODULE := true 

include $(BUILD_PACKAGE)
Android.bp
android_app {
    name: "myapp",
    srcs: [
        "path/to/your/app/src/**/*.java",
    ],
    resource_dirs: [
        "path/to/your/app/res",
    ],
    manifest: "path/to/your/app/AndroidManifest.xml",
    
    //加载类库
    static_libs: [
        "mylibrary",
    ],
    
    //是否系统签名
    certificate: "platform",
    platform_apis: true,
    //是否生成到priv-app目录
    privileged: true,
    
}

上面是代码的情况,编译apk获取系统签名的

Android13或者更新的源代码,大多都是使用Android.bp进行编译。

上面就是本文的主要内容,下面是一些相关内容知识。

三、缺少系统权限报错示例

这里以Settings设置属性为示例讲解。

Settings属性获取是不需要权限的,Settings属性设置是需要权限的,为啥会这样?

其实是因为代码里面定义的,具体是哪个类的哪行代码限制的?有兴趣的可以往下看看:

//Settings 获取属性值,不用权限和签名
int value = Settings.Global.getInt(getContentResolver(), Settings.Global.WIFI_ON, -1);

//Settings 设置属性值,需要系统签名或者系统权限,否则会异常
boolean value = Settings.Global.putInt(getContentResolver(), Settings.Global.WIFI_ON, 1);

//Wifi开关控制,需要系统签名或者系统权限,否则返回false,但是不会异常
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
boolean value =  wifiManager.setWifiEnabled(true);

1、设置Settings属性报错

设置wifi开关状态报错:

 Caused by: java.lang.SecurityException: Permission denial: writing to settings requires:android.permission.WRITE_SECURE_SETTINGS
        at android.os.Parcel.createExceptionOrNull(Parcel.java:3011)
        at android.os.Parcel.createException(Parcel.java:2995)
        at android.os.Parcel.readException(Parcel.java:2978)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
        at android.content.ContentProviderProxy.call(ContentProviderNative.java:732)
        at android.provider.Settings$NameValueCache.putStringForUser(Settings.java:3021)
        at android.provider.Settings$Global.putStringForUser(Settings.java:16753)
        at android.provider.Settings$Global.putString(Settings.java:16594)
        at android.provider.Settings$Global.putInt(Settings.java:16824)
        at com.demo.systemapp.MainActivity.setSettingsValue(MainActivity.java:29)
        at java.lang.reflect.Method.invoke(Native Method) 

从报错日志看是缺少了权限:WRITE_SECURE_SETTINGS。这个权限是需要签名或者系统应用才能拥有的权限。

如果要规避应用崩溃,可以通过try {}catch捕获异常,避免崩溃。

2、设置Wifi开关状态失败

调用wifi开关接口日志:

2024-01-20 17:09:04.193 638-1052/system_process I/WifiService: setWifiEnabled packageName = com.demo.systemapp, enable = true
2024-01-20 17:09:04.195 638-1052/system_process I/WifiService: setWifiEnabled not allowed for uid=10084

上面的日志都是 WifiServiceImpl.java 里面的日志。

显示了是哪个应用,调用了wifi开关的什么状态,后续显示not allowed ,因为uid = 100084,相当于啥都没做。

这种情况没有报错,也不会崩溃,但是就是调用了没效果。

所以大概可以猜测出需要设置Settings属性或者调用Wifi开关状态的接口都是要系统权限或者系统签名。

3、Settings属性设置为啥需要系统权限?

看看代码就知道了:

frameworks\base\core\java\android\provider\Settings.java

//Settings.XXX.Put属性需要权限的原因,代码这里定义了的。
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static boolean putString(ContentResolver resolver,String name,String value,。。。){
    return putStringForUser(resolver, name, value, tag, makeDefault,
            resolver.getUserId(), DEFAULT_OVERRIDEABLE_BY_RESTORE);
}

代码这里已经声明了,必须要系统应用和 WRITE_SECURE_SETTINGS权限,

才能调用后续的put方法,所以未获取到权限就会抛出异常。

4、设置Wifi开关状态为啥失败?

根据下面代码的具体判断就可以看得出:

packages\modules\Wifi\service\java\com\android\server\wifi\WifiServiceImpl.java

//WifiManager.setWifiEnabled 失败原因,这里判断后返回了false
public synchronized boolean setWifiEnabled(String packageName, boolean enable) {
  		
  		//这里判断的是三方应用,主要是uid,三方应用不具有系统签名,所以返回false,不往下走
  		if (isThirdParty && !isTargetSdkLessThanQ) {
            mLog.info("setWifiEnabled not allowed for uid=%").c(callingUid).flush();
            return false;
        }
        //这里判断是否是飞行模式并且是否不是系统应用(priv-app目录下的应用)
        //如果是飞行模式,并且不是priv-app目录的系统应用,返回false
        // If Airplane mode is enabled, only privileged apps are allowed to toggle Wifi
        if (mSettingsStore.isAirplaneModeOn() && !isPrivileged) {
            mLog.err("setWifiEnabled in Airplane mode: only Settings can toggle wifi").flush();
            return false;
        }
		
		//如果不是priv-app目录应用,并且热点开启的情况,返回false
        // If SoftAp is enabled, only privileged apps are allowed to toggle wifi
        if (!isPrivileged && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
            mLog.err("setWifiEnabled with SoftAp enabled: only Settings can toggle wifi").flush();
            return false;
        }
        ...
}

上面是Android13 中wifi开关状态返回false的几种情况分析。

四、其他

1、系统权限介绍

framework 定义的所有 权限都是有定义包含 protectionLevel 等级的,主要权限等级有:普通,运行时,系统签名,特殊 。

(1)普通权限(normal)

此类权限允许访问超出应用沙盒的数据和执行超出应用沙盒的操作。但这些数据和操作对用户隐私及对其他应用的操作带来的风险非常小。

比如,上网权限,wifi 状态监听,蓝牙,获取后台任务等待,大概有一百多个。

(2)运行时权限(dangerous)

运行时权限也称为危险权限,此类权限授予应用对受限数据的额外访问权限,并允许应用执行对系统和其他应用具有更严重影响的受限操作。因此需要先在应用中请求运行时权限,然后才能访问受限数据或执行受限操作。当应用请求运行时权限时,系统会显示运行时权限弹窗提示。

比如,文件读写权限,日历读取和设置权限等待,总共不到一百个。

(3)签名权限(signature)

当应用声明了其他应用已定义的签名权限时,如果两个应用使用同一证书进行签名,系统会在安装时向前者授予该权限。否则,系统无法向前者授予该权限。(注意:有些签名权限不适合第三方应用使用。)

该权限只需要在manifest中声明使用,同时应用和这类权限定义者拥有一样的签名系统就会默认授予应用这类权限;系统授予这类权限后应用无需像运行时权限一样动态申请。

比如,Android11 新增的文件管理权限,对wifi,热点,Camera一些特殊操作权限,比较多,大概有五百多个。

(4)特殊权限(privileged)

特殊权限与特定的应用操作相对应。只有平台和原始设备制造商 (OEM) 可以定义特殊权限。此外,如果平台和 OEM 想要防止有人执行功能特别强大的操作(例如通过其他应用绘图),通常会定义特殊权限。系统设置中的特殊应用访问权限页面包含一组用户可切换的操作。其中的许多操作都以特殊权限的形式实现。每项特殊权限都有自己的实现细节。系统会为特殊权限分配“appop”保护级别。

比如本文的: 允许应用程序在所有用户之间进行交互 权限,启动前台服务权限,可通过overlay形式覆盖属性权限,应用主题更改监听等等,大概有三百多个。

一般情况下:特殊权限大部分与系统签名文件同时使用。

我们常用的广播都定义在framework\base\core\res\AndroidManifest.xml里面,如果要新增可以在里面新增

值得注意的是,系统签名文件是比系统应用权限大的,比如:

    //MANAGE_CAMERA 权限,必须要系统签名才能获取到
    <permission android:name="android.permission.MANAGE_CAMERA"
        android:protectionLevel="signature" />
    //CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS 权限,系统签名或者priv-app应用都可以获取到
    <permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
        android:protectionLevel="signature|privileged" />

上面的是 Android13 上比较新的权限定义,有兴趣的可以看看。

在线查看权限定义网址:

http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/core/res/AndroidManifest.xml

2、如何查看一个应用的权限情况?

dumpsys package XXX包名,可以查看apk信息:

E:\Studio\project\test\SystemApp>adb shell
rk3588_t:/ $ ^C
130|rk3588_t:/ $ dumpsys package com.demo.systemapp

Packages:
  Package [com.demo.systemapp] (6e1e98c): //1、应用包名
    userId=10083 //uid,system.uid = 10000
    pkg=Package{1a148d5 com.demo.systemapp}
    codePath=/system/app/SystemAppDemo //2、apk路径
    resourcePath=/system/app/SystemAppDemo
    timeStamp=2024-01-20 11:45:31
    lastUpdateTime=2024-01-20 11:45:31
    requested permissions:  //3、AndroidManifest中声明的权限
      android.permission.WRITE_SECURE_SETTINGS
      android.permission.POST_NOTIFICATIONS
    User 0: ceDataInode=9991 installed=true hidden=false suspended=false distractionFlags=0 stopped=false notLaunched=false enabled=0 instant=false virtual=false
      installReason=0
      firstInstallTime=2024-01-20 11:45:31
      uninstallReason=0
      runtime permissions:  //4、获取到的权限,granted=true 才算获取到!
        android.permission.POST_NOTIFICATIONS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]

rk3588_t:/ $
E:\Studio\project\test\SystemApp>

从dumpsys package XXX 命令的日志,可以看到包名应用对应的Uid信息,版本信息,apk安装位置,安装时间,AndroidManifest声明的权限,实际获取到的权限等等信息。

requested permissions 下面的权限是应用声明的权限,runtime permissions的权限是当前获取到的权限。

值得注意的是即使是系统应用或者系统签名应用也是要在 AndroidManifest 中声明需要的权限。

3、系统权限级别总结

Android中不同应用权限级别从低到高总结场景
(1)普通应用。只用到了普通权限,比如上网权限等等。
(2)system/app 系统应用,用来可以限制无法手动卸载,对系统权限没有要求的情况,无法调用系统api
(3)system/priv-app 系统应用(未设置uid.system),无法手动卸载,可以调用部分系统api
(4)系统签名应用普通安装的方式,可以调用系统相关api,同时也可以被手动卸载,可以添加privileged特殊权限
(5)system/priv-app 系统应用(设置uid.system),无法手动卸载,可以调用全部系统api,可以添加privileged特殊权限

了解到不同的权限需求,应用要安装/编译在什么目录心里就有个底了。

4、Android APEX:系统新篇章的应用扁平化技术

Android13 和更新的版本编译,你会发现package/modules下面的代码模块都是编译到了目录: /system/apex/XXXPackage/XXX.apk

这里面的apk有些是系统签名应用,有些是普通应用,这个和具体的bp里面的编译规则相关。

APEX 的相关介绍:

https://blog.csdn.net/u011897062/article/details/133122565

https://blog.csdn.net/u010164190/article/details/122324409

这块知识看起来还是比较复杂。

对于普通应用开发者,和一般的系统应用开发者其实没啥研究价值。

可以简单的理解一下模块化的概念:比如把一个apk的某些模块编译成一个apk,这些模块可以接收广播处理事务或者启动服务处理事务等功能。

这个和应用开发中的模块化是同样的原理,只是源码中Android13 以前很少这样开发,后续更新的源码应该是会更多这样的模块。

  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峥嵘life

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值