聊聊Android6.0 以上系统权限

聊聊Android6.0 以上系统权限,第1张

一个新建的Android应用默认是没有权限的,这意味着它不能执行任何可能对用户体验有不利影响的 *** 作或者访问设备数据。为了使用受保护的功能,你必须包含一个或者多个标签在你的app manifest中。

1、Android 60中权限分为两种,普通权限和危险权限(即运行时权限,下面统称运行时权限)。

11普通权限

如果你的应用manifest中只申明了普通权限(也就是说,这些权限对于用户隐私和设备 *** 作不会造成太多危险),系统会自动授予这些权限。

12运行时权限

如果你的应用manifest中声明了运行时权限(也就是说,这些权限可能会影响用户隐私和设备的普通 *** 作),系统会明确的让用户决定是否授予这些权限。系统请求用户授予这些权限的方式是由当前应用运行的系统版本来决定的。

121 Android60及以上的系统

如果你的设备运行的是Android60(API level 23)及以上的系统,并且你的应用的targetSdkVersion也是23或者更高,那么应用向用户请求这些权限是实时的。这意味着用户可以随时取消 这些运行时权限的授权。所以应用在每次需要用到这些运行时权限的时候都需要去检查是否还有这些权限的授权。

122 Android 51及以下的系统

如果你的设备运行在Android51(API level 22)及以下的系统中,或者你的app的targetSdkVersion是22或者更低。系统会请求用户在apk安装的时候授予这些权限。

如果你的应用更新的时候添加了一个权限,系统会在用户更新应用的时候请求用户授予这个权限 , 一旦用户安装了这个应用,唯一可以取消授权的方式就是卸载掉这个应用。 注意这句话的意思,想一下如果app的targetSdkVersion是22或者以下,但是运行在Android60及以上的设备中会有什么问题?后面会分析这个问题 。

常常来说一个授权失败会抛出SecurityException,然而这并不是在 所有情况下都会发生。比如,发送一个广播去检查授权(SendBroadcast(Intent)),数据会被发送给所有接收者,但是当这个方法的请求返 回的时候,你不会收到任何一个因为授权失败抛出的异常,其实在大多数情况下,授权失败只会打印系统日志。

13自动权限调整

简单的说,如果你的app targetSdkVersion是3,而你当前运行的系统版本是4,那么在android version 4 中新添加的权限会自动添加到你的app中。

比如 WRITE_EXTERNAL_STORAGE权限是在api 4的时候添加的,而你的应用的targetSdkVersion是3,那么这个权限会自动添加到你的应用中。而且在官方商店上这个权限也会列出来(尽管可能你并不需要这个权限)。

所以建议经常更新你的targetSdkVersion到最新版本。

下面来回答上面的那个问题,如果app的targetSdkVersion是22或者以下,但是运行在android 60或以上版本的手机中,会发生什么?

安装过程中,会一起请求用户授予所有 权限,如果用户拒绝,将不能安装这个app,只有用户全部同意这些授权,才能安装这个应用,但是问题来了,安装好了这个应用之后,android60以 上的系统中,用户是可以去设置中取消授权的,而且是随时都可以取消,所以很多运行时权限可能也得不到,目前官方的做法是,如果用户取消该项授权,那么依赖 该项授权的方法的返回值为null,所以你的app可能会报空指针异常。以后是否会针对22以下的app做改变还不得而知,毕竟crash是很难让人接受 的,但是crash是由用户造成的,用户应该也可以理解。

2、普通权限和运行时权限

系统权限会被传递给两种不同的保护级别,我们所知道这两种最重要的保护级别就是普通权限和运行时权限。

21 普通权限

普通权限的覆盖区域是在你的app需要访问沙盒以外的数据和资源的时候,但是对用户隐私和其他app的 *** 作只有很少的影响,比如开启手电筒的权限。这个时候,系统会自动授权这些普通权限。

Normal Permissions:

ACCESS_LOCATION_EXTRA_COMMANDS

ACCESS_NETWORK_STATE

ACCESS_NOTIFICATION_POLICY

ACCESS_WIFI_STATE

BLUETOOTH

BLUETOOTH_ADMIN

BROADCAST_STICKY

CHANGE_NETWORK_STATE

CHANGE_WIFI_MULTICAST_STATE

