关于Activity的getReferrer()之一:如何在Activity中获取调用者

来源:互联网 发布:702qq.com域名更改为 编辑:程序博客网 时间:2024/06/05 09:00

0. 为什么要在Activity中获取调用者?

主要是安全定制两个方面的需求吧。

  • 安全需求: 
    一般Activity如果已经对外开放了(即exported为true,或者加了Intent-filter),那么对Activity的保护就会降低,形成Activity攻击面,引入了风险。如果只对某几个app开放其Activity,则可以获取调用者的信息,并进行控制。

  • 定制需求: 
    如果被启动的Activity想对特定的调用者进行定制操作,则需要知道调用者是谁。

1. 在Activity中获取调用者的方法

注:先列出所有可能的方法,后面会给出哪些方法可行。

  1. Binder.getCallingUid() 和 Binder.getCallingPid(),然后根据uid,pid查找到包名

  2. Activity的getCallingPackage() 和 getCallingActivity()

  3. Activity的getReferrer() 【注意:Android 5.1(Api level 22)中才引入的】

  4. 反射的方式获取Activity的mReferrerreflectGetReferrer()【注:自定义函数,目的是获取到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中检查调用者是谁。

需要说明的是,本文中有referrermReferrer两种说法,这两种是有区别的。 
其中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;        }    }
  • 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的界面:

    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自身。
shell@c7ltechn:/ $ ps | grep 29089u0_a189   29089 2366  1414876 66788 SyS_epoll_ 0000000000 S com.galian.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

    // 测试`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);
  • 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

    // 测试`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);
  • 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

    // 测试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);
  • 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. 结论

从上面的测试结果可以得到下面的结论:

  1. 通过反射的方式(reflectGetReferrer())获取到的mReferrer,是调用者的包名,目前来看是可靠的,但是需要在Android5.1(Api level 22)以及之后才能用。

  2. getCallingPackage() 和 getCallingActivity()只有在startActivityForResult()的时候才可以得到调用者的包名。

  3. Activity的getReferrer()是不可靠的,因为调用者可以自己设置referrer的值。所以不能依赖此值来判断调用者。

  4. Binder.getCallingUid() 和 Binder.getCallingPid()一般用在同步调用中,在这几个情况中并不适用。


4. 关于mReferrer的细节

4.1 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";
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

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    }
  • 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 之后才能用。

自定义方法:

    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";        }    }
  • 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















阅读全文
0 0
原创粉丝点击