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

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

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 29089
u0_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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值