Android Rights Management
1. Basic Introduction
_Android security architecture states that by default, no application has the right to perform any action that adversely affects other applications, operating systems or users. This includes reading and writing the user's private data, such as contacts or e-mail, reading and writing files from other applications, performing network access, keeping the device awake, and so on.
_Application privileges are based on system security features and help Android support the following goals related to user privacy:
- Control: Users can control the data they share with the app.
- Transparency: Users can see what data is used by the application and why the application accesses the data.
- Data Minimization: Applications can only access and use the data required for a specific task or operation invoked by the user.
Permission Type
Android divides permissions into different types, including install-time permissions, run-time permissions, and special permissions. Each permission type indicates the limited range of data that the application can access and the limited range of operations that the application can perform when the system grants the permission to apply.
1. Installation Permissions
_Installation-time privileges grant the application restricted access to restricted data and allow the application to perform restricted operations that have minimal impact on the system or other applications. If install-time permissions are declared in an application, they are automatically granted when the user allows the application to install.
_Installation rights are divided into several subtypes, including common rights and signature rights.
-
General permissions
Such permissions allow access to data beyond the application sandbox and perform operations beyond the application sandbox. However, these data and operations pose little risk to user privacy and operations on other applications. The system assigns a "normal" level of protection to common privileges. -
Signature rights
When an application declares the signing rights defined by another application, if two applications sign with the same signing file, the system grants that right to the other application at installation time. Otherwise, the system cannot grant the privilege to the former. Signature rights are assigned a "signature" protection level.
2. Runtime permissions
_Runtime privileges, also known as dangerous privileges, grant an application additional access to restricted data and allow the application to perform restricted operations that have more severe impact on the system and other applications. You need to request permissions at runtime before you can access restricted data or perform restricted operations. When the application requests runtime permissions, a pop-up box displays the runtime permission request prompt. Many runtime privileges access user private data, which is a special kind of restricted data that contains potentially sensitive information. For example, location information and contact information belong to the user's private data. Runtime privileges are assigned a "dangerous" protection level.
_Runtime privilege mechanism is a new feature of Android 6.0 (M), so different Android versions of devices and targetSdkVersion settings can affect the performance of applications applying for dangerous privileges.
- In Android6. When used on devices prior to version 0, applications will apply for old permission systems, hazardous permissions, or installations of applications, regardless of whether the targetSdkVersion is greater than 23.
- In Android6. The targetSdkVersion set in the application will be used to determine when the device version 0 or higher is used: 1) If the targetSdkVersion is lower than 23, the old rules will continue to be used and hazardous rights will be applied for at installation time; 2) If the targetSdkVersion is greater than or equal to 23, the application needs to apply for permission dynamically at runtime.
Note: Starting with Android 6.0 (Marshmallow, API 23), users can revoke a privilege for an application at any time, even if the targetSdkVersion applied is less than 23. Therefore, you must ensure that the application performs well after requesting permissions fails.
_If the device is running Android 6.0 or higher and the targetSdkVersion applied is 23 or higher, the system will behave as follows when the user requests dangerous permissions:
- If an application requests a dangerous permission that is already listed in its manifest file and the application does not currently have any permissions for that permission group, a dialog box is displayed to the user asking if the user is authorized, which describes the permission group the application wants to access instead of the specific permissions within the group. For example, if an application requests READ_CONTACTS privilege, the system will pop up a dialog box to inform the user that the application needs to access the device's contacts, if the user allows authorization, then the system will grant the application the required privileges.
- If an application requests a dangerous permission that is already listed in its manifest file, and the application currently has other dangerous permissions for that permission group, the system grants that permission immediately without notifying the user. For example, if an application has been previously requested and granted READ_CONTACTS permission, then it requests WRITE_ This privilege will be granted immediately upon CONTACTS.
Permission group concept:
_Permissions are grouped according to the functions and features of the device. The system handles permission requests in the form of permission groups, which may correspond to several permissions requested in Manifest. For example, the STORAGE permission group includes READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions. The privilege group approach makes it easier for users to understand privileges and handle APP privilege requests, preventing too many complex privileges from being granted individually.
_Any privilege can belong to a privilege group, including normal privileges and application customized privileges. However, permission groups only affect the user experience when permissions are dangerous. Permission groups with normal permissions can be ignored.
Hazardous permissions and corresponding groupings are as follows:
3. Special privileges
_Special privileges correspond to specific application operations. Only platform and original equipment manufacturer (OEM) can define special permissions. In addition, if platforms and OEMs want to prevent someone from performing particularly powerful operations, such as drawing through other applications, special privileges are usually defined. Special privileges are assigned an "appop" protection level. After Android 6.0, only system applications will be able to use these special privileges.
2. Dynamic Permission Application
1. Main usage methods
(1)ContextCompat.checkSelfPermission
_Check if the application has a dangerous permission, and if the application has this permission, the method will return to PackageManager.PERMISSION_GRANTED; If the application does not have this permission, the method will return to PackageManager.PERMISSION_DENIED.
(2)ActivityCompat.requestPermissions
_The application can dynamically apply for permissions through this method, after which a dialog box prompts the user to authorize the group of permissions requested.
(3)ActivityCompat.shouldShowRequestPermissionRationale
_Check if this permission was last denied. This method returns true if the permission was previously requested and the user rejected the request, but the request can continue. The following returns false:1) Users have never applied for this privilege; 2) In the past, the user rejected the permission request and chose the Don't ask again option in the permission request system dialog box; 3) The user has allowed the request for this privilege; 4) The device specification prohibits the application of this authority.
_Therefore, it is useless to use this method alone and should be used in the callback for requesting permission.
Note: There may be differences in the handling of rights between different mobile phone systems. Some mobile phone systems default to no more pop-up if they choose to reject the authorization request when popping up, and then call this method to return false.
(4)onRequestPermissionsResult
_When permission is requested, a dialog box is displayed to the user. When the user responds, the system calls the applied onRequestPermissionsResult() method, passes the user response to it, and processes the corresponding scenario.
2. Call process
(1) In Android Manifest. The permissions to be applied for are stated in the xml.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.CAMERA"></uses-permission> <uses-permission android:name="android.permission.WRITE_CONTACTS"></uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
(2) Check the permissions before applying for permissions, and apply for permissions if the application does not have such permissions; If you apply for more than one privilege at a time, you should check the privileges one by one, and finally apply for unauthorized privileges in a unified way.
private String PM_SINGLE=Manifest.permission.WRITE_EXTERNAL_STORAGE; //Request Single Permission public void applyForSinglePermission(){ Log.i(TAG,"applyForSinglePermission"); try{ //If the operating system SDK level is above 23 (android6.0), make a dynamic permission request if(Build.VERSION.SDK_INT>=23){ //Determine whether you have permissions int nRet=ContextCompat.checkSelfPermission(this,PM_SINGLE); Log.i(TAG,"checkSelfPermission nRet="+nRet); if(nRet!= PackageManager.PERMISSION_GRANTED){ Log.i(TAG,"Perform Permission Request..."); ActivityCompat.requestPermissions(this,new String[]{PM_SINGLE},10000); } else{ showToast("Permissions authorized"); } } }catch(Exception e){ e.printStackTrace(); } } private String[] PM_MULTIPLE={ Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.CAMERA,Manifest.permission.WRITE_CONTACTS }; //Request Multiple Permissions public void applyForMultiplePermissions(){ Log.i(TAG,"applyForMultiplePermissions"); try{ //If the operating system SDK level is above 23 (android6.0), make a dynamic permission request if(Build.VERSION.SDK_INT>=23){ ArrayList<String> pmList=new ArrayList<>(); //Get the current list of unauthorized permissions for(String permission:PM_MULTIPLE){ int nRet=ContextCompat.checkSelfPermission(this,permission); Log.i(TAG,"checkSelfPermission nRet="+nRet); if(nRet!= PackageManager.PERMISSION_GRANTED){ pmList.add(permission); } } if(pmList.size()>0){ Log.i(TAG,"Perform Permission Request..."); String[] sList=pmList.toArray(new String[0]); ActivityCompat.requestPermissions(this,sList,10000); } else{ showToast("All permissions are authorized"); } } }catch(Exception e){ e.printStackTrace(); } }
(3) Override the onRequestPermissionsResult callback method to listen for user authorization operations.
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); try{ ArrayList<String> requestList=new ArrayList<>();//Allow Question List ArrayList<String> banList=new ArrayList<>();//Prohibited List for(int i=0;i<permissions.length;i++){ if(grantResults[i] == PackageManager.PERMISSION_GRANTED){ Log.i(TAG,"["+permissions[i]+"]Permission authorization succeeded"); } else{ //Determine whether to allow reapplication of this privilege boolean nRet=ActivityCompat.shouldShowRequestPermissionRationale(this,permissions[i]); Log.i(TAG,"shouldShowRequestPermissionRationale nRet="+nRet); if(nRet){//Allow re-application requestList.add(permissions[i]); } else{//Prohibit applications banList.add(permissions[i]); } } } //Prioritize the prohibition list if(banList.size()>0){//Inform the role of the privilege and require that the privilege be granted manually showFinishedDialog(); } else if(requestList.size()>0){//Inform about the role of permissions and reapply showTipDialog(requestList); } else{ showToast("Permission authorization succeeded"); } }catch (Exception e){ e.printStackTrace(); showToast("Exception occurred in permission request callback"); } } public void showFinishedDialog(){ AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("warning") .setMessage("Please go to Settings to open the relevant permissions, otherwise the function will not work properly!") .setPositiveButton("Determine", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // In general, if the user does not authorize, the function will not run, do exit processing finish(); } }) .create(); dialog.show(); } public void showTipDialog(ArrayList<String> pmList){ AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("Tips") .setMessage("["+pmList.toString()+"]Permissions are necessary to apply permissions, please authorize") .setPositiveButton("Determine", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String[] sList=pmList.toArray(new String[0]); //Re-apply for this permission ActivityCompat.requestPermissions(MainActivity.this,sList,10000); } }) .create(); dialog.show(); }
3. PermissionUtil Tool Class
_This class is a Self-encapsulated tool class for Android privilege requests. It can apply for multiple privileges by privilege group name or privilege name, and encapsulate the processing logic for user authorization operation. It can define different response processing for user authorization by passing in callback interface.
PermissionUtil implementation:
import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.util.Log; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; /** * author:chenjs */ public class PermissionUtil { private static final String TAG=PermissionUtil.class.getSimpleName(); private static final boolean LOG_FLAG=true;//Log Identification //calendar private static final String[] Group_Calendar={ Manifest.permission.READ_CALENDAR,Manifest.permission.WRITE_CALENDAR }; //Camera private static final String[] Group_Camera={ Manifest.permission.CAMERA }; //Mail list private static final String[] Group_Contacts={ Manifest.permission.WRITE_CONTACTS,Manifest.permission.GET_ACCOUNTS, Manifest.permission.READ_CONTACTS }; //Location private static final String[] Group_Location={ Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION }; //Microphone private static final String[] Group_Microphone={ Manifest.permission.RECORD_AUDIO }; //Telephone private static final String[] Group_Phone={ Manifest.permission.READ_PHONE_STATE,Manifest.permission.CALL_PHONE, Manifest.permission.READ_CALL_LOG,Manifest.permission.WRITE_CALL_LOG, Manifest.permission.ADD_VOICEMAIL,Manifest.permission.USE_SIP, Manifest.permission.PROCESS_OUTGOING_CALLS }; //sensor private static final String[] Group_Sensors={ Manifest.permission.BODY_SENSORS }; //Short message private static final String[] Group_Sms={ Manifest.permission.READ_SMS,Manifest.permission.SEND_SMS, Manifest.permission.RECEIVE_SMS,Manifest.permission.RECEIVE_MMS, Manifest.permission.RECEIVE_WAP_PUSH }; //storage private static final String[] Group_Storage={ Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE }; private static Map<String,String[]> m_PermissionGroupList=null; static{ initMap(); } /** * Apply for a set of privileges through the privilege group name * @param context * @param permissionGroupName * @param requestCode * @param listener */ public static void requestByGroupName(Activity context, String permissionGroupName,int requestCode,OnPermissionsListener listener){ requestByGroupName(context, new String[]{permissionGroupName}, requestCode, listener); } /** * Request multiple groups of permissions by permission group name * @param context Activity context * @param pgNameArray Multiple Rights Group Names to Request * @param requestCode Request Code * @param listener Callback interface */ public static void requestByGroupName(Activity context, String[] pgNameArray,int requestCode,OnPermissionsListener listener){ showLog("requestByPermissionGroup"); try{ //If the operating system SDK level is above 23 (android6.0), make a dynamic permission request if(Build.VERSION.SDK_INT>=23 && pgNameArray!=null){ String[] permissionsList=getAppPermissionsList(context);//Apply Permission List ArrayList<String> targetList=new ArrayList<>(); if(permissionsList==null || permissionsList.length==0){ showLog("Access list is empty"); return; } for(String groupName:pgNameArray){ ArrayList<String> tmpPermissionList=isPermissionDeclared(permissionsList,groupName); if(tmpPermissionList==null){//not found showLog("not found["+groupName+"]Permissions in"); continue; } for(int i=0;i<tmpPermissionList.size();i++){ //Determine whether you have permissions int nRet=ContextCompat.checkSelfPermission(context,tmpPermissionList.get(i)); if(nRet!= PackageManager.PERMISSION_GRANTED){ targetList.add(tmpPermissionList.get(i)); } } } if(targetList.size()>0){ showLog("Make the following permission request:"+targetList.toString()); String[] sList=targetList.toArray(new String[0]); ActivityCompat.requestPermissions(context,sList,requestCode); } else{ showLog("All permissions are authorized"); if(listener!=null){ listener.onPermissionsOwned(); } } } }catch(Exception e){ e.printStackTrace(); } } /** * Request a set of permissions by permission name * @param context * @param permission * @param requestCode * @param listener */ public static void requestByPermissionName(Activity context, String permission,int requestCode,OnPermissionsListener listener){ requestByPermissionName(context, new String[]{permission}, requestCode, listener); } /** * Request multiple groups of permissions by permission name * @param context Activity context * @param permissionArray Multiple Rights Names to Request * @param requestCode Request Code * @param listener Callback interface */ public static void requestByPermissionName(Activity context, String[] permissionArray,int requestCode,OnPermissionsListener listener){ showLog("requestPermissions"); try{ //If the operating system SDK level is above 23 (android6.0), make a dynamic permission request if(Build.VERSION.SDK_INT>=23 && permissionArray!=null){ ArrayList<String> targetList=new ArrayList<>(); for(String strPermission:permissionArray){ //Determine whether you have permissions int nRet=ContextCompat.checkSelfPermission(context,strPermission); if(nRet!= PackageManager.PERMISSION_GRANTED){ targetList.add(strPermission); } } if(targetList.size()>0){ showLog("Make the following permission request:"+targetList.toString()); String[] sList=targetList.toArray(new String[0]); ActivityCompat.requestPermissions(context,sList,requestCode); } else{ showLog("All permissions are authorized"); if(listener!=null){ listener.onPermissionsOwned(); } } } }catch(Exception e){ e.printStackTrace(); } } /** * Handle user actions when requesting permissions * @param context * @param permissions Permission to apply * @param grantResults Authorization Status of Permissions * @param listener Callback interface * @param controlFlag A control identifier that determines whether to continue processing the list of re-requests after responding to the list of prohibitions (to avoid conflicting processing both prohibit and re-apply lists, such as popping up two prompt boxes) */ public static void onRequestPermissionsResult(Activity context,String[] permissions, int[] grantResults,OnPermissionsListener listener,boolean controlFlag) { try{ ArrayList<String> requestList=new ArrayList<>();//Re-application List ArrayList<String> banList=new ArrayList<>();//Prohibited List for(int i=0;i<permissions.length;i++){ if(grantResults[i] == PackageManager.PERMISSION_GRANTED){ showLog("["+permissions[i]+"]Permission authorization succeeded"); } else{ boolean nRet=ActivityCompat.shouldShowRequestPermissionRationale(context,permissions[i]); //Log.i(TAG,"shouldShowRequestPermissionRationale nRet="+nRet); if(nRet){//Allow re-application requestList.add(permissions[i]); } else{//Prohibit applications banList.add(permissions[i]); } } } do{ //Prioritize the prohibition list if(banList.size()>0){ if(listener!=null){ listener.onPermissionsForbidden(permissions,grantResults,banList); } if(!controlFlag){//Skip processing the reapplieable list if the control is identified as false after processing the prohibited list break; } } if(requestList.size()>0){ if(listener!=null){ listener.onPermissionsDenied(permissions,grantResults,requestList); } } if(banList.size()==0 && requestList.size()==0){ showLog("Permission authorization succeeded"); if(listener!=null){ listener.onPermissionsSucceed(); } } }while (false); }catch (Exception e){ e.printStackTrace(); } } /** * Judging permission status * @param context * @param permission Permission Name * @return */ public static boolean checkPermission(Context context,String permission){ try{ //If the operating system SDK level is above 23 (android6.0), make a dynamic permission request if(Build.VERSION.SDK_INT>=23){ int nRet= ContextCompat.checkSelfPermission(context,permission); showLog("checkSelfPermission nRet="+nRet); return nRet==PackageManager.PERMISSION_GRANTED? true : false; } return true; }catch(Exception e){ e.printStackTrace(); return false; } } /** * Get the list of permissions in the current application manifest * @param context Application Context * @return */ public static String[] getAppPermissionsList(Context context){ try{ PackageManager packageManager = context.getApplicationContext().getPackageManager(); String packageName=context.getApplicationContext().getPackageName(); String[] array = packageManager.getPackageInfo(packageName,PackageManager.GET_PERMISSIONS).requestedPermissions; return array; }catch (Exception e){ e.printStackTrace(); } return null; } /** * Determines whether permissions in the specified permission group are declared in the permission list * @param permissionList Permission List * @param permissionGroup Permission Group Name * @return Returns found permission group permissions if present, otherwise returns null */ public static ArrayList<String> isPermissionDeclared(String[] permissionList, String permissionGroup){ try{ if(permissionList!=null && permissionGroup!=null){ String[] pmGroup=m_PermissionGroupList.get(permissionGroup); if(pmGroup!=null){ ArrayList<String> arrayList=new ArrayList<>(); //ergodic for(int i=0;i<pmGroup.length;i++){ String strPermission=pmGroup[i]; for(int j=0;j< permissionList.length;j++){ if(strPermission.equals(permissionList[j])){//Find permissions in the specified permission group arrayList.add(strPermission); break; } } } if(arrayList.size()==0){ return null; } return arrayList; } } }catch (Exception e){ e.printStackTrace(); } return null; } private static void initMap(){ if(m_PermissionGroupList==null){ m_PermissionGroupList=new HashMap<>(); m_PermissionGroupList.put(Manifest.permission_group.CALENDAR,Group_Calendar); m_PermissionGroupList.put(Manifest.permission_group.CAMERA,Group_Camera); m_PermissionGroupList.put(Manifest.permission_group.CONTACTS,Group_Contacts); m_PermissionGroupList.put(Manifest.permission_group.LOCATION,Group_Location); m_PermissionGroupList.put(Manifest.permission_group.MICROPHONE,Group_Microphone); m_PermissionGroupList.put(Manifest.permission_group.PHONE,Group_Phone); m_PermissionGroupList.put(Manifest.permission_group.SENSORS,Group_Sensors); m_PermissionGroupList.put(Manifest.permission_group.SMS,Group_Sms); m_PermissionGroupList.put(Manifest.permission_group.STORAGE,Group_Storage); } } private static void showLog(String str){ if(LOG_FLAG){ Log.i(TAG,str); } } public interface OnPermissionsListener { /** * Processing when permissions are already in place */ void onPermissionsOwned(); /** * Handling when permissions are prohibited * @param permissions Full permissions requested * @param grantResults Authorization Status of Permissions * @param pmList List of permissions prohibited from applying */ void onPermissionsForbidden(String[] permissions, int[] grantResults,ArrayList<String> pmList); /** * Processing when permission is denied * @param permissions * @param grantResults * @param pmList List of rights that can be re-applied */ void onPermissionsDenied(String[] permissions, int[] grantResults,ArrayList<String> pmList); /** * Processing when permission request succeeds */ void onPermissionsSucceed(); } }
The following examples can be used:
private final int RequestCode3_1 =10004; //Rewrite by user according to their needs private PermissionUtil.OnPermissionsListener mListener3_1=new PermissionUtil.OnPermissionsListener() { @Override public void onPermissionsOwned() { showTip("This privilege already exists"); } @Override public void onPermissionsForbidden(String[] permissions, int[] grantResults, ArrayList<String> pmList) { showTip("The following permissions are prohibited:"+pmList.toString()); AlertDialog dialog = new AlertDialog.Builder(mContext) .setTitle("warning") .setMessage("Go to Settings and turn it on manually"+pmList.toString()+"Jurisdiction!") .setPositiveButton("Determine", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .create(); dialog.show(); } @Override public void onPermissionsDenied(String[] permissions, int[] grantResults, ArrayList<String> pmList) { showTip("The following permissions were denied authorization:"+pmList.toString()); //Re-request permissions AlertDialog dialog = new AlertDialog.Builder(mContext) .setTitle("Tips") .setMessage("["+pmList.toString()+"]Permissions are necessary to apply permissions, please authorize") .setPositiveButton("Determine", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String[] sList=pmList.toArray(new String[0]); //Re-apply for permissions and apply for multiple groups of permissions by permission name PermissionUtil.requestByPermissionName(mContext,sList, RequestCode3_1,mListener3_1); } }) .create(); dialog.show(); } @Override public void onPermissionsSucceed() { showTip("Permission application succeeded"); } }; public void requestPermission3_1(){ String[] pgArray=new String[]{ Manifest.permission_group.SENSORS,Manifest.permission_group.SMS,Manifest.permission_group.STORAGE }; showTip("Conduct[sensor+Short message+storage]Permission Request..."); //Apply for specified permissions by permission group name PermissionUtil.requestByGroupName(mContext,pgArray, RequestCode3_1,mListener3_1); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode){//It can be handled differently for different privilege request operations or in the same way (requestCode is not judged) case RequestCode3_1:{ PermissionUtil.onRequestPermissionsResult(mContext,permissions,grantResults,mListener3_1,false); break; } } }
Specific tool classes and demonstration projects are placed on Github, which interested students can learn about. If you have any questions, please come to discuss them and make progress together.
GitHub address: https://github.com/JINSHENGCCC/Android_Common/tree/master/AndroidPermission/src