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) {
    // ...
}
이 몇 개의 매개 변 수 를 보 세 요.
  • caller:AMS 는 관련 임 무 를 수행 한 후에 이 를 통 해 Binder 를 클 라 이언 트 APP 프로 세 스 로 호출 하여 Activity 대상 을 예화 하고 수명 주기 방법 을 되 돌려 줍 니 다.caller 의 Binder 서버 는 APP 프로 세 스에 있 습 니 다
  • calling Package:이 매개 변수 표지 호출 자 패키지 이름..
  • ...
  • 여 기 는 Hook 의 일부 시스템 적 인 것 을 시도 할 수 있 습 니 다.구체 적 으로 Hook 의 코드 는 먼저 제시 하지 않 습 니 다.테스트 를 통 해 Android 9 의 샤 오미 장치 에서 성공 할 수 있 습 니 다.관심 이 있 으 면 스스로 연구 하고 토론 할 수 있 습 니 다.잠시 공개 하지 않 겠 습 니 다.필요 한 친구 가 댓 글 을 남 겨 주세요.혹은 샤 오미 롬 소스 코드 를 역 컴 파일 하면 안에서 뭔 가 를 발견 할 수 있다.
    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
    배경 팝 업 인터페이스 권한 검사:
  • 반사 AppOpsManager 관련 방법 으로 opCode 에 대응 하 는 권한 을 검 측 합 니 다
  • opCode=10021(좁쌀 기종);4.567917.다른 기종 은 opCode 를 옮 겨 다 닐 수 있 습 니 다.
    Android P 버 전의 샤 오미:
    4.567917.Hook 관련 매개 변 수 를 통 해 배경 에서 Activity 를 시작 합 니 다.코드 는 어떤 이유 로 제시 할 수 없 기 때문에 필요 한 친구 가 메 시 지 를 남 겨 주세요4.567917.샤 오미 기종 만 테스트 한 적 이 있 고 다른 기종 은 반드시 사용 할 수 있 는 것 이 아니다4.567917.이론 적 으로 P 버 전 이하 의 샤 오미 도 지지 해 야 한다.
    Android P 버 전의 기종:
  • moveTaskToFront 방법 을 통 해 응용 을 프론트 로 전환 합 니 다
  • 4.567917.이런 방법 은 공식 API 이기 때문에 호환성 이 더 좋 을 수 있다
  • 전환 에 실패 하면 moveTaskToFront 방법 을 몇 번 더 시도 할 수 있 습 니 다
  • 4.567917.이론 적 으로 P 버 전 이하 의 기종 도 지원 해 야 한다.
    Android Q 버 전의 기종:
    4.567917.시스템 전체 화면 알림 방식 으로 배경 Activity 를 조정 합 니 다4.567917.따로 제 한 된 ROM 에서 실 패 를 조정 할 수 있 습 니 다.
    MIUI 코드 를 역 컴 파일 하 는 방식 은 하나의 추측 일 뿐 시간 적 원인 은 행동 에 옮 기지 못 했다.제품 오빠 의 수 요 는 당분간 완전히 실현 되 지 못 할 것 같 습 니 다.관련 연 구 를 해 본 적 이 있 는 지 모 르 겠 습 니 다.(또는 내막 을 알 고 있 는)친구 들 이 참고 방향 을 제시 할 수 있 는 지 모 르 겠 습 니 다.비록 비교적 건달 적 인 기능 이지 만 코드 는 무죄 입 니 다.헤헤,목 표를 향 해 해결 방법 을 생각 하고 여러 방향 으로 조사 연 구 를 합 니 다.나 는 그 자체 가 재 미 있 고 향상 되 는 일이 라 고 생각한다!관련 연 구 를 한 적 이 있 는 학생 들 이 평론 구역 에서 건 의 를 하고 오리 에 게 필요 한 것 을 잘 해 주 는 것 을 환영 합 니 다.
    이상 은 바로 안 드 로 이 드 배경 에서 Activity 를 시작 하 는 실현 예제 의 상세 한 내용 입 니 다.안 드 로 이 드 배경 에서 Activity 를 시작 하 는 것 에 관 한 자 료 는 다른 관련 글 을 주목 하 세 요!

    좋은 웹페이지 즐겨찾기