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