CHANGE_WIFI_STATE

DISABLE_KEYGUARD

EXPAND_STATUS_BAR

GET_PACKAGE_SIZE

INSTALL_SHORTCUT

INTERNET

KILL_BACKGROUND_PROCESSES

MODIFY_AUDIO_SETTINGS

NFC

READ_SYNC_SETTINGS

READ_SYNC_STATS

RECEIVE_BOOT_COMPLETED

REORDER_TASKS

REQUEST_INSTALL_PACKAGES

SET_ALARM

SET_TIME_ZONE

SET_WALLPAPER

SET_WALLPAPER_HINTS

TRANSMIT_IR

UNINSTALL_SHORTCUT

USE_FINGERPRINT

VIBRATE

WAKE_LOCK

WRITE_SYNC_SETTINGS

22 运行时权限

运行时权限的覆盖区域是你的app想要的数据和资源涉及用户的隐私信息,或者是可能潜在的影响用户的存储数据或者其他app的 *** 作。比如,请求获取用户联系人信息的权限。如果一个app申明了运行时权限,用户必须明确的授权这些权限给app。

23 权限组

所有的运行时权限都属于对应的权限组,如果你的app运行在android60及以上系统,下面的规则都适用:

231 如果一个app在manifest中请求了一个运行时权限,而且app还没有得到这个运行时权限所在的权限组中的任何一个运行时权限授权,那么系统会d出一个对话框,描述app想要访问的运行时权限的权限组,这个对话框不会描述这个权限组中某一个特定的权限。比如,你的app想要请求READ_CONTACT权限,对话框只会描述app想要请求设备的联系人,如果用户授权通过,系统会授予app所请求的该项权限。

232 如果一个app在manifest中请求一个运行时权限,并且这个app已经在相同的权限组中有了另一个运行时权限的授权,那么系统不会d出对话框,而是会立即授予app该项运行时权限的授权。比如,一个app之前请求过一个READ_CONTACT的权限并且被授权通过,之后又请求一个WRITE_CONTACT(在同一个权限组)权限,那么系统会自动授予该权限。

其实所有权限(普通权限、运行时权限、用户自定义权限)都属于特定的权限组,但是只有运行时权限的权限组才会影响用户体验。

233运行时权限组和权限组列表

group:androidpermission-groupCONTACTS

permission:androidpermissionWRITE_CONTACTS

permission:androidpermissionGET_ACCOUNTS

permission:androidpermissionREAD_CONTACTS

group:androidpermission-groupPHONE

permission:androidpermissionREAD_CALL_LOG

permission:androidpermissionREAD_PHONE_STATE

permission:androidpermissionCALL_PHONE

permission:androidpermissionWRITE_CALL_LOG

permission:androidpermissionUSE_SIP

permission:androidpermissionPROCESS_OUTGOING_CALLS

permission:comandroidvoicemailpermissionADD_VOICEMAIL

group:androidpermission-groupCALENDAR

permission:androidpermissionREAD_CALENDAR

permission:androidpermissionWRITE_CALENDAR

group:androidpermission-groupCAMERA

permission:androidpermissionCAMERA

group:androidpermission-groupSENSORS

permission:androidpermissionBODY_SENSORS

group:androidpermission-groupLOCATION

permission:androidpermissionACCESS_FINE_LOCATION

permission:androidpermissionACCESS_COARSE_LOCATION

group:androidpermission-groupSTORAGE

permission:androidpermissionREAD_EXTERNAL_STORAGE

permission:androidpermissionWRITE_EXTERNAL_STORAGE

group:androidpermission-groupMICROPHONE

permission:androidpermissionRECORD_AUDIO

group:androidpermission-groupSMS

permission:androidpermissionREAD_SMS

permission:androidpermissionRECEIVE_WAP_PUSH

permission:androidpermissionRECEIVE_MMS

permission:androidpermissionRECEIVE_SMS

permission:androidpermissionSEND_SMS

permission:androidpermissionREAD_CELL_BROADCASTS

可以通过adb shell pm list permissions -d -g进行查看。

看到上面的dangerous permissions,会发现一个问题,好像危险权限都是一组一组的,恩,没错,的确是这样的,那么有个问题:分组对我们的权限机制有什么影响吗?的确是有影响的,如果app运行在Android 6x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了 同一组 的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时d出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。

