Android应用开发Permission理解

Android的permission机制想必大家并不陌生。在开始主要内容之前,我们来简单回顾一下吧。

1.Android为每个应用程序分配一个唯一的UID,以确保每个应用程序有独立的空间,使它与其它应用程序分隔开来,避免其他应用程序进行非授权的访问。

2.Android可以为其几大组件定义permission,包括Activity, Service, ContentProvider, BroadCast Intent.

3.在manifest中定义或声明使用permission.

下面给一些例子,便于理解和使用上面说的几点:

1.声明自定义permission

<permission android:name=“com.example.perm.READ_INCOMING_MSG”  
    android:label=“Read incoming messages from the EX service.”  
    android:description=“Allows the app to access any messages received by  
        the EX service. Any app granted this permission will be able to read all  
        messages processed by the ex1 application.”  
    android.protectionLevel=“dangerous”  
    android:permissionGroup=“android.permission-group.PERSONAL_INFO”  
/>

2.使用权限

    <manifest xmlns:android=“http://schemas.android.com/apk/res/android”  
        package=“com.example.testapps.test1”>  
        ...  
        <uses-permission android:name=“android.permission.INTERNET” />  
        ...  
    </manifest>

3.为Activity添加permission

    <activity android:name=“.Activity1”  
        android.permission=“com.example.perm.START_ACTIVITY”>  
        <intent-filter>  
        ...  
        </intent-filter>  
    </activity>

添加后,只有声明使用 com.example.perm.START_ACTIVITY的应用才能启动此Activity.


4.为Service添加permission,类似于Activity。

 <service android:name=“.MailListenerService”  
        android:permission=“com.example.perm.BIND_TO_MSG_LISTENER”  
        android:enabled=“true”  
        android:exported=“true”  
        <intent-filter></intent-filter>  
    </service>  

5.为ContentProvider添加permission,这个应用的最为广泛。

a.在provider中添加权限。我们添加了读权限和写权限。

<provider android:name=“com.example.test.app1.MailProvider”  
    android:authorities=“com.example.test.app1.mailprovider”  
    android:readPermission=“com.example.perm.DB_READ”  
    android:writePermission=“com.example.perm.DB_WRITE”>  
</provider> 

b.有时候我们需要主动grant权限给其他应用

    <provider android:name=“com.example.test.app1.MailProvider”  
        android:authorities=“com.example.test.app1.mailprovider”  
        android:readPermission=“com.example.perm.DB_READ”  
        android:writePermission=“com.example.perm.DB_WRITE”  
        android:grantUriPermission=“true”>  
    </provider>

上面我们主动grant了整个provider的权限给其他应用。我们还可以只grant其中的某个子路径给其他应用

<provider android:name=“com.example.test.app1.MailProvider”  
    android:authorities=“com.example.test.app1.mailprovider”  
    android:readPermission=“com.example.perm.DB_READ”  
    android:writePermission=“com.example.perm.DB_WRITE”>  
    <grant-uri-permission android:path=“/attachments/”>  
</provider>

c.在程序中动态的grant URI权限给Action接收者。

    uri = “content://com.example.test.provider1/attachments/42”;  
    Intent intent = new Intent(Intent.ACTION_VIEW);  
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);  
    intent.setDataAndType(uri, “image/gif”);  
    startActivity(intent);

d.在程序中动态的将权限grant给某一指定应用

uri = “content://com.example.test.provider1/attachments/42”;  
Context.grantUriPermission(“com.example.test.app2”, uri,intent.FLAG_GRANT_READ_URI_PERMISSION);

6.为Broadcast添加permisson。

a.在发送Broadcast时指定permission

Intent bcastIntent = new Intent(MESSAGE_RECEIVED);  
context.sendBroadcast(bcastIntent, “com.example.perm.MSG_NOTIFY_RCV”);

b.在注册receiver时指定permission

    IntentFilter intentFilter = new IntentFilter(MESSAGE_RECEIVED);   
    UIMailBroadcastReceiver rcv = new UIMailBroadcastReceiver();   
    context.registerReceiver(rcv, intentFilter,“com.example.perm.MSG_NOTIFY_SEND”, null);  

7.在程序中动态检查是否拥有某一权限(不鼓励使用)

    int canProcess = checkCallingOrSelfPermission(“com.example.perm.READ_INCOMING_MSG”);   
    if (canProcess != PERMISSION_GRANTED) throw new SecurityException();  

在我们使用权限的过程中,经常会碰到android:protectionLevel这个域。它称为permission level:


Android permissions fall into four levels:

Normal – These permissions cannot impart real harm to the user (e.g. change the wallpaper) and, while apps need to requestthem, they are automatically granted.

Dangerous – These can impart real harm (e.g. call numbers,open Internet connections, etc) and apps need to request them with user confirmation.

Signature – These are automatically granted to a requesting app. if that app is signed by the same certificate (so, developed by thesame entity) as that which declared/created the permission. This level is designed to allow apps that are part of a suite, or otherwise related, to share data.

Signature/System – Same as Signature, except that the system image gets the permissions automatically as well. This is designed for use by device manufacturers only.



当某一系统的provider权限被声明为signature时,我们第三方的 应用程序是无法访问到的,但如果它grant了其中的某些子路径,那么这些子路径就可以访问。

在JellyBean中,我们看到DownloadProvider的ALL_ACCESS_DOWNLOADS权限被声明为signature保护方式,但幸运的是all_download这个子路径是被grant出来的。

