Android 배경 에서 Activity 를 시작 하 는 예시
며칠 전에 제품 이 수 요 를 제 기 했 습 니 다.백 스테이지 에서 저희 앱 의 Activity 를 시작 하려 고 했 습 니 다.Android 버 전의 업데이트 와 각 ROM 업 체 의 무한 개조 에 따라 이런 사용자 체험 에 영향 을 주 는 기능 은 많은 제한 을 받 았 습 니 다.어 쩔 수 없 었 습 니 다.깡패 의 기능 이지 만 돈 을 가지 고 사람 을 대신 해서 재 해 를 없 애 는 바람 에 끙끙 거 리 는 조사 연 구 를 시 작 했 습 니 다.
네 이 티 브 안 드 로 이 드 ROM
먼저 안 드 로 이 드 의 네 이 티 브 ROM 부터 공식 적 인 설명 에 따 르 면 백 스테이지 액 티 비 티 시작 제한 은 안 드 로 이 드 10(API 29)에서 시 작 된 것 이 고 그 전에 네 이 티 브 ROM 은 이 제한 이 없 었 기 때문에 저 는 각각 안 드 로 이 드 9(API 28)와 10(API 29)버 전의 시 뮬 레이 터 를 시 작 했 는데 API 28 에서 백 스테이지 에서 액 티 비 티 를 직접 시작 할 수 있 음 을 발 견 했 습 니 다.반면 API 29 에 서 는 직접 시작 할 수 없 도록 제한 을 받 았 다.공식백그라운드 에서 Activity 시작 하기의 제한 설명 을 참조 하여 제한 을 받 지 않 는 예외 상황 을 제시 했다.그 밖 에 공식 적 인 추천 은 배경 에서 시작 하 는 수요 에 대해 먼저 사용자 에 게 Activity 를 직접 시작 하 는 것 이 아니 라 Notification 을 보 여 준 다음 에 사용자 가 Notification 을 클릭 한 후에 해당 하 는 논 리 를 처리 하 는 것 이다.또한 Notification 을 설정 할 때 setFullScreenIntent 를 통 해 전체 화면 Intent 대상 을 추가 할 수 있 습 니 다.이 방법 은 테스트 를 통 해 Android 10 의 시 뮬 레이 터 에서 배경 에서 Activity 인터페이스(android.permission.USE 가 필요 합 니 다.FULL_SCREEN_INTENT 권한).코드 는 다음 과 같 습 니 다:
object NotificationUtils {
private const val ID = "channel_1"
private const val NAME = "notification"
private var manager: NotificationManager? = null
private fun getNotificationManagerManager(context: Context): NotificationManager? {
if (manager == null) {
manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
}
return manager
}
fun sendNotificationFullScreen(context: Context, title: String?, content: String?) {
if (Build.VERSION.SDK_INT >= 26) {
clearAllNotification(context)
val channel = NotificationChannel(ID, NAME, NotificationManager.IMPORTANCE_HIGH)
channel.setSound(null, null)
getNotificationManagerManager(context)?.createNotificationChannel(channel)
val notification = getChannelNotificationQ(context, title, content)
getNotificationManagerManager(context)?.notify(1, notification)
}
}
private fun clearAllNotification(context: Context) {
getNotificationManagerManager(context)?.cancelAll()
}
private fun getChannelNotificationQ(context: Context, title: String?, content: String?): Notification {
val fullScreenPendingIntent = PendingIntent.getActivity(
context,
0,
DemoActivity.genIntent(context),
PendingIntent.FLAG_UPDATE_CURRENT
)
val notificationBuilder = NotificationCompat.Builder(context, ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(title)
.setContentText(content)
.setSound(null)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_CALL)
.setOngoing(true)
.setFullScreenIntent(fullScreenPendingIntent, true)
return notificationBuilder.build()
}
}
지금까지 전체적으로 느낌 이 좋 았 습 니 다.현 단계 의 안 드 로 이 드 네 이 티 브 ROM 은 백 스테이지 에서 Activity 인터페이스 를 정상적으로 시작 할 수 있 습 니 다.안 드 로 이 드 9 든 10 버 전이 든 모두 흐뭇 합 니 다.맞 춤 형 ROM
문제 가 수면 위로 떠 오 르 기 시작 했다.각 제조 업 체 가 안 드 로 이 드 에 대한 맞 춤 형 화 는 각각 다 르 기 때문에 안 드 로 이 드 는 GPL 프로 토 콜 을 계승 하지 않 았 다.이것 은 아파 치 오픈 소스 허가 프로 토 콜,즉 제3자 제조 업 체 가 코드 를 수정 한 후에 소스 를 닫 을 수 있 기 때문에 제조 업 체 ROM 의 소스 코드 가 바닥 까지 어떤 수정 을 했 는 지 알 수 없다.어떤 기종 은 백 엔 드 팝 업 인터페이스 를 추 가 했 습 니 다.예 를 들 어 MIUI 에 이 권한 이 추 가 됐 고 기본적으로 닫 혔 습 니 다.백 엔 드 에 가입 하지 않 는 한샤 오미 오픈 플랫폼문서 에 설명 되 어 있 습 니 다.이 권한 은 기본적으로 거부 되 었 습 니 다.백 엔 드 팝 업 페이지 를 사용 할 수 없 을 뿐만 아니 라 특수 응용 프로그램 에 화이트 리스트 를 제공 합 니 다.예 를 들 어 음악(가사 표시),운동,VOIP(전화)등;화이트 리스트 애플 리 케 이 션 은 프로 모 션 등 악의 적 인 행위 가 발생 하면 화이트 리스트 를 영구 취소한다.
배경 팝 업 인터페이스 권한 검사
샤 오미 모델 에 추 가 된 이 배경 팝 업 인터페이스의 권한 은 AppOps Service 에서 새로운 권한 을 확장 하 는 것 입 니 다.AppOps Manager 소스 코드 를 보면 익숙 한 상수 들 을 볼 수 있 습 니 다.
@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
public static final int OP_GPS = 2;
public static final int OP_READ_CONTACTS = 4;
// ...
}
따라서 AppOps Service 를 통 해 배경 팝 업 인터페이스 권한 이 있 는 지 확인 할 수 있 습 니 다.이 권한 에 대응 하 는 OpCode 는 무엇 입 니까?인터넷 관계자 에 따 르 면 이 권한 의 Code 는 10021 이 므 로 AppOps Manager.checkOpNoThrow 나 AppOps Manager.noteOpNoThrow 등 일련의 방법 으로 이 권한 이 존재 하 는 지 확인 할 수 있 습 니 다.그러나 이 방법 들 은 모두@hide 로 표 시 된 것 이 므 로 반 사 를 사용 해 야 합 니 다.
fun checkOpNoThrow(context: Context, op: Int): Boolean {
val ops = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
try {
val method: Method = ops.javaClass.getMethod(
"checkOpNoThrow", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java
)
val result = method.invoke(ops, op, myUid(), context.packageName) as Int
return result == AppOpsManager.MODE_ALLOWED
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun noteOpNoThrow(context: Context, op: Int): Int {
val ops = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
try {
val method: Method = ops.javaClass.getMethod(
"noteOpNoThrow", Int::class.javaPrimitiveType, Int::class.javaPrimitiveType, String::class.java
)
return method.invoke(ops, op, myUid(), context.packageName) as Int
} catch (e: Exception) {
e.printStackTrace()
}
return -100
}
또한 다른 권한 이 추 가 된 code 를 알 고 싶다 면 위의 방법 으로 특정한 범위(예 를 들 어 10000~10100)내 code 의 권한 을 옮 겨 다 니 고 핸드폰 조작 으로 검색 하고 자 하 는 권한 을 끄 고 옮 겨 다 니 는 결과 에 따라 대체적으로 권한 이 있 는 code 를 얻 을 수 있 습 니 다.Android P 배경 시작 권한
샤 오미 맥 스 3 에서 테스트 한 결과 배경 에서 Activity 인 터 페 이 스 를 시작 할 수 있 는 두 가지 방식 이 발견 되 었 는데 그 시스템 은 Android 9 을 기반 으로 하 는 MIUI 시스템 이다.
방식 1:moveTaskToFront
이런 방식 은 백 스테이지 에서 액 티 비 티 를 직접 시작 하 는 것 이 아니 라 사고방식 을 바 꾸 었 다.백 스테이지 에서 목표 액 티 비 티 를 시작 하기 전에 응용 프로그램 을 프론트 데스크 로 전환 한 다음 에 목표 액 티 비 티 를 시작 하 는 것 이다.필요 하 다 면 액 티 비 티.moveTaskToBack 방법 을 통 해 이전에 프론트 데스크 로 전환 한 액 티 비 티 를 백 스테이지 로 다시 옮 겨 테스트 를 거 쳐안 드 로 이 드 10 에 서 는 이 방법 이 효력 을 잃 었 습 니 다.하지만 10 이하 버 전 은 구 할 수 있 습 니 다.TASKS 권한).
목표 Activity 를 시작 하기 전에 응용 프로그램 이 백 엔 드 에 있 는 지 판단 하고 판단 방법 은 Activity Manager.getRunning AppProcesses 방법 이나 application.Activity LifecycleCallbacks 를 통 해 전 백 엔 드 를 감청 할 수 있 습 니 다.이 두 가지 방법 은 인터넷 에 모두 글 이 있 으 므 로 군말 하지 않 겠 습 니 다.배경 을 직접 붙 여 프론트 데스크 로 전환 하 는 코드:
fun moveToFront(context: Context) {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
activityManager?.getRunningTasks(100)?.forEach { taskInfo ->
if (taskInfo.topActivity?.packageName == context.packageName) {
Log.d("LLL", "Try to move to front")
activityManager.moveTaskToFront(taskInfo.id, 0)
return
}
}
}
fun startActivity(activity: Activity, intent: Intent) {
if (!isRunningForeground(activity)) {
Log.d("LLL", "Now is in background")
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
// TODO moveToFront ,
moveToFront(activity)
activity.startActivity(intent)
activity.moveTaskToBack(true)
} else {
NotificationUtils.sendNotificationFullScreen(activity, "", "")
}
} else {
Log.d("LLL", "Now is in foreground")
activity.startActivity(intent)
}
}
방식 2:훅MIUI 시스템 이 작 동 하지 않 기 때문에 AOSP 소스 코드 를 다시 연구 해 보 자.죽은 말 은 살 아 있 는 말 의사 가 되 어 어떤 실 마 리 를 찾 을 수 있 는 지 살 펴 보 자.먼저 Activity.startActivity 방법 부터 추적 합 니 다.Activity 시작 소스 프로 세 스 를 읽 어 본 적 이 있다 면 Activity.startActivity 를 알 수 있 거나 Instrumentation.exec Start Activity 에 호출 한 다음 Binder 를 통 해 AMS 관련 방법 으로 호출 하면 권한 인증 이 AMS 에서 이 루어 집 니 다.권한 이 만족 하지 않 으 면 자 연 스 럽 게 시작 하 는 데 실패 합 니 다(Android 10).
// APP
public ActivityResult execStartActivity(Context who, IBinder contextThread, ...) {
// ...
// Binder AMS
int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(),
intent, intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
// ...
}
// system_server
// AMS
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
// ...
}
이 몇 개의 매개 변 수 를 보 세 요.Android Q 배경 시작 권한
위 에 안 드 로 이 드 Q 버 전부터 네 이 티 브 시스템 도 백 스테이지 시작 제한 을 넣 었 으 며,알림 설정 을 통 해 풀 스크린 인 텐트 를 네 이 티 브 안 드 로 이 드 10 시스템 에서 백 스테이지 에서 액 티 비 티 를 시작 할 수 있 도록 했다.AOSP 소스 코드 를 보면 AMS 에서 이 부분의 배경 권한 이 제 한 된 코드 를 찾 을 수 있 습 니 다.위 에서 startActivity 프로 세 스 를 설명 하고 앱 프로 세 스 가 요청 하면 Binder 크로스 프로 세 스 를 통 해 system 로 호출 됩 니 다.server 프로 세 스 의 AMS 를 Activity Starter.startActivity 방법 으로 호출 합 니 다.배경 시작 에 대한 제한 은 여기 있 습 니 다.
// , , ,
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
SafeActivityOptions options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
// ...
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
inTask != null, callerApp, resultRecord, resultStack);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
callingPackage);
boolean restrictedBgActivity = false;
if (!abort) {
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
originatingPendingIntent, allowBackgroundActivityStart, intent);
}
// ...
}
이 곳 의 shouldAbortBackground Activity Start 호출 은 Android Q 에 추 가 된 것 입 니 다.방법 명 을 보면 식칼 을 사용 할 수 있 습 니 다.이것 은 배경 을 대상 으로 시 작 된 것 입 니 다.
boolean shouldAbortBackgroundActivityStart(...) {
final int callingAppId = UserHandle.getAppId(callingUid);
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
return false;
}
if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
return false;
}
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
return false;
}
// don't abort if the caller has the same uid as the recents component
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
return false;
}
// don't abort if the callingUid is the device owner
if (mService.isDeviceOwner(callingUid)) {
return false;
}
// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
Slog.w(TAG, "Background activity start for " + callingPackage
+ " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
return false;
}
// ...
}
이 방법 을 통 해 백 스테이지 시작 제한 과 공식 문서백그라운드 에서 Activity 시작 하기의 제한 에 대한 설명 은 대응 할 수 있 습 니 다.이 안 은 모두 uid 에 대한 권한 판단 이 고 시스템 프로 세 스 system 입 니 다.server 에서 완료 되 었 습 니 다.가방 이름 만 바 꾸 면 소 용 없습니다...백 스테이지 작 동 에 대한 단독 제한 이 없 는 ROM 에서 전체 화면 통 지 를 통 해 백 스테이지 Activity 페이지 를 성공 적 으로 팝 업 할 수 있다.예 를 들 어 샤 오미 A3,그리고 비보 한 대 와 삼 성 핸드폰 도 있 는데 구체 적 인 기종 은 잊 어 버 렸 다.제 한 된 장치 에 서 는 빨 간 쌀 Note 8 Pro 와 같은 것 이 튀 어 나 오지 않 는 다.
레 드 미 노트 8 프로 라 는 경골한 에 대해 끊임없이 많은 방법 을 시 도 했 지만 사실은 모두 운 에 맡 겼 다.MIUI 의 소스 코드 를 얻 지 못 했 기 때문에 나중에 생각 을 바 꾸 려 고 한다.이 핸드폰 에서 관련 framework.jar 를 꺼 내 서 역 컴 파일 해 볼 수 있 을 까?어쩌면 수확 이 있 을 지도 몰라!그러나 루트 폰 이 필요 하 다.이것 은 하기 쉽다.샤 오 미 는 자신 이 루트 를 제공 할 수 있 는 개발 판 시스템 을 제공 하고 있다.그래서 MIUI 홈 페이지 에 가서 찾 아 보 니 이 홍 미 Note 8 Pro 모델 은 개발 판 시스템 을 제공 하지 않 았 다(웃음).예전 에 저급 기 샤 오 미 는 더 이상 개발 판 을 제공 하지 않 는 다 고 말 한 것 같다.좋아,수중 에 다른 시도 할 수 있 는 휴대 전화 가 없어.
다시 생각해 보 니 안정 판 ROM 가방 을 직접 다운로드 할 수 있 을 까?압축 을 풀 면 소스 코드 와 관련 된 흔적 을 얻 을 수 있 는 도구 가 있 을 까?그래서 ROM.zip 을 다운로드 한 후에 압축 을 풀 었 습 니 다.안에 시스템 이미지 img 파일 과.dat.br 파일 만 있 습 니 다.이 부분 은 잘 모 르 겠 습 니 다.제 가 원 하 는 것 을 얻 을 수 있어 도 전체 절차 에 걸 리 는 시간 비용 이 예상 을 초과 할 것 이 라 고 추측 하기 때문에 잠시 이 생각 을 내 려 놓 을 수 밖 에 없습니다.후속 적 으로 더 깊이 연구 할 시간 이 충분 하 다.
총결산
네 이 티 브 안 드 로 이 드 ROM
안 드 로 이 드 네 이 티 브 ROM 은 안 드 로 이 드 9(직접 시작)든 10 버 전(전체 화면 알림 을 통 해)이 든 백그라운드 에서 Activity 인 터 페 이 스 를 정상적으로 시작 할 수 있다.
맞 춤 형 ROM
배경 팝 업 인터페이스 권한 검사:
Android P 버 전의 샤 오미:
4.567917.Hook 관련 매개 변 수 를 통 해 배경 에서 Activity 를 시작 합 니 다.코드 는 어떤 이유 로 제시 할 수 없 기 때문에 필요 한 친구 가 메 시 지 를 남 겨 주세요4.567917.샤 오미 기종 만 테스트 한 적 이 있 고 다른 기종 은 반드시 사용 할 수 있 는 것 이 아니다4.567917.이론 적 으로 P 버 전 이하 의 샤 오미 도 지지 해 야 한다.
Android P 버 전의 기종:
Android Q 버 전의 기종:
4.567917.시스템 전체 화면 알림 방식 으로 배경 Activity 를 조정 합 니 다4.567917.따로 제 한 된 ROM 에서 실 패 를 조정 할 수 있 습 니 다.
MIUI 코드 를 역 컴 파일 하 는 방식 은 하나의 추측 일 뿐 시간 적 원인 은 행동 에 옮 기지 못 했다.제품 오빠 의 수 요 는 당분간 완전히 실현 되 지 못 할 것 같 습 니 다.관련 연 구 를 해 본 적 이 있 는 지 모 르 겠 습 니 다.(또는 내막 을 알 고 있 는)친구 들 이 참고 방향 을 제시 할 수 있 는 지 모 르 겠 습 니 다.비록 비교적 건달 적 인 기능 이지 만 코드 는 무죄 입 니 다.헤헤,목 표를 향 해 해결 방법 을 생각 하고 여러 방향 으로 조사 연 구 를 합 니 다.나 는 그 자체 가 재 미 있 고 향상 되 는 일이 라 고 생각한다!관련 연 구 를 한 적 이 있 는 학생 들 이 평론 구역 에서 건 의 를 하고 오리 에 게 필요 한 것 을 잘 해 주 는 것 을 환영 합 니 다.
이상 은 바로 안 드 로 이 드 배경 에서 Activity 를 시작 하 는 실현 예제 의 상세 한 내용 입 니 다.안 드 로 이 드 배경 에서 Activity 를 시작 하 는 것 에 관 한 자 료 는 다른 관련 글 을 주목 하 세 요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.