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

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

يتم استدعاء التدقيق في الوصول إلى المعلومات في سلسلة المحادثات التي يتم فيها طلب البيانات. هذا يعني أنّه إذا طلبت حزمة SDK أو مكتبة تابعة لجهة خارجية في تطبيقك واجهة برمجة تطبيقات تصل إلى البيانات الخاصة، فإنّ ميزة "التدقيق في الوصول إلى المعلومات" تتيح لك OnOpNotedCallback فحص المعلومات المتعلقة بالطلب. عادةً ما يمكن لكائن رد الاتصال هذا معرفة ما إذا كان الاستدعاء صادرًا من تطبيقك أو من حزمة تطوير البرامج (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() في حالات نادرة جدًا عندما يمرّر تطبيق معرّف UID الخاص به إلى 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.setOnOpNotedCallback(getMainExecutor(), appOpsCallback);
    }
}