ביקורת הגישה לנתונים

ביקורת של גישה לנתונים תעזור לכם להבין איך האפליקציה והיחסים שלה עם רכיבים אחרים מקבלים גישה לנתונים פרטיים ממשתמשים. התהליך הזה זמין במכשירים הבאים: מכשירים עם Android 11 (רמת API 30) ואילך, מאפשרים לזהות גישה לנתונים שעלולה להיות לא צפויה. האפליקציה שלך יכולה לרשום מופע מתוך AppOpsManager.OnOpNotedCallback, שיכול לבצע פעולות בכל פעם שמתרחש אחד מהאירועים הבאים:

  • קוד האפליקציה שלך ניגש לנתונים פרטיים. כדי לקבוע איזה חלק לוגי באפליקציה הפעיל את האירוע, אפשר לבדוק את הגישה לנתונים לפי תג שיוך.
  • קוד בספרייה או ב-SDK תלויים ניגש לנתונים פרטיים.

ביקורת על גישה לנתונים מופעלת בשרשור שבו מתבצעת הבקשה לנתונים במקום. כלומר, אם ספרייה או ערכת SDK של צד שלישי באפליקציה שלכם קוראות לממשק API שמקבל גישה לנתונים פרטיים, ביקורת הגישה לנתונים מאפשרת ל-OnOpNotedCallback לבדוק מידע על הקריאה. בדרך כלל, אובייקט ה-callback הזה יכול לזהות אם הקריאה הגיעה מהאפליקציה או מה-SDK על ידי בדיקת הסטטוס הנוכחי של האפליקציה, כמו מעקב ה-stack של השרשור הנוכחי.

רישום ביומן של גישה לנתונים

כדי לבצע ביקורת גישה לנתונים באמצעות מכונה של AppOpsManager.OnOpNotedCallback, מטמיעים את לוגיקת הקריאה החוזרת ברכיב שבהם אתם מתכוונים לבדוק את הגישה לנתונים, למשל במסגרת פעילות onCreate() או onCreate().

קטע הקוד הבא מגדיר AppOpsManager.OnOpNotedCallback עבור ביקורת על הגישה לנתונים במסגרת פעילות אחת:

KotlinJava
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)
}
@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() נשלחת קריאה אם הגישה לנתונים לא מתרחשת במהלך קריאה ל-API. הדוגמה הנפוצה ביותר היא כשהאפליקציה רושמת מאזינים גישה לנתונים מתרחשת בכל פעם שמופעלת קריאה חוזרת (callback) של המאזין.

    הארגומנט AsyncNotedOp שהועבר אל onAsyncNoted() מכיל שנקראת getMessage(). השיטה הזו מספקת מידע נוסף על את הגישה לנתונים. במקרה של קריאות חוזרות לגבי המיקום, ההודעה תכיל את גיבוב הזהות (hash) של המערכת של המאזינים.

  • onSelfNoted() נשלחת קריאה במקרה הנדיר מאוד שבו אפליקציה מעבירה UID שלה אל noteOp()

בדיקת הגישה לנתונים לפי תג שיוך

ייתכן שלאפליקציה יש כמה תרחישים עיקריים לדוגמה, כמו מתן אפשרות למשתמשים לצלם תמונות ולשתף אותן עם אנשי הקשר שלהם. אם אתם מפתחים אפליקציה למטרות מרובות, תוכלו להחיל תג שיוך על כל חלק באפליקציה בזמן הביקורת על הגישה לנתונים שלה. ההקשר של attributionTag מוחזר באובייקטים המועברים לקריאות ל-onNoted(). כך קל יותר לעקוב אחרי הגישה לנתונים בחזרה לחלקים לוגיים בקוד.

כדי להגדיר תגי שיוך באפליקציה, מבצעים את השלבים שמפורטים בקטעים הבאים.

הצהרה על תגי שיוך במניפסט

אם האפליקציה שלך מטרגטת את Android 12 (רמת API 31) ואילך, עליך להצהיר על זכויות יוצרים תגי שיוך (Attribution) בקובץ המניפסט של האפליקציה, בפורמט שמוצג את קטע הקוד הבא. אם תנסו להשתמש בתג שיוך שלא השתמשתם בו להצהרה בקובץ המניפסט של האפליקציה, המערכת יוצרת תג 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>

יצירת תגי שיוך (Attribution)

ב onCreate() השיטה של הפעילות שבה אתם ניגשים לנתונים, למשל הפעילות שבה אתם לבקש את מיקום המשתמש, או לגשת לרשימת אנשי הקשר של המשתמש, להתקשר createAttributionContext(), להעביר את תג השיוך שאותו רוצים לשייך לחלק אפליקציה.

קטע הקוד הבא מדגים איך ליצור תג שיוך של שיתוף מיקום תמונות באפליקציה:

KotlinJava
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.
    }
}
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.
        }
    }
}

הכללת תגי שיוך ביומני הגישה

צריך לעדכן את הקריאה החוזרת (callback) של AppOpsManager.OnOpNotedCallback כך שיומני האפליקציה כוללים את שמות תגי השיוך (Attribution) שהגדרתם.

קטע הקוד הבא מציג את הלוגיקה המעודכנת שמתעדת תגי שיוך:

KotlinJava
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)
    }
}
@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);
    }
}