稽核資料存取

您可以透過執行資料存取稽核功能,深入瞭解應用程式及其依附元件如何存取使用者的私人資料。這項程序適用於搭載 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);
    }
}