后台弹出界面权限踩坑

后台弹出界面权限踩坑

   最近在处理MIUI系统中应用后台弹出界面权限时踩了一些坑,总结下经验,方便遇到同样问题的人。

后台弹出界面权限

   Android系统/应用自带了很多权限,为了限制应用的一些行为。但“魔高一尺,道高一丈”,现有的一些权限其实不能完全限制一些应用的行为,所以一些产商会在权限管理中自行添加相关的权限。

在这里插入图片描述

   上图中,后台弹出界面权限就是MIUI系统自行添加的权限。顾名思义其作用就是限制应用在后台弹出界面的权利,默认关闭,需手动赋予该权限应用才能在后台拉起Activity等界面。

   其在系统中的定义如下

//声明为 hide,即应用无法通过hook来修改该权限。
    /** @hide Background start Activity */
       public static final int OP_BACKGROUND_START_ACTIVITY = 10021;

   查看了下源码,其设置的时机是在PermissionConfiguration.java被加载时,在其内部的static方法块中进行设置以及初始化的。

 {
 // Background start Activity
Permission permission = new Permission(PermissionManager.PERM_ID_BACKGROUND_START_ACTIVITY,
           R.string.HIPS_Perm_background_start_activity,
           R.string.HIPS_Perm_background_start_activity_Desc,
           PermissionManager.GROUP_SETTINGS,
            PermissionManager.ACTION_ACCEPT, PermissionManager.ACTION_ACCEPT);
  permission.setOps(AppOpsManager.OP_BACKGROUND_START_ACTIVITY);
    permission.addFlag(Permission.FLAG_ALWAY_ADD);
   permission.addFlag(Permission.FLAG_NO_ASK);
   sPermissions.add(permission);
}

   MIUI在系统中专门使用ExtraActivityManagerService来控制这些权限的行为。当你应用尝试在后台打开一个界面并且没有授予该权限时,系统就会打印以下日志,并且拦截该行为。

2020-06-09 16:22:42.379 31608-31608/com.kuaiest.video I/Timeline: Timeline: Activity_launch_request time:234147729 intent:Intent { flg=0x10800000 cmp=com.kuaiest.video/.feature.screenlock.TesttAcitivity }
2020-06-09 16:22:42.402 1472-3398/? I/ActivityManager: START u0 {flg=0x10800000 cmp=com.kuaiest.video/.feature.screenlock.TesttAcitivity} from uid 10487
2020-06-09 16:22:42.403 1472-3398/? D/com.android.server.am.ExtraActivityManagerService: MIUILOG- Permission Denied Activity : Intent { flg=0x10800000 cmp=com.kuaiest.video/.feature.screenlock.TesttAcitivity } pkg : com.kuaiest.video uid : 10487 tuid : 10052

绕过方案

   为了绕过该权限,尝试了几种方案如下:

1.前台服务方式

   开始想到这个权限主要是针对后台应用,于是想着在恰当的时机启动个前台服务,然后在前台服务中进行界面弹出操作,实现如下。

    //启动服务
    if (!TestFService.serviceIsLive) {
        // Android 8.0使用startForegroundService在前台启动新服务
        mForegroundService = new Intent(this, TestFService.class);
        mForegroundService.putExtra("Foreground", "This is a foreground service.");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(mForegroundService);
        } else {
            startService(mForegroundService);
        }
    } else {
        Toast.makeText(this, "前台服务正在运行中...", Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtils.d(TAG, "onStartCommand");
        // 标记服务启动
        TestFService.serviceIsLive = true;
        // 数据获取
        String data = intent.getStringExtra("Foreground");
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
        Intent intent1 = new Intent(this, TesttAcitivity.class);
        intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        startActivity(intent1);
        return super.onStartCommand(intent, flags, startId);
    }

   尝试无果,该方式无法绕过这个权限。

2.引导用户开启方式

   这种方式是网上一些笔者分享的经验,但这个方法只是提示用户去权限管理中开启权限,而不是绕过权限,所以该方法也不是很好的解决方案。如果有需要可以看下网上这个方法的介绍——后台弹出界面权限适配方案

3.堆栈调度方式

   这个方法是一个取巧的办法,能够让应用在一些场景下绕过该权限的限制,其实就是利用ams获取到当前后台应用的堆栈,然后将堆栈强行推到前台使应用恢复到前台,从而绕过权限限制弹出对应的界面,具体看以下方法描述。

    private  void isRunningForegroundToApp(Context context, final Class Class) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        //利用系统方法获取当前Task堆栈, 数目可按实际情况来规划,这里只是演示
        List<ActivityManager.RunningTaskInfo> taskInfoList = activityManager.getRunningTasks(20);

       
        for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {
            //遍历找到本应用的 task,并将它切换到前台
            if (taskInfo.baseActivity.getPackageName().equals(context.getPackageName())) {
                Log.d(TAG, "timerTask  pid " + taskInfo.id);
                Log.d(TAG, "timerTask  processName " + taskInfo.topActivity.getPackageName());
                Log.d(TAG, "timerTask  getPackageName " + context.getPackageName());
                activityManager.moveTaskToFront(taskInfo.id, ActivityManager.MOVE_TASK_WITH_HOME);
                Intent intent = new Intent(context, Class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                context.startActivity(intent);
                break;
            }
        }
    }

   目前尝试的方法挺多的,只有上述的几种方式靠谱些,如果有新的方法可以提出来交流,交流。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值