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

يمكنك الحصول على إحصاءات حول كيفية وصول تطبيقك والتطبيقات المستنِدة إليه إلى البيانات الخاصة من المستخدمين من خلال إجراء التدقيق في الوصول إلى البيانات. تتيح لك هذه العملية، المتوفّرة على الأجهزة التي تعمل بالإصدار 11 من Android (مستوى واجهة برمجة التطبيقات 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(). توفّر هذه الطريقة مزيدًا من المعلومات عن الوصول إلى البيانات. في حالة معاودات الاتصال الخاصة بالموقع الجغرافي، تحتوي الرسالة على نظام-identity-hash الخاص بالمتتبِّع.

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