3、相关API

31 在AndroidManifest文件中添加需要的权限。

这个步骤和我们之前的开发并没有什么变化,试图去申请一个没有声明的权限可能会导致程序崩溃。

32 检查权限

if (ContextCompatcheckSelfPermission(thisActivity,

ManifestpermissionREAD_CONTACTS)

!= PackageManagerPERMISSION_GRANTED) {

}else{

//

}

这里涉及到一个API,ContextCompatcheckSelfPermission,主要用于检测某个权限是否已经被授予,方法返回值为PackageManagerPERMISSION_DENIED或者PackageManagerPERMISSION_GRANTED。当返回DENIED就需要进行申请授权了

33 申请授权

ActivityCompatrequestPermissions(thisActivity,

new String[]{ManifestpermissionREAD_CONTACTS},

MY_PERMISSIONS_REQUEST_READ_CONTACTS);

该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框 逐一 询问用户是否授权。

34 处理权限申请回调

@Override

publicvoidonRequestPermissionsResult(intrequestCode,

String permissions[],int[] grantResults) {

switch(requestCode) {

case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {

// If request is cancelled, the result arrays are empty

if(grantResultslength >0&& grantResults[0] == PackageManagerPERMISSION_GRANTED) {

// permission was granted, yay! Do the contacts-related task you need to do

}else{

// permission denied, boo! Disable the functionality that depends on this permission

}

return;

}

}

}

ok,对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。如果申请成功,就可以做你的事情了!

那么将上述几个步骤结合到一起就是:

// Here, thisActivity is the current activity

if (ContextCompatcheckSelfPermission(thisActivity,

ManifestpermissionREAD_CONTACTS)

!= PackageManagerPERMISSION_GRANTED) {

// Should we show an explanation

if (ActivityCompatshouldShowRequestPermissionRationale(thisActivity,

ManifestpermissionREAD_CONTACTS)) {

// Show an expanation to the user asynchronously -- don't block

// this thread waiting for the user's response! After the user

// sees the explanation, try again to request the permission

} else {

// No explanation needed, we can request the permission

ActivityCompatrequestPermissions(thisActivity,

new String[]{ManifestpermissionREAD_CONTACTS},

MY_PERMISSIONS_REQUEST_READ_CONTACTS);

// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an

// app-defined int constant The callback method gets the

// result of the request

}

}

谢幕,至此有关于android60 以上权限相关的内容已经详细讲完了!

Set

UID

会创建s与t权限,是为了让一般用户在执行某些程序的时候,能够暂时具有该程序拥有者的权限。举例来说,我们知道,账号与密码的存放文件其实是

/etc/passwd与 /etc/shadow而 /etc/shadow文件的权限是“-r- - - - - - - -

”。它的拥有者是root在这个权限中,仅有root可以“强制”存储,其他人是连看都不行的。

但是,偏偏笔者使用dmtsai这个一般身份用户去更新自己的密码时,使用的就是

/usr/bin/passwd程序,却可以更新自己的密码。也就是说,dmtsai这个一般身份用户可以存取 /etc/shadow密码文件。这怎么可能?明明

/etc/shadow就是没有dmtsai可存取的权限。这就是因为有s权限的帮助。当s权限在user的x时,也就是类似 -r - s - - x - - x,称为Set

UID,简称为SUID,这个UID表示User的ID,而User表示这个程序(/usr/bin/passwd)的拥有者(root)。那么,我们就可以知道,当dmtsai用户执行

/usr/bin/passwd时,它就会“暂时”得到文件拥有者root的权限。

SUID仅可用在“二进制文件(binary

file)”,SUID因为是程序在执行过程中拥有文件拥有者的权限,因此,它仅可用于二进制文件,不能用在批处理文件(shell脚本)上。这是因为

shell脚本只是将很多二进制执行文件调进来执行而已。所以SUID的权限部分,还是要看shell脚本调用进来的程序设置,而不是shell脚本本身。当然,SUID对目录是无效的。这点要特别注意。

 Set

GID

进一步而言,如果s的权限是在用户组,那么就是Set

GID,简称为SGIDSGID可以用在两个方面。

文件:如果SGID设置在二进制文件上,则不论用户是谁,在执行该程序的时候,它的有效用户组(effective

group)将会变成该程序的用户组所有者(group

id)。

