Android IPC/Binder Framework 实战(下)
Android IPC 框架 实战
Async Binder IPC 实战
AsyncStockQuoteAidl
importcom.stockquote.aidl.StockQuoteRequest;
importcom.stockquote.aidl.IStockQuoteServiceResponseListener;
onewayinterfaceIStockQuoteService{
voidretrieveStockQuote(inStockQuoteRequestrequest,inIStockQuoteServiceResponseListenerlistener);
}
importcom.stockquote.aidl.StockQuoteResponse;
onewayinterfaceIStockQuoteServiceResponseListener{
voidonResponse(inStockQuoteResponseresponse);
}
AsyncStockQuoteServer
publicclassStockQuoteServiceImplextendsIStockQuoteService.Stub{
@Override
publicvoidretrieveStockQuote(StockQuoteRequestrequest,IStockQuoteServiceResponseListenerlistener)throwsRemoteException{
String[]stockIdArray=null;
if(request.getType()==StockQuoteRequest.Type.STOCKQUOTE_MULTIPLE){
stockIdArray=request.getStockIdArray();
}else{
return;
}
StockQuotestockQuote=StockNetResourceManager.getInstance()
.setUpStockQuotes(stockIdArray);
listener.onResponse(newStockQuoteResponse(stockQuote));
}
}
AsyncStockQuoteClient
publicclassMainActivityextendsActivity
implementsServiceConnection{
privatestaticfinalStringTAG="MainActivity";
privatestaticfinalintRESPONSE_MESSAGE_ID=1;
privateProgressDialogmDialog;
privateTextViewmNameTv;
privateTextViewmQuoteTv;
privateIStockQuoteServicemService;
privatefinalHandlerresponseHandler=newHandler(){
@Override
publicvoidhandleMessage(Messagemessage){
switch(message.what){
caseRESPONSE_MESSAGE_ID:
mNameTv.setText(((StockQuote)message.obj).getStockName());
mQuoteTv.setText(((StockQuote)message.obj).getStockPrice());
mDialog.dismiss();
break;
}
}
};
privatefinalIStockQuoteServiceResponseListenerresponseListener
=newIStockQuoteServiceResponseListener.Stub(){
// this method is executed on one of the pooled binder threads
@Override
publicvoidonResponse(StockQuoteResponseresponse)
throwsRemoteException{
Messagemessage=responseHandler
.obtainMessage(RESPONSE_MESSAGE_ID,response.getResult());
responseHandler.sendMessage(message);
}
};
@Override
protectedvoidonCreate(BundlesavedBundle){
super.onCreate(savedBundle);
setContentView(R.layout.main);
mNameTv=(TextView)findViewById(R.id.name);
mQuoteTv=(TextView)findViewById(R.id.quote);
}
@Override
protectedvoidonResume(){
super.onResume();
if(!super.bindService(newIntent(IStockQuoteService.class.getName()),
this,BIND_AUTO_CREATE)){
}
}
@Override
protectedvoidonPause(){
super.onPause();
super.unbindService(this);
}
publicvoidonServiceConnected(ComponentNamename,IBinderservice){
mService=IStockQuoteService.Stub.asInterface(service);
setText();
}
publicvoidonServiceDisconnected(ComponentNamename){
mService=null;
}
privatevoidsetText(){
finalStockQuoteRequestrequest=newStockQuoteRequest(null,
newString[]{"002024"},StockQuoteRequest.Type.STOCKQUOTE_MULTIPLE);
mDialog=ProgressDialog.show(this,"",
"正在获取...",true);
try{
mService.retrieveStockQuote(request,responseListener);
}catch(RemoteExceptione){
mDialog.dismiss();
return;
}
}
}
Binder Security
The binder driver allows only a single CONTEXT_MGR (i.e. servicemanager) to register
…
staticlongbinder_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg)
{
…
switch(cmd){
…
caseBINDER_SET_CONTEXT_MGR:
if(binder_context_mgr_node!=NULL){
printk(KERN_ERR"binder: BINDER_SET_CONTEXT_MGR already set\n");
ret=-EBUSY;
gotoerr;
}
…
binder_context_mgr_node=binder_new_node(proc,NULL,NULL);
…
}
…
…
Each binder transaction caries in it the UID and PID of the sender, which we can easily access:
android.os.Binder.getCallingPid()
android.os.Binder.getCallingUid()
一旦我们知道了对方的 UID,我们就能得到对方的 app 信息:PackageManager.getPackagesForUid(int uid)
一旦我们知道了对方的app,我们就能得到对方的权限信息:PackageManager.getPackageInfo(String packageName, int flags) (with the PackageManager.GET_PERMISSIONS flag)
但有比这更容易的方法来获知 app 是否拥有某种权限:
Context.checkCallingOrSelfPermission(String permission),该方法在对方进程拥有某权限时会返回 PackageManager.PERMISSION_GRANTED,否则 PackageManager.PERMISSION_DENIED
Context.enforceCallingPermission(String permission, String message) ,若对方没有某权限,则直接抛出 SecurityException
实战
SecurityStockQuoteService
StockQuote Permissions
android:name="com.stockquote.service.STOCKQUOTE_PERMISSIONS"
android:label="@string/stockquote_permissions_group_label"/>
添加 permission
…
use stockquote service
applications with this permissions get stockquote results for free
…
…
android:name="com.stockquote.service.USE_STOCKQUOTSE_SERVICE"
android:description="@string/use_stockquote_service_permission_description"
android:label="@string/use_stockquote_service_permission_label"
android:permissionGroup="com.stockquote.service.STOCKQUOTPE_PERMISSIONS"
android:protectionLevel="dangerous"/>
…
android:name=".StockQuoteService"
android:permission="com.stockquote.service.USE_STOCKQUOTSE_SERVICE">
…
SecurityStockQuoteClient
…
…
动态获知权限
publicclassStockQuoteServiceImplextendsIStockQuoteService.Stub{
…
privatefinalContextcontext;
publicStockQuoteServiceImpl(Contextcontext){
this.context=context;
}
privatelongcheckN(longn){
if(n>10){
this.context.enforceCallingOrSelfPermission(
Manifest.permission.USE_SLOW_STOCKQUOTSE_SERVICE,"Go away!");
}
returnn;
}
…
}
其他 Binder 特性
询问远程对象是否存活,isBinderAlive() 和 pingBinder()
获得远程对象终止的通知,linkToDeath(IBinder.DeathRecipient recipient, int flags)
publicclassLocationManagerServiceextendsILocationManager.StubimplementsRunnable{
…
privateReceivergetReceiver(ILocationListenerlistener){
IBinderbinder=listener.asBinder();
Receiverreceiver=mReceivers.get(binder);
if(receiver==null){
receiver=newReceiver(listener);
…
receiver.getListener().asBinder().linkToDeath(receiver,0);
…
}
returnreceiver;
}
privatefinalclassReceiverimplementsIBinder.DeathRecipient,PendingIntent.OnFinished{
finalILocationListenermListener;
…
Receiver(ILocationListenerlistener){
mListener=listener;
…
}
…
publicvoidbinderDied(){
…
removeUpdatesLocked(this);
}
…
}
…
}
Binder driver reports various stats on active/failed transactions via
/proc/binder/
/proc/binder/failed_transaction_log
/proc/binder/state
/proc/binder/stats
/proc/binder/transaction_log
/proc/binder/transaction
/proc/binder/proc/