0. 为什么要在Activity中获取调用者?
主要是安全和定制两个方面的需求吧。
-
安全需求:
一般Activity如果已经对外开放了(即exported为true,或者加了Intent-filter),那么对Activity的保护就会降低,形成Activity攻击面,引入了风险。如果只对某几个app开放其Activity,则可以获取调用者的信息,并进行控制。 -
定制需求:
如果被启动的Activity想对特定的调用者进行定制操作,则需要知道调用者是谁。
1. 在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
值(目前来看是可靠的)。
2. 测试代码:验证上面的各种方法
2.1 场景和测试代码
场景:
有2个app,一个包名为com.galian.mainapp
,app名为MainApp
;另一个包名为com.galian.secondapp
,app名为SecondApp
。
SecondApp启动MainApp的MainActivity,在MainApp的MainActivity中检查调用者是谁。
需要说明的是,本文中有referrer
和mReferrer
两种说法,这两种是有区别的。
其中referrer
是指Activity的getReferrer()
的返回值。
mReferrer
是Activity类中的成员变量。
getReferrer()
的代码后面会给出。
-
被调用者:MainApp
MainApp的界面:
其实这是从Launcher app启动MainApp的情况,可以看到Launcher的包名(com.sec.android.app.launcher)。
// 测试各种方法,并显示
private void checkCallingApp() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("\n\n");
// Binder.getCallingUid(), Binder.getCallingPid()
stringBuffer.append("1. caller uid = ").append(Binder.getCallingUid()).append(", pid = ")
.append(Binder.getCallingPid()).append("\n\n");
// getReferrer(), Activity自带的获取referrer的方法,Api Level22之后才可以用
Uri referrer = getReferrer();
stringBuffer.append("2. caller (referrer uri) : ");
if (referrer != null) {
stringBuffer.append(referrer.toString());
} else {
stringBuffer.append("null");
}
stringBuffer.append("\n\n");
// reflectGetReferrer()反射的方式获取mReferrer
String referrerStr = reflectGetReferrer();
stringBuffer.append("3. caller (reflect mReferrer) : ").append(referrerStr).append("\n\n");
// Activity的getCallingPackage()
String callingPkg = getCallingPackage();
stringBuffer.append("4. callingPkg: ").append(callingPkg).append("\n\n");
// Activity的getCallingActivity()
ComponentName componentName = getCallingActivity();
stringBuffer.append("5. caller componentName: ");
if (componentName != null) {
stringBuffer.append(componentName.toString());
} else {
stringBuffer.append("null");
}
mTextView.setText(getString(R.string.caller_name, stringBuffer.toString()));
}
反射的方式获取mReferrer
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
-
调用者:SecondApp
SecondApp的界面:
SecondApp的代码在各种情况中给出。
2.2 情况1: 调用者没有修改referrer
SecondApp启动MainApp的MainActivity
:
// 测试getReferrer(): 没有修改referrer的情况
Intent intent = new Intent();
intent.setClassName("com.galian.mainapp", "com.galian.mainapp.MainActivity");
startActivity(intent);
结果分析:
- uid为10189,pid为29089,这是MainApp自身。
- 1
- 2
- 1
- 2
- getReferrer()的返回值为
android-app://com.galian.secondapp
,此值可以得到调用者的包名。 - 反射方式获取到的
mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。 - getCallingPackage()和getCallingActivity()都返回null
2.3 情况2:调用者通过Intent.EXTRA_REFERRER
修改referrer的值
SecondApp启动MainApp的MainActivity
:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
结果分析:
- uid为10189,pid为29089,这是MainApp自身。
- getReferrer()的返回值为
android-app://com.test.app
,此值不是真正的调用者的包名。 - 反射方式获取到的
mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。 - getCallingPackage()和getCallingActivity()都返回null
2.4 情况3:调用者通过Intent.EXTRA_REFERRER_NAME
修改referrer的值
SecondApp启动MainApp的MainActivity
:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
结果分析:
- uid为10189,pid为29089,这是MainApp自身。
- getReferrer()的返回值为
android-app://com.example.app
,此值不是真正的调用者的包名。 - 反射方式获取到的
mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。 - getCallingPackage()和getCallingActivity()都返回null
2.5 情况4:测试getCallingPackage()和getCallingActivity()
同时调用者通过Intent.EXTRA_REFERRER
修改referrer的值。
SecondApp通过startActivityForResult()
启动MainApp的MainActivity
:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
结果分析:
- uid为10189,pid为29089,这是MainApp自身。
- getReferrer()的返回值为
android-app://com.test.app
,此值不是真正的调用者的包名。 - 反射方式获取到的
mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。 - getCallingPackage()返回
com.galian.secondapp
,getCallingActivity()返回ComponentInfo{com.galian.secondapp/com.galian.secondapp.SecondMainActivity}
。可以得到调用者的包名。
3. 结论
从上面的测试结果可以得到下面的结论:
-
通过反射的方式(
reflectGetReferrer()
)获取到的mReferrer
,是调用者的包名,目前来看是可靠的,但是需要在Android5.1(Api level 22)以及之后才能用。 -
getCallingPackage()
和getCallingActivity()
只有在startActivityForResult()
的时候才可以得到调用者的包名。 -
Activity的
getReferrer()
是不可靠的,因为调用者可以自己设置referrer的值。所以不能依赖此值来判断调用者。 -
Binder.getCallingUid()
和Binder.getCallingPid()
一般用在同步调用中,在这几个情况中并不适用。
4. 关于mReferrer
的细节
4.1 Activity的getReferrer()
需要注意的是,此方法是在Android 5.1 (Api level 22)中引入的,Android 5.1之前是不能使用的。
Intent.Java
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
Activity.java
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
4.2 反射的方式获取Activity的mReferrer
需要注意的是,此方法是基于getReferrer()
(mReferrer
)的,所以也必须在Android 5.1 (Api level 22)及 5.1 之后才能用。
自定义方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
**4.3 Activity中的mReferrer
是如何传递过来的?
由于内容较多,请参考另一篇博文《关于Activity的getReferrer()
之二:调用者的包名是如何传给mReferrer
的,兼谈startActivity的详细流程》
转自:http://blog.csdn.net/u013553529/article/details/53856800