目录:如果SGID是设置在A目录上,则在该A目录内所建立的文件或目录的用户组,将会是此A目录的用户组。

一般来说,SGID多用在特定的多人团队的项目开发上,在系统中用得较少。

 Sticky

Bit

这个Sticky Bit当前只针对目录有效,对文件没有效果。SBit对目录的作用是:“在具有SBit的目录下,用户若在该目录下具有w及x权限,则当用户在该目录下建立文件或目录时,只有文件拥有者与root才有权力删除”。换句话说:当甲用户在A目录下拥有group或other的项目,且拥有w权限,这表示甲用户对该目录内任何人建立的目录或文件均可进行“删除/重命名/移动”等 *** 作。不过,如果将A目录加上了Sticky

bit的权限,则甲只能够针对自己建立的文件或目录进行删除/重命名/移动等 *** 作。

举例来说,/tmp本身的权限是“drwxrwxrwt”,在这样的权限内容下,任何人都可以在

/tmp内新增、修改文件,但仅有该文件/目录的建立者与root能够删除自己的目录或文件。这个特性也很重要。可以这样做个简单测试:

1 以root登入系统,并且进入 /tmp中。

2 touch

test,并且更改test权限成为777

3 以一般用户登入,并进入 /tmp

4

尝试删除test文件。

SUID/SGID/SBIT权限设置

前面介绍过SUID与SGID的功能,那么,如何打开文件使其成为具有SUID与SGID的权限呢?这就需要使用数字更改权限了。现在应该知道,使用数字更改权限的方式为“3个数字”的组合,那么,如果在这3个数字之前再加上一个数字,最前面的数字就表示这几个属性了(注:通常我们使用chmod

xyz filename的方式来设置filename的属性时,则是假设没有SUID、SGID及Sticky

bit)。

4为SUID

2为SGID

1为Sticky

bit

假设要将一个文件属性改为“-rwsr-xr-x”,由于s在用户权限中,所以是SUID,因此,在原先的755之前还要加上4,也就是使用

“chmod 4755

filename”来设置。此外,还有大S与大T的产生。参考下面的范例(注意:下面的范例只是练习而已,所以笔者使用同一个文件来设置,必须知道,SUID不是用在目录上,SBIT不是用在文件上)。

[root@linux ~]# cd /tmp

[root@linux tmp]# touch test

[root@linux tmp]# chmod 4755 test; ls -l test

-rwsr-xr-x 1 root root 0 Jul 20 11:27 test

[root@linux tmp]# chmod 6755 test; ls -l test

-rwsr-sr-x 1 root root 0 Jul 20 11:27 test

[root@linux tmp]# chmod 1755 test; ls -l test

-rwxr-xr-t 1 root root 0 Jul 20 11:27 test

[root@linux tmp]# chmod 7666 test; ls -l test

-rwSrwSrwT 1 root root 0 Jul 20 11:27 test

# 这个例子要特别小心。怎么会出现大写的S与T呢?不都是小写的吗?

#

因为s与t都是取代x参数的,但是,我们是使用

#

7666也就是说,user、group以及others都没有x这个可执行的标志

# (因为666)。所以,S、T表示“空的”。

#

SUID是表示“该文件在执行时,具有文件拥有者的权限”,但文件

# 拥有者都无法执行了,哪里来的权限给其他人使用呢?当然就是空的

要解答这个看似简单的问题,需要先复习一下Linux系统里“命令”这个词的含义。

Linux系统中的命令有两种:一是内置命令,是Shell与生俱来的一部分,比如最基础的 cd 、 echo 、 kill 等;二是外部命令,包含已编译的实用程序以及Shell脚本两种,它们两者又可以统称为可执行文件(executables)。我们平时常用的大多数看起来像“内置(自带)命令”的命令,其实都是/usr/bin及其他目录下的已编译程序,如 ls 、 ps 、 grep 等。

可见,外部命令的实体并不存在于Shell中,而是在调用时才从对应的位置加载。如果用户调用命令时没有提供路径的话,Shell通过PATH环境变量来定位外部命令的路径。

