您可透過執行資料存取稽核功能,深入瞭解您的應用程式及其依附元件如何存取使用者的私人資料。這項程序適用於搭載 Android 11 (API 級別 30) 以上版本的裝置,讓您能夠更有效地識別潛在未預期的資料存取。您的應用程式可以註冊 AppOpsManager.OnOpNotedCallback
的執行個體,只要發生下列其中一個事件,系統就會執行動作:
- 應用程式的程式碼存取私人資料。為協助您判斷應用程式叫用事件的邏輯部分,您可以 依歸因標記稽核資料存取。
- 獨立程式庫或 SDK 中的程式碼存取私人資料。
在資料要求發生的執行緒上叫用資料存取稽核。也就是說,只要應用程式中的第三方 SDK 或程式庫呼叫存取私人資料的 API,資料存取稽核功能即可讓您的 OnOpNotedCallback
檢查呼叫的相關資訊。通常,這個回呼物件會查看應用程式的目前狀態,例如目前執行緒的堆疊追蹤,藉此判斷呼叫是來自您的應用程式還是 SDK。
記錄資料存取
如果要使用 AppOpsManager.OnOpNotedCallback
的執行個體執行資料存取稽核,請在您打算稽核資料存取的元件中導入呼叫邏輯,例如,在某個活動的 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()
方法:
如果生資料存取並未發生在應用程式 API 呼叫期間,系統就會呼叫
onAsyncNoted()
。最常見的例子是,當您的應用程式註冊事件監聽器後,每次叫用事件監聽器的回呼時都會發生資料存取。傳入
onAsyncNoted()
的AsyncNotedOp
引數包含一個名為getMessage()
的方法。這個方法提供更多資料存取的相關資訊。如果是位置資訊回呼,訊息中包含事件監聽器的 system-identity-hash。在極少數情況下,當應用程式將其專屬的 UID 傳入
noteOp()
後,系統會呼叫onSelfNoted()
。
按歸因標記稽核資料存取
您的應用程式可能有幾個主要用途,例如允許使用者拍攝相片並與聯絡人分享。如果您開發的是這種多用途應用程式,即可在稽核應用程式的資料存取時,將「歸因標記」 套用至應用程式的各個部分。傳遞至 onNoted()
回呼的物件會傳回 attributionTag
結構定義。如此一來,您就能更輕鬆地將資料存取追溯至程式碼的邏輯部分。
如果要在應用程式中定義歸因標記,請完成以下各節的步驟。
在資訊清單中宣告歸因標記
如果您的應用程式鎖定 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" should be 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); } }