按Home键后,后台服务启动Activity要延时5s左右才会启动原理分析(基于Android4.4.3源码分析)

最近车载项目中碰到一个问题:在酷我音乐Activity界面,按下Home按键后,点击音源硬按键,中间件后台服务接收到音源硬按键消息后,会启动酷我音乐界面Activity,但是Activity是在延时5s后才会显示;而点击导航硬按键,中间件后台服务接收到导航硬按键消息后,会启动导航APP界面,此时Activity会立刻显示出来。那问题来了,同样的都是由后台服务启动,按home键后,为什么导航界面会立刻显示出来,领导把这件事情交给我来负责研究。

一、事件分发前的拦截过程

详细的源码分析,网上已经有了相关的资料,在这里我就不重复分析(详细见https://m.2cto.com/kf/201611/563390.html)。

在这里只是分析一下Home事件在分发前的关键拦截过程:

PhoneWindowManager.java类

public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
       ........
        if (keyCode == KeyEvent.KEYCODE_HOME) {
            if (!down) {
                .........
                launchHomeFromHotKey();
                return -1;

            }

        ........

        }

    }

void launchHomeFromHotKey() {
        .....
                        try {
                            ActivityManagerNative.getDefault().stopAppSwitches();
                        } catch (RemoteException e) {
                        }
         .....

 }

最后会走到ActivityManagerService的stopAppSwitches()方法

static final long APP_SWITCH_DELAY_TIME = 5*1000;

public void stopAppSwitches() {
        if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires permission "
                    + android.Manifest.permission.STOP_APP_SWITCHES);
        }
        
        synchronized(this) {
            mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
                    + APP_SWITCH_DELAY_TIME;
            mDidAppSwitch = false;
            mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
            Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
            mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);
        }

    }

  二、startActivity的启动流程

   关于启动流程,网上已经有很多的相关资料,在这里我们只分析ActivityStackSupervisor类的startActivityLocked的方法,在此方法内我们可以发现在执行下个流程的startActivityUncheckedLocked方法前,会有个条件判断,如下:

final ActivityStack stack = getFocusedStack();
        if (stack.mResumedActivity == null
                || stack.mResumedActivity.info.applicationInfo.uid != callingUid) {
            if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
                PendingActivityLaunch pal =
                        new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
                mService.mPendingActivityLaunches.add(pal);
                setDismissKeyguard(false);
                ActivityOptions.abort(options);
                return ActivityManager.START_SWITCHES_CANCELED;
            }

        }

由于是后台服务启动的Activity,所以stack.mResumedActivity.info.applicationInfo.uid != callingUid的值肯定为true,其中callingUid为后台服务的UID,stack.mResumedActivity.info.applicationInfo.uid为当前前台显示Activity的UID。

继续分析ActivityManagerService类的checkAppSwitchAllowedLocked的方法:

int checkComponentPermission(String permission, int pid, int uid,
            int owningUid, boolean exported) {
        ...
        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported);
    }

最后分析ActivityManager类的checkComponentPermission的方法。

public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        if (uid == 0 || uid == Process.SYSTEM_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }

        ....

        try {
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            Slog.e(TAG, "PackageManager is dead?!?", e);
        }
        return PackageManager.PERMISSION_DENIED;
    }

由上可以发现后台服务的UID如果为Process.SYSTEM_UID,或者启动的Activity具有android.Manifest.permission.STOP_APP_SWITCHES权限,就不会进入延时5s启动Activity流程,而是进入startActivityUncheckedLocked方法正常启动Activity。

三、原因分析

经过一、二的分析,再在关键地方加入日志,把callingUid的值打印出来,最后发现后台服务启动导航的时候,callingUid的值为1000,与Process.SYSTEM_UID相等,而启动酷我音乐,callingUid的值为50122,进入到延时5s启动Activity的流程。

经过咨询中间件模块负责人,中间件服务的UID为Process.SYSTEM_UID,且中间件服务启动导航应用和启动酷我音乐的方式是有所区别的,启动导航应用是通过中间件的上下文调用startActivity来启动的,而启动酷我音乐是通过绑定酷我服务调用aidl接口来启动的。

综上所述,由于酷我音乐属于第三方应用,不具有系统权限,不能赋予android.Manifest.permission.STOP_APP_SWITCHES的权限,那只能改变中间件服务启动酷我的方法来解决酷我音乐启动延时5s才会显示的问题。

猜你喜欢

转载自blog.csdn.net/hsaekong/article/details/81031239