如果在PATH给定的路径仍然找不到命令,Shell就会返回"command not found"。这也就解释了为什么在Linux下安装完JDK之后,总是要将$JAVA_HOME/bin写入PATH——用户肯定不想每次调用JDK提供的命令时都要先cd到JDK的安装路径,或者把路径写得清清楚楚。

按照上文所述,我们平时自己写的Shell脚本也是外部命令。下面在/tmp/test目录下直接创建一个文件: touch my_scriptsh ,并在其中写几句简单的话。

然后以不同的方式执行这个脚本。

这段示例的信息量蛮大的,下面以Q&A的形式逐个解决问题。

并没有,只是表示相对路径(即当前目录)而已, /my_scriptsh 即在当前目录/tmp/test执行my_scriptsh脚本。用绝对路径和用相对路径执行脚本是等价的,因此可以干脆将它们打包统称为“/方式”,以与“sh方式”区分开来。

在/方式下,当前Shell进程会使用fork系统调用产生一个子Shell进程,并使用execve系统调用让OS在子Shell进程中执行脚本。execve系统调用必然会检查脚本文件的权限,而新touch出来的文件权限是 -rw-r--r-- ,并没有可执行(x)权限,所以会报Permission denied。如果把权限改正,那么它的执行结果与sh方式是相同的。

sh方式的不同点在于,OS会通过 sh 命令的调用直接产生一个新的Shell进程,并将 my_scriptsh 当作命令行参数传进去。新的Shell进程就会读取、解释并执行该脚本,而OS不关心该文件到底是什么,自然也就不要求可执行权限,只要求读权限就可以了。

Linux与Windows不同,如果用户不指定路径,那么就会直接跳过对当前路径的检查,直接按照PATH来查找。而PATH里自然是没有当前路径的,command not found就顺理成章了。也就是说,加 / 是为了告诉Shell:“我已经确定我要执行的东西在当前路径了”。

看官可能会问,干脆在PATH里直接加上当前路径(即 ),不就可以免去打 / 的麻烦了吗?Linux非常不推荐这样做,安全风险极大。举个极端的例子:一个普通用户在自己的家目录新建了一个名为ls的脚本,但里面的内容是 rm -rf / 。然后root用户来到该用户的家目录,并执行ls命令,如果PATH里有当前路径的话,结果可想而知。

source是Shell(准确地说是Bash)的内置命令,在Bourne Shell中的等价命令是一个点 ,即点命令。用source命令执行脚本文件时,是在当前Shell进程中执行,而不是像/与sh方式一样在新的Shell进程中执行,因此早先设置的变量在脚本里是可以读取到的。

source一般不用来执行业务脚本,最常见用途是在某些初始化脚本修改之后使其立即生效,即 source /etc/profile 这样。

shebang是指脚本文件中以字符 #! 开头的第一行,它用来指定这个脚本该用哪种解释器来解释。上文中出现的 #!/bin/sh 就表示应该使用sh(在这里就是Bash)来解释它。

需要注意,只有/方式执行脚本才会读取shebang并调用指定的解释器,而“sh方式”(sh当然可以换成任意其他的解释器)会忽略shebang。举个例子,新建一个Python脚本,但是shebang仍然故意错写:

如果执行 /my_scriptpy 的话,会报语法错误,因为Bash不能解释Python;执行 python my_scriptpy 是正常的,因为会直接用Python解释器。若把shebang改回正确的 #!/usr/bin/python ,那么两种方式都能正常执行。

实际上,shebang甚至可以写成任意外部命令(当然不推荐这样做)。举个有趣的栗子,创建脚本rm_selfsh:

执行 sh rm_selfsh ,会输出"I am still here!",并且rm_selfsh文件本身还在。但若是执行 /rm_selfsh ,则没有任何输出,并且rm_selfsh文件本身消失了。

没有权限。

先Root。

-----------------------------------------------------------------------------------------------------

安卓精英团为你解答

安卓精英团欢迎各位精英加入

两种可能性

1.手机驱动没装好

2.adb不是最高权限启动

如果是Linux下,进入su模式,先adb kill-server然后 adb start-server

以上就是关于聊聊Android6.0 以上系统权限全部的内容,包括:聊聊Android6.0 以上系统权限、Linux中chmod中的 permission(r,w,x,s,t) 里的s和t代表什么意思呢、用sh、./、source执行Shell脚本到底有何不同等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9340220.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存