Puoi ottenere informazioni su come la tua app e le relative dipendenze accedono ai dati privati
degli utenti eseguendo il controllo dell'accesso ai dati. Questa procedura, disponibile sui
dispositivi con Android 11 (livello API 30) e versioni successive, ti consente di identificare meglio
l'accesso ai dati potenzialmente imprevisto. La tua app può registrare un'istanza
di AppOpsManager.OnOpNotedCallback
, che può eseguire
azioni ogni volta che si verifica uno dei seguenti eventi:
- Il codice della tua app accede a dati privati. Per aiutarti a determinare quale parte logica della tua app ha richiamato l'evento, puoi controllare l'accesso ai dati in base al tag di attribuzione.
- Il codice in una libreria o in un SDK dipendente accede a dati privati.
Il controllo dell'accesso ai dati viene richiamato sul thread in cui viene eseguita la richiesta di dati. Ciò significa che se un SDK o una libreria di terze parti nella tua app chiama un'API
che accede a dati privati, il controllo dell'accesso ai dati consente al tuo
OnOpNotedCallback
di esaminare le informazioni sulla chiamata. In genere, questo
oggetto callback può indicare se la chiamata proviene dalla tua app o dall'SDK
esaminando lo stato attuale dell'app, ad esempio lo stack trace del thread corrente.
Registrare l'accesso ai dati
Per eseguire il controllo dell'accesso ai dati utilizzando un'istanza di
AppOpsManager.OnOpNotedCallback
, implementa la logica di callback nel componente
in cui intendi controllare l'accesso ai dati, ad esempio all'interno del metodo
onCreate()
di un'attività o del metodo
onCreate()
di un'applicazione.
Il seguente snippet di codice definisce un AppOpsManager.OnOpNotedCallback
per
controllare l'accesso ai dati all'interno di una singola attività:
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); } }
I metodi onAsyncNoted()
e onSelfNoted()
vengono chiamati in situazioni specifiche:
onAsyncNoted()
viene chiamato se l'accesso ai dati non avviene durante la chiamata API dell'app. L'esempio più comune è quando la tua app registra un listener e l'accesso ai dati avviene ogni volta che viene richiamato il callback del listener.L'argomento
AsyncNotedOp
passato aonAsyncNoted()
contiene un metodo chiamatogetMessage()
. Questo metodo fornisce maggiori informazioni sull'accesso ai dati. Nel caso dei callback di posizione, il messaggio contiene l'hash dell'identità di sistema del listener.onSelfNoted()
viene chiamato nel rarissimo caso in cui un'app trasmette il proprio UID anoteOp()
.
Controllare l'accesso ai dati in base al tag di attribuzione
La tua app potrebbe avere diversi casi d'uso principali, ad esempio consentire agli utenti di scattare
foto e condividerle con i propri contatti. Se sviluppi un'app multiuso, puoi applicare un tag di attribuzione a ogni parte dell'app quando esegui l'audit del relativo accesso ai dati. Il contesto attributionTag
viene restituito
negli oggetti passati alle chiamate a onNoted()
.
In questo modo, puoi risalire più facilmente all'accesso ai dati fino alle parti logiche del tuo codice.
Per definire i tag di attribuzione nella tua app, completa i passaggi nelle sezioni seguenti.
Dichiarare i tag di attribuzione nel manifest
Se la tua app ha come target Android 12 (livello API 31) o versioni successive, devi dichiarare
i tag di attribuzione nel file manifest dell'app, utilizzando il formato mostrato nello
snippet di codice seguente. Se tenti di utilizzare un tag di attribuzione che non
dichiari nel file manifest della tua app, il sistema crea un tag null
per te e
registra un messaggio in 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>
Crea tag di attribuzione
Nel metodo
onCreate()
dell'attività in cui accedi ai dati, ad esempio l'attività in cui
richiedi la posizione o accedi all'elenco dei contatti dell'utente, chiama
createAttributionContext()
,
passando il tag di attribuzione che vuoi associare a una parte della tua
app.
Il seguente snippet di codice mostra come creare un tag di attribuzione per una parte di un'app che condivide foto e posizione:
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. } } }
Includere i tag di attribuzione nei log degli accessi
Aggiorna il callback AppOpsManager.OnOpNotedCallback
in modo che i log della tua app
includano i nomi dei tag di attribuzione che hai definito.
Il seguente snippet di codice mostra la logica aggiornata che registra i tag di attribuzione:
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); } }