[안드로이드] 권한, 위험권한, permission, RECEIVE_SMS
하라는대로 다 했는데 왜 나만 안될까....
권한 (Permission)
*Android 6.0(API 23/마쉬멜로 버전) 이상에서 일반권한과 위험권한이 나뉨
- 설치 시간 권한
: 최소한 필요한 권한
앱 세부정보에 안내가 되어 있고, 사용자가 앱을 설치할 때 자동으로 앱에 권한을 부여함
- 런타임 권한 (위험 권한)
: 마이크, 카메라 등 민감한 정보 사용할 때
앱을 실행하는 시점에, 권한을 요청
* 권한 요청 메시지는 '시스템'이 띄
(일반권한, 서명권한, 특별권한도 있음)
https://developer.android.com/guide/topics/permissions/overview?hl=ko
[위험 권한 종류]
[위험권한 사용법]
1. Manifest.xml에 권한 선언
uses-permission 태그 사용
<manifest ..>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<application ...>
...
</application>
</mainfest>
*일부 하드웨어 및 소프트웨어 기능 상수는 <uses-feature> 시스템을 사용하여
API가 필요하다고 선언하기 전에 API를 사용할 수도 있음
(참고 : https://developer.android.com/guide/topics/manifest/uses-feature-element?hl=ko#permissions-features )
https://developer.android.com/guide/topics/manifest/uses-feature-element?hl=ko
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
2. 권한을 사용할 UI 구성
- SMS를 표시할 SmsActivity.java, activity_sms.xml 작성
https://itnote-for-me.tistory.com/46
[안드로이드] 브로드캐스트 수신자, onReceiver, SMS메시지 수신 어플
메모 : ***22버전 이후 위험권한으로 SMS 수신 안되는 부분 수정 필요***(아래 코드는 22 이전 버전에서만 작동합니다)브로드캐스트 수신자 (Broadcast Receiver): 앱에서 브로드캐스팅 메시지를 받고 싶
itnote-for-me.tistory.com
3. 앱에 이미 권한이 부여되었는지 확인 (권한이 필요할 때마다)
+ 권한이 없는 경우, 권한을 요청하는 이유 안내
int permissionCheck = ContextCompat.checkSelfPermission(this,Manifest.permission.RECEIVE_SMS);
- shouldShowRequestPermissionRationale() 을 사용하여, 사용자가 권한 설명을 원하는지 확인
- requestPermission() 을 사용하여 시스템이 권한 요청 코드를 관리하도록 허용
1) 시스템이 권한 요청 코드를 관리하도록 허용
*CONTEXT, REQUESTED_PERMISSION는 상황에 맞게 적절히 변경
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
// You can use the API that requires the permission.
performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...);
} else {
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
requestPermissionLauncher.launch(
Manifest.permission.REQUESTED_PERMISSION);
}
}
private ActivityResultLauncher<String> requestPermissionLauncher =
registerForActivityResult(new RequestPermission(), isGranted -> {
if (isGranted) {
//권한이 허용됨. 계속 진행
} else {
//사용자에게 권한 거부로 인해 사용 못하는 기능 안내
}
});
2) 권한 요청 코드 직접 관리
*CONTEXT, REQUESTED_PERMISSION,REQUEST_CODE 는 상황에 맞게 적절히 변경
//MainActivity.java 의 onCreate() 내부에
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
performAction(...);
} else if (shouldShowRequestPermissionRationale(...)) {
showInContextUI(...);
} else {
requestPermissions('CONTEXT',
new String[] { Manifest.permission.REQUESTED_PERMISSION },
REQUEST_CODE);
}
//사용자가 시스템 권한 대화상자에 응답하면, 시스템은 앱의 onRequestPermissionResult()을 호출함
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission is granted. Continue the action or workflow
// in your app.
} else {
// Explain to the user that the feature is unavailable because
// the feature requires a permission that the user has denied.
// At the same time, respect the user's decision. Don't link to
// system settings in an effort to convince the user to change
// their decision.
}
return;
}
// Other 'case' lines to check for other
// permissions this app might request.
}
}
3. 런타임 권한 요청 메시지 표시
4. 런타임 권한 부여에 대한 응답 확인
#최종 Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MySMSReceiver"
tools:targetApi="31">
<activity
android:name=".SmsActivity"
android:exported="false" />
<receiver
android:name=".SmsReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="10000">
<action android:name="android.provier.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
#최종 MainActivity.java
package com.example.mysmsreceiver;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.Manifest;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// BroadcastReceiver br = new SmsReceiver();
// IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
// filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
// this.registerReceiver(br,filter);
int permissionCheck = ContextCompat.checkSelfPermission(this,Manifest.permission.RECEIVE_SMS);
//권한이 이미 있는 경우
if(permissionCheck == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"SMS 수신 권한 있음",Toast.LENGTH_LONG).show();
}
//권한이 없는 경우
else{
Toast.makeText(this,"SMS 수신 권한 없음",Toast.LENGTH_LONG).show();
//RECEIVE_SMS에 관한 권한에 대해서 인지
if(shouldShowRequestPermissionRationale(Manifest.permission.RECEIVE_SMS)){
Toast.makeText(this,"SMS 권한 설명 필요함", Toast.LENGTH_LONG).show();
}else{
//사용자에게 교육용 UI가 표시되거나 shouldShowRequestPermissionRationale()의 반환값에서 설명을 표시하지 않아도 된다고 나타나면
//권한 요청하는 대화상자 표시 **시스템에서 하는 것
requestPermissions(new String[]{Manifest.permission.RECEIVE_SMS},1);
}
}
}
//사용자가 권한 대화상자에 응답하면 시스템은 앱의 onRequestPermissionsResult()를 호출
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
//requestCode가 1인 RECEIVE_SMS에 대한 권한인 경우
case 1:
//request가 취소되면, grantResults가 비어있음
//request 응답받았다면
if(grantResults.length>0){
if(grantResults[0] == PERMISSION_GRANTED){
Toast.makeText(this,"SMS 수신 권한을 사용자가 승인함",Toast.LENGTH_LONG).show();
}else if(grantResults[0] == PERMISSION_DENIED){
Toast.makeText(this,"SMS 수신 권한을 사용자가 거부함",Toast.LENGTH_LONG).show();
//권한이 거부된 경우, 거부에 따른 영향을 설명해야함
//Android 11(API수준 30)부터 특정 권한에 관해 2번 이상 거부하면, 그 권한을 다시 묻지 않음
}
}else{
//request 응답 받지 못한 경우
Toast.makeText(this,"SMS 수신 권한을 부여받지 못함",Toast.LENGTH_LONG).show();
}
}
}
}
[참조]
https://www.boostcourse.org/mo316/lecture/22591?isDesc=false
안드로이드 앱 프로그래밍
부스트코스 무료 강의
www.boostcourse.org
https://developer.android.com/guide/topics/permissions/overview?hl=ko
Android에서의 권한 | Android 개발자 | Android Developers
Android에서의 권한 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱 권한은 다음 항목에 대한 액세스를 보호하여 사용자 개인 정보 보호를 지원합니다. 제
developer.android.com
https://developer.android.com/training/permissions/requesting?hl=ko
런타임 권한 요청 | Android 개발자 | Android Developers
런타임 권한 요청 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 모든 Android 앱은 액세스가 제한된 샌드박스에서 실행됩니다. 앱이 자체 샌드박스 밖에 있
developer.android.com
2.2: Sending and Receiving SMS Messages - Part 2 · GitBook
2.2: Part 2 - Sending and Receiving SMS Messages Contents: Task 3. Receive SMS messages with a broadcast receiver To receive SMS messages, use the onReceive() method of the BroadcastReceiver class. The Android framework sends out system broadcasts of event
google-developer-training.github.io