AccessibilityService()로 다른 앱 접근 막기
배운 내용 정리를 위해 기록한다.
시스템을 개발하다보면, 서비스를 통해 어떤 기능이나 개념을 구현해야할 때가 있다.
오늘은 accessibilityService로 접근성을 얻어 다른 앱을 차단하는 기능을 구현해보겠다.
우선 서비스는 무엇일까?
모바일을 개발하는 개발자라면 모두 알 것이라고 생각하는 Android 4대 컴포넌트(Activity, Service, BroadcastReciever, Contents Provider) 중 하나이다. 주로 백그라운드에서 지속적으로 돌아가는 동작을 제어한다. 이 서비스를 이용해서 다양한 백그라운드 프로세스를 만드는데, 오늘은 그 중 하나인 AccessibilityService로 제어 프로세스를 만들고자한다.
요 AccessibilityService는 이름에서도 알 수 있듯이, 접근성을 제어하는 서비스이다.
그렇다면 접근성이란..?
- 사용자의 행동을 감시하거나
- 사용자의 조작시의 이벤트를 취득
- 앱의 View 구조를 얻거나
- 이벤트 발행시의 View 정보 (id, class, text 등)
- 현재의 Window의 View 구조(view tree)
- 얻어진 View를 Service 내에서 클릭하거나
- 위에서 얻은 View 정보로부터 사용자 조작을 모방
등과 같은 기능을 수행할 수 있는 권한을 말한다. 휴.
이 권한의 사용은
설정 - 접근성 - 설치된 서비스에서 확인할 수 있고, 누르면 허용이 가능하다.
설명 다 했다...그렇다면 이 '접근성' + '서비스'를 어떻게 써먹느냐 하는것인디..
간단하다. 앞서 소개했던 접근성의 기능중에
- 사용자의 조작시의 이벤트를 취득
이걸 써먹으면 되지롱~!
접근성 서비스를 만들어놓으면, 사용자가 앱을 사용하다가 어떤 특정 액션을 취해서(홈버튼을 눌러서 앱을 끈 다음 다른 앱을 킨다던지) 다른 앱에 접근하게 되었을 때, 이 접근성 서비스가 이를 캐치해서 우리의 프로젝트에 알려주게 된다. 그럼 우리의 프로젝트는 이 앱이 우리가 허용한 앱인지를 확인하고 접근을 허용하든지...금지하든지...하면 된다.
자 이제 프로젝트를 구상해보자.
일단 접근성을 설정해두기 위해서는 접근성 허용이 필요하니
1. 시작시에 사용자에게 이를 전달받는다
그 다음으로,
2. 허용 가능한 앱 리스트를 만들어둔다
앱내에서 허용한 리스트만 쓸 수도 있지만, 사용자가 임의로 추가를 할 수도 있어야 사용성이 높은 어플이 될 것이다.
3. 허용 가능한 앱을 추가할것인지 사용자에게 묻는다.
4. 있다면 받아서 리스트에 추가해 허용 가능한 앱 리스트를 갱신한다.
5. 사용자가 이벤트를 발생시켰을 때, 허용 가능한 앱이 아니라면 사용자가 앱을 사용하기 전에 특정 기능(타이머와 같
은)을 종료하도록 만든다.
여기서 3,4번은 5번에서 실행되도록 프로세스를 만들어야할것이다.
나는 모든과정에 앞서 특정 버튼을 누르는 시점에 접근성 권한이 허용되어있는지 먼저 확인하도록
프로세스를 만들었다.
private fun checkAccessPermission(): Boolean {
val accessibilityManager: AccessibilityManager =
getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
var list: List<AccessibilityServiceInfo> =
accessibilityManager!!.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC)
for (i in list.indices) {
val info = list[i]
if (info.resolveInfo.serviceInfo.packageName == application.packageName) {
return true
}
}
return false
}
(이친구를 원하는 시점에 조건문 안으로 호출해준다. 허용되어있다면 true를 반환하고, 아니라면 false를 반환한다.)
만약 접근성이 허용되지 않았다면 사용자에게 dialog를 띄워 이를 직접 허용하게끔 유도했다
fun setAccessibilityPermissions() {
val permissionDialog = AlertDialog.Builder(this)
permissionDialog.setTitle("접근성 권한 설정")
permissionDialog.setMessage("앱을 사용하기 위해 접근성 권한이 필요합니다.")
permissionDialog.setPositiveButton("허용", DialogInterface.OnClickListener { dialog, which ->
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
return@OnClickListener
}).create().show()
}
이후 사용자가 이를 허용했다면, 서비스가 시작되게 된다.
다음으로는,
2. 허용 가능한 앱 리스트를 만들어둔다 / 4. 있다면 받아서 리스트에 추가해 허용 가능한 앱 리스트를 갱신한다. 를
구현하기 위해 리스트를 만들어줘야한다.
나는 우선적으로 허용될 몇 개의 앱을 리스트로 만들고, 사용자가 확장할 수 있게 하였다.
코드는 다음과 같다.
1,3은 xml도 따로 필요하고, 선택후 저기 add 해주는 기능 말고는 없으므로 생략한다.
다음으로 5. 사용자가 이벤트를 발생시켰을 때, 허용 가능한 앱이 아니라면 사용자가 앱을 사용하기 전에 특정 기능(타이머와 같은)을 종료하도록 만든다. 를 구현하기 위해 드디어 서비스를 써줄것이다.
앞서 언급했던대로, 사용자의 이벤트를 캐치하는 accessibility서비스를 사용할것이다. 이친구는 갤럭시에서는 제공이 안되는 카카오톡 미리보기 어플 등으로 사용이 되고있다.
우선 서비스를 사용하기 위해서 서비스 코드를 만들어준다.
class Accessibility : AccessibilityService() {
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
val denyApp = false
if (event!!.eventType === AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
Log.e("packageName", event!!.packageName.toString())
if (!restricedApp.contains(event!!.packageName)) {
restrictApp()
Toast.makeText(
this.applicationContext,
"허용된 앱이 아닙니다.",
Toast.LENGTH_SHORT
).show()
}
}
}
override fun onInterrupt() {
}
private fun restrictApp(){
val intent = Intent()
intent.action = "android.intent.action.MAIN"
intent.addCategory("android.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 내부에 원하는 코드를 작성해주어야 한다. 나는 앞서 만들어둔 사용자 리스트에 캐치된 이벤트의 패키지명이 포함되는지를 확인했다. 포함된다면 허용된 앱이고 아니라면 사용이 허가되지 않은 앱일 것이다. 그러므로 여기
서는 알맞는 액션 함수를 구현해 호출해주면 된다. 나는 restrictApp()을 만들어주었다.
다음으로, 서비스를 메니페스트에 등록해주어야한다.
이 관련 태그는 application 하위에 들어가야한다.
이제 거의 다 왔다,...............
마지막으로, 저 태그 안에 보이는 resource의 accessibility_service_config.xml을 xml디렉토리 하위에 만들어준다
여기서 notificationTimeout은 잘 설정해야한다. 저걸 길게 잡으면 반응 속도가 느려진다. 앱 차단을 위해서라면 저 간격이 타이트한게 좋겠지? 나는 그래서 100밀리초로 지정했다.
끝.