主要是安全和定制两个方面的需求吧。
安全需求:
一般Activity如果已经对外开放了(即exported为true,或者加了Intent-filter),那么对Activity的保护就会降低,形成Activity攻击面,引入了风险。如果只对某几个app开放其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
值(目前来看是可靠的)。
场景:
有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的全部代码
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
// 反射的方式获取mReferrer
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;
}
}
调用者:SecondApp
SecondApp的全部代码
SecondApp的界面:
SecondApp的代码在各种情况中给出。
SecondApp启动MainApp的MainActivity
:
// 测试getReferrer(): 没有修改referrer的情况
Intent intent = new Intent();
intent.setClassName("com.galian.mainapp", "com.galian.mainapp.MainActivity");
startActivity(intent);
结果分析:
shell@c7ltechn:/ $ ps | grep 29089
u0_a189 29089 2366 1414876 66788 SyS_epoll_ 0000000000 S com.galian.mainapp
android-app://com.galian.secondapp
,此值可以得到调用者的包名。mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。Intent.EXTRA_REFERRER
修改referrer的值 SecondApp启动MainApp的MainActivity
:
// 测试`getReferrer()`:修改referrer,通过Intent.EXTRA_REFERRER
Intent intent = new Intent();
intent.setClassName("com.galian.mainapp", "com.galian.mainapp.MainActivity");
// case 1
intent.putExtra(Intent.EXTRA_REFERRER, Uri.parse("android-app://com.test.app"));
startActivity(intent);
结果分析:
android-app://com.test.app
,此值不是真正的调用者的包名。mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。Intent.EXTRA_REFERRER_NAME
修改referrer的值 SecondApp启动MainApp的MainActivity
:
// 测试`getReferrer()`:修改referrer,通过Intent.EXTRA_REFERRER_NAME
Intent intent = new Intent();
intent.setClassName("com.galian.mainapp", "com.galian.mainapp.MainActivity");
// case 2
intent.putExtra(Intent.EXTRA_REFERRER_NAME, "android-app://com.example.app");
startActivity(intent);
结果分析:
android-app://com.example.app
,此值不是真正的调用者的包名。mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。 同时调用者通过Intent.EXTRA_REFERRER
修改referrer的值。
SecondApp通过startActivityForResult()
启动MainApp的MainActivity
:
// 测试getCallingPackage() 和 getCallingActivity()
Intent intent = new Intent();
intent.setClassName("com.galian.mainapp", "com.galian.mainapp.MainActivity");
// case 1
intent.putExtra(Intent.EXTRA_REFERRER, Uri.parse("android-app://com.test.app"));
intent.putExtra("GET_DATA", true);
startActivityForResult(intent, REQUEST_CODE_TEST);
结果分析:
android-app://com.test.app
,此值不是真正的调用者的包名。mReferrer
为:com.galian.secondapp
,此值可以得到调用者的包名。com.galian.secondapp
,getCallingActivity()返回ComponentInfo{com.galian.secondapp/com.galian.secondapp.SecondMainActivity}
。可以得到调用者的包名。从上面的测试结果可以得到下面的结论:
通过反射的方式(reflectGetReferrer()
)获取到的mReferrer
,是调用者的包名,目前来看是可靠的,但是需要在Android5.1(Api level 22)以及之后才能用。
getCallingPackage()
和 getCallingActivity()
只有在startActivityForResult()
的时候才可以得到调用者的包名。
Activity的getReferrer()
是不可靠的,因为调用者可以自己设置referrer的值。所以不能依赖此值来判断调用者。
Binder.getCallingUid()
和 Binder.getCallingPid()
一般用在同步调用中,在这几个情况中并不适用。
mReferrer
的细节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
}
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";
}
}
mReferrer
是如何传递过来的? 由于内容较多,请参考另一篇博文《关于Activity的getReferrer()
之二:调用者的包名是如何传给mReferrer
的,兼谈startActivity的详细流程》
转自:http://blog.csdn.net/u013553529/article/details/53856800