Android应用开发Permission理解

来源:互联网 发布:淘宝延长收货最长几天 编辑:程序博客网 时间:2024/06/03 11:33

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=10192requires 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
原创粉丝点击