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即可。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories