데이터 액세스 분석

앱 및 앱 종속 항목이 사용자의 비공개 데이터에 액세스하는 방식의 투명성을 높이기 위해 Android 11은 데이터 액세스 분석을 도입합니다. 이 프로세스에서 얻은 유용한 정보를 통해 잠재적으로 예상치 못한 데이터 액세스를 확인하고 수정할 수 있습니다. 앱은 다음 이벤트 중 하나가 발생할 때마다 작업을 실행할 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)
}

자바

@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 호출 중에 데이터 액세스가 발생하지 않으면 호출됩니다. 가장 일반적인 예는 앱이 리스너를 등록하고 리스너의 콜백이 호출될 때마다 데이터 액세스가 발생하는 경우입니다.

    onAsyncNoted()에 전달되는 AsyncNotedOp 인수에는 getMessage()라는 메서드가 포함되어 있습니다. 이 메서드는 데이터 액세스에 관한 자세한 정보를 제공합니다. 위치 콜백의 경우 메시지에는 리스너의 system-identity-hash가 포함됩니다.

  • onSelfNoted()는 앱이 자체 UID를 noteOp()에 전달하는 매우 드문 경우에 호출됩니다.

속성 태그별 데이터 액세스 분석

앱에는 사용자가 사진을 촬영하고 이 사진을 연락처와 공유하는 등 몇 가지 주요 사용 사례가 있을 수 있습니다. 이러한 다목적 앱을 개발하는 경우 데이터 액세스를 분석할 때 앱의 각 부분에 속성 태그를 적용할 수 있습니다. attributionTag 컨텍스트는 onNoted() 호출에 전달되는 객체에 다시 반환됩니다. 이를 통해 코드의 논리 부분에 관한 데이터 액세스를 더 쉽게 추적할 수 있습니다.

앱에서 속성 태그를 정의하려면 다음 섹션의 단계를 완료하세요.

속성 태그 만들기

위치를 요청하거나 사용자의 연락처 목록에 액세스하는 활동과 같은 데이터에 액세스하는 활동의 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.
    }
}

자바

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)
    }
}

자바

@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);
    }
}