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