如何在Activity中獲取調用者 -- getReferrer()

如何在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()一般用在同步調用中,在這幾個情況中並不適用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章