-
외부 앱 접속 막기Android 2025. 3. 3. 20:44
안녕하세요~ 오늘은 외부 앱의 접속을 차단하는 방법에 대해서 얘기해보고자 합니다 🙂
외부 앱의 접속을 막으려면 어떤게 필요할까요?
- 접근성 권한 : 다른 앱의 접속을 차단할 수 있는 권한을 사용자로부터 받아야 함
- 서비스 : 차단할 앱을 백그라운드에서 감지해야함
- 브로드캐스트 리시버 : 안드로이드 컴포넌트간의 데이터 전달
대표적으로 3개의 구성요소가 필요한데 하나씩 짚어보겠습니다 :)
접근성 권한 받아오기
다른 앱의 접속을 차단할 수 있는 권한을 얻기 위해서는 사용자로부터 접근성 권한을 받아와야 합니다.
val isAccessibilityPermitted = checkAccessibilityPermissions() if(!isAccessibilityPermitted) { AlertDialog.Builder(this).apply { setTitle("접근성 권한 허용 필요") setMessage("앱을 사용하기 위해 접근성 권한이 필요합니다.") setPositiveButton("허용") { _, _ -> startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) } setCancelable(false) create().show() } } // 접근성 권한을 확인하는 메서드 private fun checkAccessibilityPermissions(): Boolean { val accessibilityManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager val enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC) for(serviceInfo in enabledServices) { if(serviceInfo.resolveInfo.serviceInfo.packageName == application.packageName) return true } return false }
일단은 접근성 권한이 부여가 되었는지 확인을 먼저 합니다.
접근성 권한이 없는 경우 다이얼로그를 통해서 startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) 를 통해 설정 화면으로 이동시켜 직접 사용자가 권한을 킬 수 있도록 유도합니다.
접근성 권한을 확인하는 메소드도 같이 참고하시면 됩니다.
서비스에서 데이터 수신받기
class MyService : AccessibilityService() { var blockServicePackageNames: ArrayList<String>? = null private val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { blockServicePackageNames = intent?.getStringArrayListExtra("BLOCK_SERVICE_APPLICATIONS") } } override fun onCreate() { super.onCreate() val filter = IntentFilter("com.example.lifemaster.BROADCAST_RECEIVER") LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter) } }
다른 앱의 접속을 차단하려면 백그라운드에서 동작해야 하기 때문에 안드로이드 컴포넌트 중 서비스가 필요합니다.
먼저 BroadcastReceiver 을 상속받는 receiver 변수를 정의해줍니다. 이때, 외부의 intent 에서 어떤 키값으로 데이터를 받아올 지 작성해줍니다. 저는 BLOCK_SERVICE_APPLICATIONS 을 key 값으로 정했는데, 임의로 정하면 됩니다.
서비스의 onCreate 생명주기에 맞게 브로드캐스트 리시버를 등록해줍니다. 리시버를 등록할땐 인텐트 필터도 필요한데요. 인텐트 필터에는 브로드캐스트의 액션을 정의해줍니다. "패키지명.액션명" 형태로 마찬가지로 임의로 정하면 됩니다.
서비스는 데이터를 받아올 준비를 했으니, 다른 안드로이드 컴포넌트에서 데이터를 송신하는 부분을 살펴볼까요?
프래그먼트나 액티비티에서 브로드캐스트로 데이터 송신하기
val blockServicePackageNames = arrayListOf<String>() blockServices.forEach { // blockServices: List<DetoxItem> blockServicePackageNames.add(it.appPackageName) } val intent = Intent("com.example.lifemaster.BROADCAST_RECEIVER") intent.putStringArrayListExtra("BLOCK_SERVICE_APPLICATIONS", blockServicePackageNames) LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(intent)
여기서 blockServices 변수는 차단할 서비스에 대하여 앱 패키지명이 들어있는 리스트 형태의 데이터 클래스입니다.
여기서 앱 패키지명만 골라서 리스트 형태의 다른 변수에 담아줍니다. 이후에는, 해당 데이터를 서비스에 보내야하는데요.
안드로이드 4대 컴포넌트 사이에 데이터 전달을 하려면 인텐트가 필요합니다.
서로 다른 안드로이드 컴포넌트간의 데이터 전달에는 뷰모델과 같은 방식은 권장되지 않는다고 합니다. 따라서 브로드캐스트 리시버를 사용합니다.
다시 서비스로 돌아와보겠습니다. 앱에 대한 패키지명을 가지고 어떻게 앱을 차단할 수 있을까요?
서비스에서 앱 차단하기
class MyService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent) { if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { blockServicePackageNames?.forEach { if (event.packageName.toString() == it) { preventUsingApp() } } } } private fun preventUsingApp() { val intent = Intent() intent.action = Intent.ACTION_MAIN intent.addCategory(Intent.CATEGORY_HOME) intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS or Intent.FLAG_ACTIVITY_FORWARD_RESULT or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) startActivity(intent) } }
먼저 onAccessibilityEvent 오버라이드 함수에서 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 는 앱의 상태가 변경되었을 때 호출됩니다. 그때 파라미터로 받아오는 event 에는 접속한 앱의 패키지명이 포함되어 있습니다.
event 를 통해서 받아온 앱의 패키지명과 내가 차단하고자 지정한 앱의 패키지명이 동일한 경우에, 접속을 막는 로직을 수행합니다.
preventUsingApp 이라는 이름의 메소드로 앱의 접근을 막을 수 있습니다. Intent 에 여러 설정을 추가하여 앱에 접속했을 때 다시 홈 화면으로 이동시킬 수 있습니다.
매니페스트에 서비스 등록
<service android:name=".MyService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:exported="true"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService"/> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config"/> </service>
만든 서비스를 매니페스트에 등록을 해줘야 합니다. 이때, xml 파일도 하나 같이 필요합니다.
// accessibility_service_config.xml <?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes = "typeWindowStateChanged" android:accessibilityFeedbackType = "feedbackGeneric" android:canRetrieveWindowContent = "true" android:notificationTimeout = "100" />
시연 영상
한번 구현한 내용을 직접 눈으로 확인해보면 좋겠죠 🙂 쿠팡 앱으로 테스트해볼까요?
쿠팡 앱 접속 차단 쿠팡 앱 접속 허용 재설치후에도 접속 막기 다른 앱 추가 차단 여러 경우를 고려해서 녹화해보았는데요. 앱의 접근이 막아지는 것을 볼 수 있습니다. 앱을 삭제했다가 다시 설치해도 여전히 막아지는 것을 확인할 수 있습니다.
아쉬운 점은 다른 앱에 들어갔다가 접속이 막아졌을 때 "해당 앱은 접속이 불가능합니다" 라는 토스트 메세지를 띄우지 못한다는 점입니다. 왜냐하면, 내 앱이 다른 앱의 컨텍스트에 접근할 수 없어서 다른 앱에서는 직접적으로 UI 를 변경할 수 없습니다.
앱을 사용할 때 권한 요청을 많이 받아보셨을텐데요. 그중에 접근성 권한이 얼만큼 많은 영향을 가질 수 있는 지 이번에 잘 알게 된 것 같습니다. 도움이 되셨다면 좋아요 한번씩 꾹 눌러주세요! 🙂
'Android' 카테고리의 다른 글
알람 기능 구현하기(AlarmManager) (2) 2025.04.27 서버와 통신하기(retrofit) (0) 2025.03.16 핸드폰에 설치된 앱 불러오기 (0) 2025.02.25 액티비티간 데이터 전달 - startActivityForResult (0) 2024.10.10 안드로이드 기본 앱 연동하기 - 지도 앱, 전화 앱 (0) 2024.10.03