Android R有意思(坑爹)的权限问题

背景

最近遇到一个STK测试case的问题,在Android Q平台上一直是验证pass的,但是在Android R上测试fail:
测试case要求不显示dialog,但是实际行为是显示dialog的,从逻辑上看有两处代码调用launchTextDialog()方法,为了精确定位在这里加了log,发现是因为isScreenIdle()方法为true导致进入内部else逻辑

分析

闲话少说,上代码:

if (!(msg.isHighPriority || mStkContext[slotId].mMenuIsVisible
        || mStkContext[slotId].mDisplayTextDlgIsVisibile || isTopOfStack())) {
    if(!isScreenIdle()) {
        CatLog.d(LOG_TAG, "Screen is not idle");
        sendScreenBusyResponse(slotId);
    } else {
        launchTextDialog(slotId);
    }
} else {
    launchTextDialog(slotId);
}

launchTextDialog()方法主要操作是为了弹出一个dialog,这里不再详细说明

上代码:

/* package */ boolean isScreenIdle() {
        ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        List<RunningTaskInfo> tasks = am.getRunningTasks(1);
        if (tasks == null || tasks.isEmpty()) {
            return false;
        }

        String top = tasks.get(0).topActivity.getPackageName();
        if (top == null) {
            return false;
        }

        // We can assume that the screen is idle if the home application is in the foreground.
        final Intent intent = new Intent(Intent.ACTION_MAIN, null);
        intent.addCategory(Intent.CATEGORY_HOME);

        ResolveInfo info = getPackageManager().resolveActivity(intent,
                PackageManager.MATCH_DEFAULT_ONLY);
        if (info != null) {
            if (top.equals(info.activityInfo.packageName)) {
                return true;
            }
        }

        return false;
    }

显而易见,只有

	if (top.equals(info.activityInfo.packageName)) {
        return true;
    }

此时,才会返回true,那么很简单,打个log看下top和info.activityInfo.packageName()分别是什么不就可以了:

log如下:

01-01 15:06:41.696  2853 14373 D CAT     : StkAppService: this top is: com.tcl.android.launcher
01-01 15:06:41.701  2853 14373 D CAT     : StkAppService: this info is: com.tcl.android.launcher,  intent is: Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] }

top和info都是launch,此时有些懵逼,我此时界面切到的是camera,topActivity应该是camera,不应该是launch啊,为了证实这点我又抓了下dump

adb shell dumpsys activity > dump.txt

抓出来topActivity确实是camera,那就奇怪了,所以这时候看了下代码获取topActivity的方法:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (tasks == null || tasks.isEmpty()) {
    return false;
}
String top = tasks.get(0).topActivity.getPackageName();

ActivityManager.getRunningTask()这个方法从Android L上就已经废弃了,Google是这样描述的

     * @deprecated As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method
     * is no longer available to third party
     * applications: the introduction of document-centric recents means
     * it can leak person information to the caller.  For backwards compatibility,
     * it will still return a small subset of its data: at least the caller's
     * own tasks, and possibly some other tasks
     * such as home that are known to not be sensitive

从注释上可以看到,三方apk不允许使用这个方法了,但是问题在于从Android L上就不允许三方应用使用了,但是Android Q上为什么会是pass的呢?这时候就想到了权限问题。

查代码看了下权限这边的问题,发现这样的调用顺序:

ActivityManager.getRunningTasks() > ActivityTaskManagerService.getTasks() > ActivityTaskManagerService().getFilteredTasks() > isGetTasksAllowed()

在 isGetTasksAllowed()方法中有一个权限检查的代码:

        boolean allowed = checkGetTasksPermission(android.Manifest.permission.REAL_GET_TASKS,
                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
        if (!allowed) {
            if (checkGetTasksPermission(android.Manifest.permission.GET_TASKS,
                    callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
                // Temporary compatibility: some existing apps on the system image may
                // still be requesting the old permission and not switched to the new
                // one; if so, we'll still allow them full access.  This means we need
                // to see if they are holding the old permission and are a system app.
                try {
                    if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
                        allowed = true;
                        if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
                                + " is using old GET_TASKS but privileged; allowing");
                    }
                } catch (RemoteException e) {
                }
            }
            if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
                    + " does not hold REAL_GET_TASKS; limiting output");
        }
        return allowed;

首先会检查有没有 REAL_GET_TASKS 权限,如果有,直接返回true,如果没有则会检查 GET_TASKS 权限,查了下STK的权限设置,确实是没有这个权限的,那为什么Android Q上是pass的呢?

查了下Android R上关于STK的提交,原来Google的一个NC程序员给去掉了,本来想重新提交到Google的,发现这个NC又给加回来了…

解决

很简单,加上 REAL_GET_TASKS 权限就ok了,但是只有系统应用才可以,三方应用建议还是加上 GET_TASKS 权限。

<uses-permission android:name="android.permission.REAL_GET_TASKS" />

猜你喜欢

转载自blog.csdn.net/qq_37213843/article/details/107872185
今日推荐