如何在Activity中获取调用者 -- getReferrer()
前言
为什么要在Activity中获取调用者?
主要是安全和定制两个方面的需求吧。
安全需求:
一般Activity如果已经对外开放了(即exported为true,或者加了Intent-filter),那么对Activity的保护就会降低,形成Activity攻击面,引入了风险。如果只对某几个app开放其Activity,则可以获取调用者的信息,并进行控制。定制需求:
如果被启动的Activity想对特定的调用者进行定制操作,则需要知道调用者是谁。
在Activity中获取调用者的方法
注:先列出所有可能的方法,后面会给出哪些方法可行。
Binder.getCallingUid()
和Binder.getCallingPid()
,然后根据uid,pid查找到包名Activity的
getCallingPackage()
和getCallingActivity()
Activity的
getReferrer()
【注意:Android 5.1(Api level 22)中才引入的】反射的方式获取Activity的
mReferrer: reflectGetReferrer()
【注:自定义函数,目的是获取到android.app.Activity
类的mReferrer
的值,也需要Api level 22(含)之后才能使用】
这里先给出结论:
方法1:不能在调用者
startActivity()
的时候获取到调用者的包名,只能用于Activity用到的Binder同步调用的地方。方法2: 在特定情况下可以使用
getCallingPackage()
和getCallingActivity()
,即如果Activity是通过startActivityForResult
启动的,则可以使用。方法3: Activity的
getReferrer()
是不可靠的,因为调用者可以自己设置referrer的值。方法4:是对方法3的改进,消除
getReferrer()
可能返回的不可靠的值,直接获取可靠的mReferrer
值(目前来看是可靠的)。
关于mReferrer的细节
Activity的getReferrer()
需要注意的是,此方法是在Android 5.1 (Api level 22)中引入的,Android 5.1之前是不能使用的。
Intent.java
public static final String EXTRA_REFERRER
= "android.intent.extra.REFERRER";
public static final String EXTRA_REFERRER_NAME
= "android.intent.extra.REFERRER_NAME";
Activity.java
public Uri getReferrer() {
Intent intent = getIntent();
// 优先从Intent的Intent.EXTRA_REFERRER数据获取Uri,作为referrer
Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
if (referrer != null) {
return referrer;
}
// 如果之前没有获取到,则从intent的Intent.EXTRA_REFERRER_NAME数据获取,并转换成Uri
String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
if (referrerName != null) {
return Uri.parse(referrerName);
}
// 如果上面都没有获取到,则将mReferrer转换成android-app://的形式
if (mReferrer != null) {
return new Uri.Builder().scheme("android-app").authority(mReferrer).build();
}
return null;// 都没获取到的话,返回null
}
反射的方式获取Activity的mReferrer
需要注意的是,此方法是基于getReferrer()(mReferrer)的,所以也必须在Android 5.1 (Api level 22)及 5.1 之后才能用。
自定义方法:
private String reflectGetReferrer() {
try {
Class activityClass = Class.forName("android.app.Activity");
Field refererField = activityClass.getDeclaredField("mReferrer");
refererField.setAccessible(true);
String referrer = (String) refererField.get(MainActivity.this);
return referrer;
} catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
return "No referrer";
}
}
总结
通过反射的方式
reflectGetReferrer(
)获取到的mReferrer
,是调用者的包名,目前来看是可靠的,但是需要在Android5.1(Api level 22)以及之后才能用。getCallingPackage()
和getCallingActivity()
只有在startActivityForResult()
的时候才可以得到调用者的包名。Activity的getReferrer()
是不可靠的,因为调用者可以自己设置referrer
的值。所以不能依赖此值来判断调用者。Binder.getCallingUid()
和Binder.getCallingPid()
一般用在同步调用中,在这几个情况中并不适用。