الوصول إلى البيانات للتدقيق

يمكنك الحصول على إحصاءات عن كيفية وصول تطبيقك ومستلزماته إلى البيانات الخاصة من المستخدمين من خلال إجراء تدقيق في الوصول إلى البيانات. تتوفّر هذه العملية على الأجهزة التي تعمل بالإصدار 11 (المستوى 30 لواجهة برمجة التطبيقات) من نظام Android والإصدارات الأحدث، وتتيح لك معرفة بشكل أفضل حالات الوصول إلى البيانات غير المتوقّعة. يمكن لتطبيقك تسجيل مثيل من AppOpsManager.OnOpNotedCallback، والذي يمكنه تنفيذ إجراءات في كل مرة يحدث فيها أحد الأحداث التالية:

يتمّ تفعيل ميزة "التدقيق في الوصول إلى البيانات" في سلسلة المحادثات التي يتمّ فيها تنفيذ طلب البيانات. وهذا يعني أنّه إذا كانت حزمة SDK أو مكتبة تابعة لجهة خارجية في تطبيقك تستدعي واجهة برمجة تطبيقات تتمكّن من الوصول إلى البيانات الخاصة، تسمح لك ميزة "تدقيق الوصول إلى البيانات" في OnOpNotedCallback بفحص معلومات عن الطلب. يمكن أن يحدِّد عنصر callback عادةً ما إذا كانت المكالمة واردة من تطبيقك أو من حزمة SDK من خلال النظر في الحالة الحالية للتطبيق، مثل تتبُّع تسلسل استدعاء الدوال البرمجية للسلسلة الحالية.

تسجيل الوصول إلى البيانات

لإجراء تدقيق في الوصول إلى البيانات باستخدام مثيل AppOpsManager.OnOpNotedCallback، نفِّذ منطق الاستدعاء في المكوّن الذي تريد تدقيق الوصول إلى البيانات فيه، مثل ضمن onCreate() طريقة النشاط أو onCreate()طريقة التطبيق.

يحدد مقتطف الرمز التالي AppOpsManager.OnOpNotedCallback لتدقيق الوصول إلى البيانات في نشاط واحد:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() {
        private fun logPrivateDataAccess(opCode: String, trace: String) {
            Log.i(MY_APP_TAG, "Private data accessed. " +
                    "Operation: $opCode\nStack Trace:\n$trace")
        }

        override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {
            logPrivateDataAccess(
                    syncNotedAppOp.op, Throwable().stackTrace.toString())
        }

        override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {
            logPrivateDataAccess(
                    syncNotedAppOp.op, Throwable().stackTrace.toString())
        }

        override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
            logPrivateDataAccess(asyncNotedAppOp.op, asyncNotedAppOp.message)
        }
    }

    val appOpsManager =
            getSystemService(AppOpsManager::class.java) as AppOpsManager
    appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback)
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
    AppOpsManager.OnOpNotedCallback appOpsCallback =
            new AppOpsManager.OnOpNotedCallback() {
        private void logPrivateDataAccess(String opCode, String trace) {
            Log.i(MY_APP_TAG, "Private data accessed. " +
                    "Operation: $opCode\nStack Trace:\n$trace");
        }

        @Override
        public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) {
            logPrivateDataAccess(asyncNotedAppOp.getOp(),
                    asyncNotedAppOp.getMessage());
        }
    };

    AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
    if (appOpsManager != null) {
        appOpsManager.setOnOpNotedCallback(getMainExecutor(), appOpsCallback);
    }
}

يتمّ استدعاء الطريقتَين onAsyncNoted() وonSelfNoted() في حالات معيّنة:

  • يتمّ استدعاء onAsyncNoted() في حال عدم الوصول إلى البيانات أثناء معالجة طلب بيانات تطبيقك من واجهة برمجة التطبيقات. وأكثر الأمثلة شيوعًا هو عندما يسجِّل تطبيقك مستمعًا ويتم الوصول إلى البيانات في كل مرة يتم فيها استدعاء طلب الاستدعاء للمستمع.

    تحتوي الوسيطة AsyncNotedOp التي تم تمريرها إلى onAsyncNoted() على طريقة تُسمى getMessage(). تقدّم هذه الطريقة مزيدًا من المعلومات حول إمكانية الوصول إلى البيانات. في حال تلقّي مكالمات استرجع البيانات المتعلّقة بالموقع الجغرافي، تحتوي الرسالة على مفتاح تجزئة نظام الهوية للمستمع.

  • يتمّ استدعاء onSelfNoted() في الحالة النادرة جدًا عندما يُرسِل تطبيق معيّن معرّف مستخدم فريدًا خاصًا به إلى noteOp().

تدقيق الوصول إلى البيانات حسب علامة تحديد المصدر

