平台:
Android 4.4.2
问题:
GoogleSettings进入“安全—受信任凭据”报错:
E/AndroidRuntime( 2512): Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=android.security.IKeyChainService }
E/AndroidRuntime( 2512): at android.app.ContextImpl.validateServiceIntent(ContextImpl.java:1698)
E/AndroidRuntime( 2512): at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1797)
E/AndroidRuntime( 2512): at android.app.ContextImpl.bindServiceAsUser(ContextImpl.java:1782)
E/AndroidRuntime( 2512): at android.content.ContextWrapper.bindServiceAsUser(ContextWrapper.java:546)
E/AndroidRuntime( 2512): at android.security.KeyChain.bindAsUser(KeyChain.java:478)
E/AndroidRuntime( 2512): at com.android.settings.TrustedCredentialsSettings$AdapterData$AliasLoader.doInBackground(TrustedCredentialsSettings.java:448)
E/AndroidRuntime( 2512): at com.android.settings.TrustedCredentialsSettings$AdapterData$AliasLoader.doInBackground(TrustedCredentialsSettings.java:416)
原因:
KeyChain模块没有集成
解决方式:
1. 检查core.mk,是否有包含KeyChain模块
2. 如果core.mk包含,注意检查lunch的不同产品的mk文件,有没有在PRODUCT_DELETE_APPS中删除了该模块
详细内容:
可以看到问题出在TrustedCredentialsSettings.java这个文件中,定位到该类的AliasLoader,这个内部类继承了AyncTask,在doInBackground中有这样的操作:
KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext,
profile);
// Saving the connection for later use on the certificate dialog.
mKeyChainConnectionByProfileId.put(profileId, keyChainConnection);
IKeyChainService service = keyChainConnection.getService();
...
继续跟进KeyChain.bindAsUser:
public static KeyChainConnection bindAsUser(Context context, UserHandle user)
throws InterruptedException {
...
Intent intent = new Intent(IKeyChainService.class.getName());
ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
intent.setComponent(comp);
boolean isBound = context.bindServiceAsUser(intent,
keyChainServiceConnection,
Context.BIND_AUTO_CREATE,
user);
...
}
这里bind了IKeyChainService,问题出在这个bindServiceAsUser接口上,在CotextImpl.java中实现:
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
return bindServiceCommon(service, conn, flags, user);
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
...
validateServiceIntent(service);
...
}
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));
}
}
}
在校验Intent时抛出异常,说明intent的component为null,这个component在哪里设置的呢?回到KeyChain.java,注意到setComponent的地方:
public static KeyChainConnection bindAsUser(Context context, UserHandle user)
throws InterruptedException {
...
Intent intent = new Intent(IKeyChainService.class.getName());
ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
intent.setComponent(comp);
boolean isBound = context.bindServiceAsUser(intent,
keyChainServiceConnection,
Context.BIND_AUTO_CREATE,
user);
...
}
实际上comp就是IKeyChainService对应的服务,这个服务位于 /packages/apps/KeyChain/src/com/android/keychain/KeyChainService.java文件中,属于应用KeyChain,那么这个Service为啥找不到呢?
KeyChain作为一个系统服务,需要在编译时包含进去,参考了网上的文章,检查了core.mk,是包含了KeyChain模块的,问题陷入僵局了(此时大半天过去了)。然后企业微信亮了起来,一个同事说这个模块被裁减掉了(感谢大佬),裁减原因没查到,在不同产品自己的mk文件中,删除了该模块,所以导致服务不可用。
结论
有个靠谱同事真好