ביקורת של גישה לנתונים תעזור לכם להבין איך האפליקציה והיחסים שלה עם רכיבים אחרים מקבלים גישה לנתונים פרטיים ממשתמשים. התהליך הזה, שזמין במכשירים עם Android מגרסה 11 (רמת API 30) ואילך, מאפשר לזהות טוב יותר גישה לנתונים שעלולה להיות לא צפויה. האפליקציה יכולה לרשום מופע של AppOpsManager.OnOpNotedCallback
, שיכול לבצע פעולות בכל פעם שמתרחש אחד מהאירועים הבאים:
- הקוד של האפליקציה שלכם ניגש לנתונים פרטיים. כדי לעזור לכם לקבוע איזה חלק לוגי באפליקציה הפעיל את האירוע, תוכלו ביקורת גישה לנתונים לפי תג שיוך.
- קוד בספרייה או ב-SDK תלויים ניגש לנתונים פרטיים.
בקרת הרשאות הגישה לנתונים מופעלת בשרשור שבו מתבצעת בקשת הנתונים. כלומר, אם ספרייה או ערכת SDK של צד שלישי באפליקציה שלכם קוראות לממשק API שמקבל גישה לנתונים פרטיים, ביקורת הגישה לנתונים מאפשרת ל-OnOpNotedCallback
לבדוק מידע על הקריאה. בדרך כלל, אובייקט ה-callback הזה יכול לזהות אם הקריאה הגיעה מהאפליקציה או מה-SDK על ידי בדיקת הסטטוס הנוכחי של האפליקציה, כמו מעקב ה-stack של השרשור הנוכחי.
רישום ביומן של גישה לנתונים
כדי לבצע ביקורת על גישה לנתונים באמצעות מכונה של AppOpsManager.OnOpNotedCallback
, צריך להטמיע את לוגיקת הקריאה החוזרת ברכיב שבו רוצים לבדוק את הגישה לנתונים, למשל ב-method onCreate()
של הפעילות או ב-method 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()
אם הגישה לנתונים לא מתרחשת במהלך הקריאה ל-API של האפליקציה. הדוגמה הנפוצה ביותר היא כשהאפליקציה רושמת מאזין, והגישה לנתונים מתרחשת בכל פעם שמפעילים את פונקציית ה-callback של המאזין.הארגומנט
AsyncNotedOp
שמוענק ל-onAsyncNoted()
מכיל שיטה בשםgetMessage()
. השיטה הזו מספקת מידע נוסף על הגישה לנתונים. במקרה של קריאות החזרה (callbacks) למיקום, ההודעה מכילה את גיבוב הזהות של המערכת של המאזין.onSelfNoted()
מופעלת במקרה הנדיר מאוד שבו אפליקציה מעבירה UID משלה ל-noteOp()
.
בדיקת הגישה לנתונים לפי תג שיוך
יכול להיות לאפליקציה שלכם יהיו כמה תרחישי שימוש עיקריים, למשל לאפשר למשתמשים לצלם תמונות ולשתף אותן עם אנשי הקשר שלהם. אם אתם מפתחים אפליקציה למטרות מרובות, תוכלו להחיל תג שיוך על כל חלק באפליקציה בזמן הביקורת על הגישה לנתונים שלה. ההקשר attributionTag
מוחזר באובייקטים שהועברו לקריאות אל onNoted()
.
כך תוכלו לעקוב בקלות רבה יותר אחרי הגישה לנתונים לחלקים לוגיים בקוד.
כדי להגדיר תגי שיוך באפליקציה, מבצעים את השלבים שמפורטים בקטעים הבאים.
הצהרה על תגי שיוך במניפסט
אם האפליקציה שלכם מטרגטת ל-Android 12 (רמת API 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); } }