您可以透過執行資料存取稽核功能,深入瞭解應用程式及其依附元件如何存取使用者的私人資料。這項程序適用於搭載 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()
方法:
如果資料存取並未發生在應用程式 API 呼叫期間,系統就會呼叫
onAsyncNoted()
。最常見的例子是,當您的應用程式註冊事件監聽器後,每次叫用事件監聽器的回呼時都會發生資料存取。傳入
onAsyncNoted()
的AsyncNotedOp
引數包含一個名為getMessage()
的方法。這個方法提供更多資料存取的相關資訊。如果是位置資訊回呼,訊息中包含事件監聽器的系統身分雜湊。在極少數情況下,當應用程式將其專屬的 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" 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); } }