您的位置:首页 > 移动开发 > Android开发

Android Lolipop 屏蔽隐式Intent检查引发的错误

2016-01-14 10:50 591 查看

前言

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自己也有问题啊。。。。。。

分析

隐式
Intent
验证机制生效,系统正常。

隐式
Intent
验证机制被屏蔽,系统
system_server
进程报错,定位错误位于以上启动Service代码。

绑定Service出错时,
KeyChain
抛出
AssertionError
异常。

AssertionError
异常直接继承与
Error
类。方法调用者
DevicePolicyManagerService
并未捕获此异常。

因此,导致
DevicePolicyManagerService
执行出错,又该Service是系统核心服务运行于
system_server
进程,则该进程挂掉,重启。

如果打开隐式
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
没有找到:

IKeyChainService
com.android.keychain
提供的服务,属于
KeyChain
这个MODEL。经查,发现是我们的ROM中没有加入此模块。与AOSP源码对比,更改build/target/product/core.mk,加入
KeyChain
即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android debug 隐式Intent