1.引言
android system/etc 中有很多配置文件,例如白名单,例如system-private 权限区别,还例如 如何配置aapp的内存。这些都是懵逼的。抱着好奇心态,决定深入进去看看一般是怎么设置的。参考链接:https://blog.csdn.net/superkris/article/details/7709504/
2.Android的权限规则
2.1 基于UserID的进程级别的安全机制
进程有独立的地址空间,进程与进程间默认是不能互相访问的,Android通过为每一个apk分配唯一的linux userID来实现,名称为"app_"加一个数字,比如app_43不同的UserID,运行在不同的进程,所以apk之间默认便不能相互访问。
Android提供了如下的一种机制,可以使两个apk打破前面讲的这种壁垒。 在AndroidManifest.xml中利用sharedUserId属性给不同的package分配相同的userID,通过这样做,两个package可以被当做同一个程序, 系统会分配给两个程序相同的UserID。当然,基于安全考虑,两个apk需要相同的签名,否则没有验证也就没有意义了
2.2 默认apk生成的数据对外是不可见的
实现方法是:Android会为程序存储的数据分配该程序的UserID。借助于Linux严格的文件系统访问权限,便实现了apk之间不能相互访问似有数据的机制。 例:我的应用创建的一个文件,默认权限如下,可以看到只有UserID为app_21的程序才能读写该文件
2.3 root权限和system权限
-
root权限
拥有root权限的用户,其uid=0. 拥有系统约定的最高权限。可以访问绝大部分文件
-
system 权限
system/app 下面的apk
system/priv-app 下面的apk。
俩者的区别在于:俩者虽然都是系统app,但是system/priv-app 中所具有的权限,要大于system/app
-
特权权限
特权应用程序是位于
/system/priv-app
目录下的系统应用程序 。过去,设备制造商几乎无法控制可对特权应用授予哪些签名|特许权限。从 Android 8.0 开始,制造商必须在/etc/permissions
目录下的系统配置 XML 文件中明确授予特许权限。从 Android 9 开始,实现人员必须明确授予或拒绝授予所有特许权限,否则设备将无法启动。
frameworks\base\core\res\AndroidManifest.xml 定义了各种广播,普通权限,危险权限,特权权限:
<!-- Allows notifications to be colorized
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
android:protectionLevel="signature|setup" /> //setting 具备的
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
android:protectionLevel="signature" /> //系统签名
<!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_FINGERPRINT"
android:protectionLevel="signature|privileged" /> //系统签名且还需要在system/priv-app 下
<!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
android:protectionLevel="signatureorsystem " /> //系统签名且还得是系统app(system/priv-app 或system/app)
Android 权限分成四类:
普通权限(normal permission):也叫正常权限,即使拥有了该类权限,用户的隐私数据被泄露篡改的风险也很小。例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。
敏感权限(dangerous permission):也叫危险权限,运行时权限,跟普通权限相反,一旦某个应该获取了该类权限,用户的隐私数据就面临被泄露篡改的风险。比如 READ_CONTACTS 权限就属于危险权限。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。
签名权限(signature permission):该类权限只对拥有相同签名的应用开放,比如手机QQ 自定义了一个permission 且在权限标签中加入 android:protectionLevel=”signature”,而访问它的某个数据时,必须要拥有该权限。然后微信和 QQ 发布时采用相同的签名,微信就可以申请访问 QQ 中的此权限,并使用对应权限控制的数据。其他程序即使知道了这个开放数据的接口,也在 Manifest 注册了权限,但由于应用签名不同,还是无法访问的对应的数据。
系统签名权限(signatureOrSystem permission):与 signature permission类似,但它不光要求签名相同,还要求是同类的系统级应用,一般手机厂商开发的预制应用,才会用到该类权限
应用场景1:
前提:app 系统签过名且放置于system/app/下
操作: 调用 Settings.System.putInt
现象:
Caused by: java.lang.IllegalArgumentException: You cannot keep your settings in the secure settings.
解决方案: 将app 防止于system/priv-app下
问题: 为什么放置于system/priv-app就解决了问题呢?
源码分析:putInt方法最终会调用: enforceRestrictedSystemSettingsMutationForCallingPackage 方法
1858 private void enforceRestrictedSystemSettingsMutationForCallingPackage(int operation,
1859 String name, int userId) {
1860 // System/root/shell can mutate whatever secure settings they want.
1861 final int callingUid = Binder.getCallingUid();
1862 final int appId = UserHandle.getAppId(callingUid);
1863 if (appId == android.os.Process.SYSTEM_UID
1864 || appId == Process.SHELL_UID
1865 || appId == Process.ROOT_UID) {
1866 return;
1867 }
1868
1869 switch (operation) {
1870 case MUTATION_OPERATION_INSERT:
1871 // Insert updates.
1872 case MUTATION_OPERATION_UPDATE: {
1873 if (Settings.System.PUBLIC_SETTINGS.contains(name)) {
1874 return;
1875 }
1876
1877 // The calling package is already verified.
1878 PackageInfo packageInfo = getCallingPackageInfoOrThrow(userId);
1879
1880 // Privileged apps can do whatever they want. 只要是特权应用,就可以直接插入数据
1881 if ((packageInfo.applicationInfo.privateFlags
1882 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
1883 return;
1884 }
1885 // 检查是否具备插入数据的资格,没得就会抛出异常
1886 warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
1887 packageInfo.applicationInfo.targetSdkVersion, name);
1888 } break;
18
1910 }
应用场景2:
前提:国内 app 采用系统签名,放在/system/priv-app/下,但是没有添加 android:sharedUserId="android.uid.system
操作:app本身是一个service,需要有读写权限
现象:app中直接进行创建/删除文件,报错,提示没得读写权限
解决方案:
Y:\aosp\device\google\marlin\default-permissions.xml:
<exceptions>
<exception package="com.verizon.mips.services">
<permission name="android.permission.PROCESS_OUTGOING_CALLS" fixed="false"/>
<permission name="android.permission.READ_PHONE_STATE" fixed="false"/>
<permission name="android.permission.RECEIVE_SMS" fixed="false"/>
</exception>
</exceptions>
default-permissions 配置文件会在第一次开机的时候加载。所以假如直接更改开发版中的default-permissions。需要格式化开发板,恢复出厂模式、
应用场景3:
前提:国内 app 采用系统签名,放在/system/priv-app/下,但是没有添加 android:sharedUserId="android.uid.system且android:persistent="false"
操作: 代码中直接使用startService启动服务
现象:
Not allowed to start service Intent { flg=0x1000000 cmp=packagename/.servicename (has extras) }: app is in background uid UidRecord{52db80 u2357s1000 TRNB bg:+2m42s199ms idle procs:3 seq(0,0,0)}
问题: app系统签名且置于/system/app/下 但是为啥startService 还报错呢?且为什么加上android:persistent="true"就ok了呢
解答:
startService 启动服务流程中,有一个方法appServicesRestrictedInBackgroundLocked
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " is persistent; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
if (uidOnBackgroundWhitelist(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
应用场景4:
前提:AndroidManifest 中添加android:sharedUserId="android.uid.system"
操作:代码中调用安装apk的代码
现象:弹出如下所示的dialog
问题:为什么会弹出这个框?如何解决这个问题
第一个问题解答:ActivityManagerService#checkGrantUriPermissionLocked
if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
9715 if ("com.android.settings.files".equals(grantUri.uri.getAuthority())
9716 || SystemConfig.getInstance().getAuthoriesInPreloadedApks().contains(
9717 grantUri.uri.getAuthority())) {
9718 // Exempted authority for
9719 // 1. cropping user photos and sharing a generated license html
9720 // file in Settings app
9721 // 2. sharing a generated license html file in TvSettings app
9722 } else {
9723 Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
9724 + " grant to " + grantUri + "; use startActivityAsCaller() instead");
9725 return -1;
9726 }
9727 }
callUid为SYSTEM_UID,且不满足俩个if 则return -1 ,于是就出现上述的安装失败情况。
解决方式有俩种:
- 去掉android:sharedUserId
- 更改SystemConfig.java