提前概括:
Event传递数据时,处理不当极易发生Double Free,或者访问无效指针。而且非常难查,是隐藏的很深的, 主要原因是: BREW有自动释放应用上下文环境下分配的内存的默认行为!!!!
方法1:A创建数据(普通指针),并SendEvent给B:
虽然考虑以后的兼容性,仍然不建议使用。但是,目前而言,接收方进行Memory的Copy后使用,仍然是安全的。
方法2:A创建Interface Instance,并SendEvent给B:
传统的认识是:只要接收方进行AddRef,就没有问题了。但是,如果Instance的创建是在应用环境下,那么,BREW还是会自动回收的。比如:
A创建了InterfaceA Instance,并通过SendEvent传给B,B接收时,使用了AddRef。接着,A退出,此时,A的FreeData中的RELEASEIF(InterfaceA)并不会释放InterfaceA Instance,只是Ref减1。但是,在A退出后,BREW会自动释放InterfaceA的相关内存,因为InterfaceA是在A的上下文环境创建的。那么,此后B使用的Interface A Instance将是无效的!
方法3:A 创建数据(普通指针),A PostEvent to B:
假设A应用Malloc了一块Buffer,通过PostEvent传递给B应用, 假设为了其一直有效,A应用不主动释放它,而是由B应用来释放。
case 1: B应用收到事件前,A应用已经退出。 此时虽然A应用并没有主动释放Buffer,但是,BREW有自动内存回收的行为,会在A应用退出时,自动释放该Buffer。所以,B此时取到的Buffer是无效的,可能引起内存非法操作!!
case 2: B应用收到事件时,A应用没有退出,但是,在B应用释放该Buffer前,A应用退出了。此时,BREW仍然会当A退出时,自动释放该Buffer。 这样,B使用的将是无效的内存,且当B再释放该Buffer时,就会导致Corrupt Node。
case 3: B应用收到事件时,A应用没有退出,且B应用释放该Buffer后,A应用才退出,此时才是没有任何问题的,因为B应用已经释放该Buffer,所以BREW不会当A退出时再自动释放了,也就不会存在问题了。
总结: 对于方法3,从理论上2/3的概率会导致Memory非法操作,危险至极!! 有兴趣者,可以去模拟case 1, case2 试试看,是不是有问题。
方法4:A创建Interface Instance,并PostEvent给B,B接收Instance后调用AddRef:
这从理论上就不可能避免危险,同方法2,并且增加了异步的不可靠性。
方法5:A 调用 B接口,B接口 Send/Post Event to B应用:
按照Barton.Li的叙述,很多模块的做法是, A 调用 B接口传递Buffer, B接口备份该Buffer(这样A接口就可以释放该Buffer了),B接口将备份的Buffer传递给B应用,B应用处理完后再释放该Buffer。
这里需要注意的是,A调用B接口传递的Buffer和B接口内部备份的Buffer,都是在A应用的环境下分配的。 所以BREW将在A应用退出时,自动回收这两块Buffer(如果没有释放的话)。
case1:A调用B接口传递Buffer,B接口备份该Buffer,并Post该Buffer给B应用,假设,B应用收到事件之前,A应用退出了,那么,A传递的Buffer和B备份的Buffer都会被释放,前者是A主动释放的,后者是BREW自动释放的。 这样,B应用得到的Buffer是无效的!!
case2:A调用接口传递Buffer,B接口备份Buffer,并Post该Buffer给B应用,假设B应用收到事件之前,A应用并没有退出,但是,B应用释放Buffer之前,A应用退出了。 这样,当A应用退出时,A传递的Buffer和B备份的Buffer都会被释放,前者是A主动释放的,后者是BREW自动释放的。这样,B将使用无效Buffer,且当B应用再次释放Buffer时,就会出现Double Free
case3:A调用接口传递Buffer,B接口备份Buffer,并Post该Buffer给B应用,假设B应用收到事件之前,A应用并没有退出,并且B应用释放Buffer之后,A应用才退出了。 这样,当A应用退出时,BREW不会再主动释放B接口备份的Buffer了,也就没有问题了。
总结:对于方法5, 仍然从理论上2/3的概率会导致Memory非法操作,危险至极!! 有兴趣者,可以去模拟case 1, case2 试试看,是不是有问题。
再次总结:
以上的危险性,是由于BREW的默认内存自动回收行为和PostEvent的异步不安全性导致的。 从理论上来说,如果使用PostEvent,就不可能保证数据的安全性。所以,在使用PostEvent时,应该禁止投递指针!!!!!此外,由于Interface的不可Copy性,所以,对于Interface,即便是SendEvent也是不允许的!
解决方法:
1. 按照Qualcomm的建议,尽量不使用Event投递指针。
2. 对于Non-Interface Point,如果想使用PostEvent投递,并且没有任何上述的问题,那么就使用PostURL吧!!!!! 最主要的有两点优点: 利用MIME机制进行应用间解耦;安全的异步投递数据(借助Base64编码投递任意Buffer)。
3. 另外有一种方式,可以使得PostEvent时达到数据的安全性。 但是不十分规范,因为应用环境下应该尽量避免分配系统环境下的内存:具体为:
需要通过PostEvent投递的Buffer,在系统上下文环境下分配,并由接收者释放。 这样,即便接收者收到事件前发送者已经退出,或者接收者释放Buffer前,发送者已经退出,也不会有任何问题了。 因为该Buffer是在系统环境下分配的,所以应用退出时,BREW不会自动释放它。 所以就不会出现 指针无效,或者内存泄漏,或者Double Free了。
其实Post/SendURL的内部,备份数据时,就是在系统环境下分配的内存。
4.禁止投递Interface指针。虽然通过在系统环境下创建Interface可能可以避免,但是为了降低复杂度和潜在危险性,禁止是最好的方法。