所以第三方的 应用程序是可以接收share intent来访问它的。那么下面就到了我们要重点讨论的问题:grant URI的有效期限问题。

Grant URI的有效期限

正如上面我们描述的情形,当我们拿到了DownloadProvider给我们的权限时(前提是我们注册了接收ACTION_SEND的activity),在我们的Activity创建时可以接收到

这个URI来进行一系列操作。下面给出一段测试代码:

    HelloActivity.java  
      
    package com.xm.permission;  
      
    import android.app.Activity;  
    import android.content.Intent;  
    import android.net.Uri;  
    import android.os.Bundle;  
      
    import java.io.File;  
    import java.io.FileNotFoundException;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.io.InputStream;  
      
    public class HelloActivity extends Activity {  
      
        private Uri mUri;  
        /** Called when the activity is first created. */  
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
      
            Intent intent = getIntent();  
            if (intent.getAction() == Intent.ACTION_SEND) {  
                mUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);  
            }  
      
            if (mUri == null) {  
                return;  
            }  
      
            InputStream inputStream;  
            try {  
                inputStream = getContentResolver().openInputStream(mUri);  
      
            FileOutputStream fileOutputStream = new FileOutputStream(new File("/sdcard/test.bin"));  
            int length;  
            byte[] buffer = new byte[1440];  
            while ((length = inputStream.read(buffer)) != -1) {  
               fileOutputStream.write(buffer, 0, length);  
            }  
            fileOutputStream.flush();  
            fileOutputStream.close();  
            inputStream.close();  
            } catch (FileNotFoundException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    }  

AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>  
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
        package="com.xm.permission"  
        android:versionCode="1"  
        android:versionName="1.0" >  
      
        <uses-sdk android:minSdkVersion="15" />  
        <uses-permission  
            android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
        <application  
            android:icon="@drawable/ic_launcher"  
            android:label="@string/app_name" >  
            <activity  
                android:label="@string/app_name"  
                android:name=".HelloActivity" >  
                <intent-filter >  
                    <action android:name="android.intent.action.MAIN" />  
      
                    <category android:name="android.intent.category.LAUNCHER" />  
                </intent-filter>  
                <intent-filter android:label="@string/app_name" >  
                    <action android:name="android.intent.action.SEND" />  
      
                    <data android:mimeType="*/*" />  
      
                    <category android:name="android.intent.category.DEFAULT" />  
                </intent-filter>  
            </activity>  
        </application>  
    </manifest>  

运行上面的code,我们可以成功的写一个文件到SDCard上。也许这简单的不能再简单了,但我们的问题来了,看下面:

我们添加了另外一个activity:

    package com.xm.permission;  
      
    import android.app.Activity;  
    import android.content.Intent;  
    import android.net.Uri;  
    import android.os.Bundle;  
      
    import java.io.File;  
    import java.io.FileNotFoundException;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.io.InputStream;  
      
    public class Hello2 extends Activity {  
      
        private Uri mUri;  
      
        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
      
            Intent intent = getIntent();  
            mUri = intent.getData();  
            if (mUri == null) {  
                return;  
            }  
      
            InputStream inputStream;  
            try {  
                inputStream = getContentResolver().openInputStream(mUri);  
      
            FileOutputStream fileOutputStream = new FileOutputStream(new File("/sdcard/test23.bin"));  
            int length;  
            byte[] buffer = new byte[1440];  
            while ((length = inputStream.read(buffer)) != -1) {  
               fileOutputStream.write(buffer, 0, length);  
            }  
            fileOutputStream.flush();  
            fileOutputStream.close();  
            inputStream.close();  
            } catch (FileNotFoundException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    }  

同时在AndroidManifest.xml中添加如下一句:

  1. <activity android:name=".Hello2"></activity> 

并在HelloActivity.java中的catch语句后加上如下代码:

    Intent intent2 = new Intent(HelloActivity.this, Hello2.class);  
    intent2.setData(mUri);  
    startActivity(intent2);  
    finish();  

再次运行,结果会怎么样?

答案是:可能抛出下面的异常:
E/AndroidRuntime(14582): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.xm.permission/com.xm.permission.Hello2}:
java.lang.SecurityException: Permission Denial: reading com.android.providers.downloads.DownloadProvider uri content://downloads/all_downloads/1 from pid=14582, uid=10192
requires android.permission.ACCESS_ALL_DOWNLOADS, or grantUriPermission()

明明之前有权限,怎么突然没有了呢?

这是因为我们添加了下面的code:

    finish();// HelloActivity.java  

当我们调用finish()方法结束HelloActivity后,权限也随之消失。如果我们添加如下code在HelloActivity.java中,就不会抛出异常:

Intent intent2 = new Intent(HelloActivity.this, Hello2.class);  
intent2.setData(mUri);  
startActivity(intent2);

也就是说,当调用finish()以后,最初负责接收permission的Activity被释放,permission也一并被回收了。

所以我们要尽量在接收URI的activity中处理完所有操作。这里如果我们想存储这个URI到Database以备后用的话,也会出现权限的问题。所以,如果

这个资源需要在三方的程序中访问,最好是将它拷贝过来到SDCard,但要注意会有暴露用户隐私的风险。如果都是小文件,可以考虑拷贝到 应用程序目录。

总结:当protectionlevel设置为signature时,动态Grant给第三方应用的permission不是一成不变的,它也有有效期限

转载自http://www.verydemo.com/demo_c89_i24835.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值