قد يكون لتطبيقك عدة حالات استخدام أساسية، مثل السماح للمستخدمين بالتقاط الصور ومشاركتها مع جهات اتصالهم. إذا كنت تطوّر تطبيقًا متعدد الأغراض، يمكنك تطبيق علامة تحديد المصدر على كل جزء من تطبيقك عند تدقيق الوصول إلى بياناته. يتمّ إرجاع سياق attributionTag مجددًا في الكائنات التي تمّ تمريرها إلى طلبات onNoted(). يساعدك ذلك في تتبُّع الوصول إلى البيانات بسهولة أكبر إلى الأجزاء المنطقية من الرمز البرمجي.

لتحديد علامات الإسناد في تطبيقك، أكمِل الخطوات الواردة في القسمين التاليين:

إدراج علامات تحديد المصدر في البيان

إذا كان تطبيقك يستهدف الإصدار 12 من Android (المستوى 31 من واجهة برمجة التطبيقات) أو إصدارًا أحدث، عليك الإفصاح عن علامات الإسناد في ملف بيان تطبيقك باستخدام التنسيق الموضَّح في ملف اقتباس الرمز البرمجي التالي. إذا حاولت استخدام علامة تحديد مصدر لم يتم الإفصاح عنها في ملف بيان تطبيقك، سينشئ النظام علامة null نيابةً عنك ويتسجيل رسالة في Logcat.

<manifest ...>
    <!-- The value of "android:tag" must be a literal string, and the
         value of "android:label" must be a resource. The value of
         "android:label" is user-readable. -->
    <attribution android:tag="sharePhotos"
                 android:label="@string/share_photos_attribution_label" />
    ...
</manifest>

إنشاء علامات الإسناد

في طريقة onCreate() في النشاط الذي تصل من خلاله إلى البيانات، مثل النشاط الذي تطلب فيه الموقع الجغرافي أو الوصول إلى قائمة جهات اتصال المستخدِم، أو استدعاء createAttributionContext()، مع تمرير علامة تحديد المصدر التي تريد ربطها بجزء من تطبيقك.

يوضح مقتطف الرمز التالي كيفية إنشاء علامة إحالة لجزء مشاركة موقع صورة من أحد التطبيقات:

Kotlin

class SharePhotoLocationActivity : AppCompatActivity() {
    lateinit var attributionContext: Context

    override fun onCreate(savedInstanceState: Bundle?) {
        attributionContext = createAttributionContext("sharePhotos")
    }

    fun getLocation() {
        val locationManager = attributionContext.getSystemService(
                LocationManager::class.java) as LocationManager
        // Use "locationManager" to access device location information.
    }
}

Java

public class SharePhotoLocationActivity extends AppCompatActivity {
    private Context attributionContext;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState,
            @Nullable PersistableBundle persistentState) {
        attributionContext = createAttributionContext("sharePhotos");
    }

    public void getLocation() {
        LocationManager locationManager =
                attributionContext.getSystemService(LocationManager.class);
        if (locationManager != null) {
            // Use "locationManager" to access device location information.
        }
    }
}

تضمين علامات الإسناد في سجلّات الوصول

عدِّل رد استدعاء "AppOpsManager.OnOpNotedCallback" بحيث تتضمّن سجلّات تطبيقك أسماء علامات تحديد المصدر التي حدّدتها.

يعرض مقتطف الرمز التالي المنطق المعدَّل الذي يسجّل علامات الإسناد:

Kotlin

val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() {
    private fun logPrivateDataAccess(
            opCode: String, attributionTag: String, trace: String) {
        Log.i(MY_APP_TAG, "Private data accessed. " +
                    "Operation: $opCode\n " +
                    "Attribution Tag:$attributionTag\nStack Trace:\n$trace")
    }

    override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {
        logPrivateDataAccess(syncNotedAppOp.op,
                syncNotedAppOp.attributionTag,
                Throwable().stackTrace.toString())
    }

    override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {
        logPrivateDataAccess(syncNotedAppOp.op,
                syncNotedAppOp.attributionTag,
                Throwable().stackTrace.toString())
    }

    override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
        logPrivateDataAccess(asyncNotedAppOp.op,
                asyncNotedAppOp.attributionTag,
                asyncNotedAppOp.message)
    }
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
    AppOpsManager.OnOpNotedCallback appOpsCallback =
            new AppOpsManager.OnOpNotedCallback() {
        private void logPrivateDataAccess(String opCode,
                String attributionTag, String trace) {
            Log.i("MY_APP_TAG", "Private data accessed. " +
                    "Operation: $opCode\n " +
                    "Attribution Tag:$attributionTag\nStack Trace:\n$trace");
        }

        @Override
        public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    syncNotedAppOp.getAttributionTag(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
            logPrivateDataAccess(syncNotedAppOp.getOp(),
                    syncNotedAppOp.getAttributionTag(),
                    Arrays.toString(new Throwable().getStackTrace()));
        }

        @Override
        public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) {
            logPrivateDataAccess(asyncNotedAppOp.getOp(),
                    asyncNotedAppOp.getAttributionTag(),
                    asyncNotedAppOp.getMessage());
        }
    };

    AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
    if (appOpsManager != null) {
        appOpsManager.setNotedAppOpsCollector(appOpsCollector);
    }
}