前言

Android Lolipoop以上版本屏蔽了隐式Intent启动Service,以前我们可以这么做:

Intent intent = new Intent("action.of.service");context.startService(intent);

现在这样行不通了,会报错!根据Google指示,我们最少应该给Intent里面加上Service的包名:

Intent intent = new Intent("action.of.service");intent.setPackage("packageName.of.service");context.startService(intent);

好的,这样修改就妥妥的了。

偷懒

我们动动歪脑筋,在手握源码的情况下,为何不直接屏蔽隐式Intent验证机制呢?回到KitKat年代,一片和谐,多么美好。

过程

说干就干。start/stop/bind service时的Intent验证机制位于ContextImpl.java中。
可以看到也就是抛个异常,别的啥也没干嘛。于是我们动手:

private void validateServiceIntent(Intent service) {    if (service.getComponent() == null && service.getPackage() == null) {        if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {            IllegalArgumentException ex = new IllegalArgumentException(                    "Service Intent must be explicit: " + service);            throw ex; <--- 我们轻松将其注释        } else {            Log.w(TAG, "Implicit intents with startService are not safe: " + service                    + " " + Debug.getCallers(2, 3));        }    }}

之后新编framework.jar替换原来的,重启。

问题

一切并未按预期发展,更改后的系统开机时不断重启,检查log,发现出错的调用堆栈与DevicePolicyManagerService.java和KeyChain.java两个文件有关。

DevicePolicyManagerService

代码接收开机广播,启动一个AsyncTask。

BroadcastReceiver mReceiver = new BroadcastReceiver() {    @Override    public void onReceive(Context context, Intent intent) {    ......        if (Intent.ACTION_BOOT_COMPLETED.equals(action)                || KeyChain.ACTION_STORAGE_CHANGED.equals(action)) {            new MonitoringCertNotificationTask().execute(intent);        }    }}

AsyncTask中做了一些工作,其中会调用KeyChain进行绑定Service操作。

private void manageNotification(UserHandle userHandle) {    ......    // Call out to KeyChain to check for user-added CAs    boolean hasCert = false;    try {        KeyChainConnection kcs = KeyChain.bindAsUser(mContext, userHandle);        try {            if (!kcs.getService().getUserCaAliases().getList().isEmpty()) {                hasCert = true;            }        } catch (RemoteException e) {            Log.e(LOG_TAG, "Could not connect to KeyChain service", e);        } finally {            kcs.close();        }    } catch (InterruptedException e) {        Thread.currentThread().interrupt();    } catch (RuntimeException e) {        Log.e(LOG_TAG, "Could not connect to KeyChain service", e);    }    ......}

KeyChain

public static KeyChainConnection bindAsUser(Context context, UserHandle user)        throws InterruptedException {    if (context == null) {        throw new NullPointerException("context == null");    }    ......    Intent intent = new Intent(IKeyChainService.class.getName());    ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);    intent.setComponent(comp); <---- 出错时,这个ComponentName为空    boolean isBound = context.bindServiceAsUser(intent,                                                keyChainServiceConnection,                                                Context.BIND_AUTO_CREATE,                                                user);    if (!isBound) {        throw new AssertionError("could not bind to KeyChainService");    }    return new KeyChainConnection(context, keyChainServiceConnection, q.take());}

可以看到KeyChain中视图绑定一个Service,且在出错的调用链上Component为空,实际上是隐式Intent绑定Service。
这是Google自己也有问题啊。。。。。。

分析

  1. 隐式Intent验证机制生效,系统正常。
  2. 隐式Intent验证机制被屏蔽,系统system_server进程报错,定位错误位于以上启动Service代码。
  3. 绑定Service出错时,KeyChain抛出AssertionError异常。
  4. AssertionError异常直接继承与Error类。方法调用者DevicePolicyManagerService并未捕获此异常。
  5. 因此,导致DevicePolicyManagerService执行出错,又该Service是系统核心服务运行于system_server进程,则该进程挂掉,重启。
  6. 如果打开隐式Intent验证机制,则抛出IllegalArgumentException,继承于RuntimeException,被DevicePolicyManagerService捕获,不会导致挂掉。系统继续往下运行。

解决

加一行catch即可,搞定。

        private void manageNotification(UserHandle userHandle) {            ......            // Call out to KeyChain to check for user-added CAs            boolean hasCert = false;            try {                KeyChainConnection kcs = KeyChain.bindAsUser(mContext, userHandle);                try {                    if (!kcs.getService().getUserCaAliases().getList().isEmpty()) {                        hasCert = true;                    }                } catch (RemoteException e) {                    Log.e(LOG_TAG, "Could not connect to KeyChain service", e);                } finally {                    kcs.close();                }            } catch (InterruptedException e) {                Thread.currentThread().interrupt();            } catch (RuntimeException e) {                Log.e(LOG_TAG, "Could not connect to KeyChain service", e);            } catch (AssertionError e) { <--- 万恶的异常。                Log.e(LOG_TAG, "Could not connect to KeyChain service", e);            }            ......        }

存疑

个人认为,根本原因应该是IKeyChainService这个Service没有找到,导致了后续问题。待查到为什么找不到这玩意后再来更新。

补充

接上文,已经发现了为何IKeyChainService没有找到:
IKeyChainServicecom.android.keychain提供的服务,属于KeyChain这个MODEL。经查,发现是我们的ROM中没有加入此模块。与AOSP源码对比,更改build/target/product/core.mk,加入KeyChain即可。

更多相关文章

  1. Android(安卓)launcher3 -- launcher3源码4
  2. Android(安卓)7.0 Launcher3的启动和加载流程分析----转载
  3. Android(安卓)编译出错版本匹配问题解决办法
  4. Android(安卓)studio更新自身或者SDK以后出错问题解决
  5. 编译android sdk时的出错out/host/linux-x86/obj/STATIC_...
  6. Android通过Xutils注解实例化以及事件绑定
  7. ECLIPSE android 布局页面文件出错故障排除Exception raised dur
  8. Android(安卓)Service的绑定过程
  9. android之AIDL跨进程通信详解 (四)AIDL中RemoteCallbackList的使

随机推荐

  1. MySQL查询语句过程和EXPLAIN语句基本概念
  2. MySQL简单了解“order by”是怎么工作的
  3. Windows环境下的MYSQL5.7配置文件定位图
  4. mysql 8.0.16 winx64.zip安装配置方法图
  5. mysql 8.0.16 压缩包安装配置方法图文教
  6. 你需要理解的关于MySQL的锁知识
  7. win10下mysql 8.0.16 winx64安装图文最新
  8. mysql installer community 8.0.16.0安装
  9. Windows10下mysql 8.0.16 安装配置方法图
  10. win10下mysql 8.0.16 winx64安装配置方法