package com.tws.plugin.servicemanager;
import java.lang.reflect.Proxy;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import com.tws.plugin.servicemanager.compat.BundleCompat;
import com.tws.plugin.servicemanager.local.ServicePool;
/**
* @author yongchen
*
* 利用ContentProvider实现同步跨进程调用,如果contentprovider所在进程退出,
* 其他服务进程注册的binder和service信息会丢失
*/
public class ServiceProvider extends ContentProvider {
public static final String REPORT_BINDER = "report_binder";
public static final String PUBLISH_SERVICE = "publish_service";
public static final String PUBLISH_SERVICE_BINDER = "publish_service_binder";
public static final String UNPUBLISH_SERVICE = "unpublish_service";
public static final String CALL_SERVICE = "call_service";
public static final String QUERY_SERVICE = "query_service";
public static final String QUERY_SERVICE_RESULT_IS_IN_PROVIDIDER_PROCESS = "query_service_result_is_in_provider_process";
public static final String QUERY_SERVICE_RESULT_BINDER = "query_service_result_binder";
public static final String QUERY_SERVICE_RESULT_DESCRIPTOR = "query_service_result_desciptor";
public static final String QUERY_INTERFACE = "query_interface";
public static final String QUERY_INTERFACE_RESULT = "query_interface_result";
public static final String PID = "pid";
public static final String BINDER = "binder";
public static final String NAME = "name";
public static final String INTERFACE = "interface";
private static Uri CONTENT_URI;
// 服务名:进程ID
private static ConcurrentHashMap<String, Recorder> allServiceList = new ConcurrentHashMap<String, Recorder>();
// 进程ID:进程Binder
private static ConcurrentHashMap<Integer, IBinder> processBinder = new ConcurrentHashMap<Integer, IBinder>();
public static Uri buildUri() {
if (CONTENT_URI == null) {
CONTENT_URI = Uri.parse("content://" + ServiceManager.sApplication.getPackageName() + ".svcmgr/call");
}
return CONTENT_URI;
}
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (Build.VERSION.SDK_INT >= 19) {
Log.d("call", "callingPackage = " + getCallingPackage());
}
Log.d("call", "Thead : id = " + Thread.currentThread().getId() + ", name = " + Thread.currentThread().getName() + ", method = " + method + ", arg = "
+ arg);
if (method.equals(REPORT_BINDER)) {
final int pid = extras.getInt(PID);
IBinder iBinder = BundleCompat.getBinder(extras, BINDER);
processBinder.put(pid, iBinder);
try {
iBinder.linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
removeAllRecordorForPid(pid);
}
}, 0);
} catch (RemoteException e) {
e.printStackTrace();
processBinder.remove(pid);
}
} else if (method.equals(PUBLISH_SERVICE)) {
String serviceName = arg;
int pid = extras.getInt(PID);
String interfaceClass = extras.getString(INTERFACE);
IBinder binder = processBinder.get(pid);
if (binder != null && binder.isBinderAlive()) {
Recorder recorder = new Recorder();
recorder.pid = pid;
recorder.interfaceClass = interfaceClass;
allServiceList.put(serviceName, recorder);
} else {
allServiceList.remove(pid);
}
return null;
} else if (method.equals(UNPUBLISH_SERVICE)) {
int pid = extras.getInt(PID);
String name = extras.getString(NAME);
if (TextUtils.isEmpty(name)) {
removeAllRecordorForPid(pid);
} else {
allServiceList.remove(name);
notifyClient(name);
}
return null;
} else if (method.equals(CALL_SERVICE)) {
return MethodRouter.routerToInstance(extras);
} else if (method.equals(QUERY_INTERFACE)) {
Bundle bundle = new Bundle();
Recorder recorder = allServiceList.get(arg);
if (recorder != null) {
bundle.putString(QUERY_INTERFACE_RESULT, recorder.interfaceClass);
}
return bundle;
} else if (method.equals(QUERY_SERVICE)) {
String serviceName = arg;
if (allServiceList.containsKey(serviceName)) {
Object instance = ServicePool.getService(serviceName);
Bundle bundle = new Bundle();
if (instance != null && !Proxy.isProxyClass(instance.getClass())) {
bundle.putBoolean(QUERY_SERVICE_RESULT_IS_IN_PROVIDIDER_PROCESS, true);
return bundle;
} else {
Recorder recorder = allServiceList.get(serviceName);
if (recorder != null) {
IBinder iBinder = processBinder.get(recorder.pid);
if (iBinder != null && iBinder.isBinderAlive()) {
bundle.putBoolean(QUERY_SERVICE_RESULT_IS_IN_PROVIDIDER_PROCESS, false);
bundle.putString(QUERY_SERVICE_RESULT_DESCRIPTOR, ProcessBinder.class.getName() + "_" + recorder.pid);
BundleCompat.putBinder(bundle, QUERY_SERVICE_RESULT_BINDER, iBinder);
return bundle;
}
}
return null;
}
}
}
return null;
}
private void removeAllRecordorForPid(int pid) {
Log.w("ServiceProvider", "remove all service recordor for pid" + pid);
// 服务提供方进程挂了,或者服务提供方进程主动通知清理服务, 则先清理服务注册表, 再通知所有客户端清理自己的本地缓存
processBinder.remove(pid);
Iterator<Map.Entry<String, Recorder>> iterator = allServiceList.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Recorder> entry = iterator.next();
if (entry.getValue().pid.equals(pid)) {
iterator.remove();
notifyClient(entry.getKey());
}
}
}
private void notifyClient(String name) {
// 通知持有服务的客户端清理缓存
Intent intent = new Intent(ServiceManager.ACTION_SERVICE_DIE_OR_CLEAR);
intent.putExtra(NAME, name);
ServiceManager.sApplication.sendBroadcast(intent);
}
public static class Recorder {
public Integer pid;
public String interfaceClass;
}
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// doNothing
return null;
}
@Override
public String getType(Uri uri) {
// doNothing
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// doNothing
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// doNothing
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// doNothing
return 0;
}
}