package android.taobao.atlas.runtime.newcomponent.service;
import android.app.IServiceConnection;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.taobao.atlas.runtime.RuntimeVariables;
import android.taobao.atlas.runtime.newcomponent.AdditionalPackageManager;
import android.taobao.atlas.runtime.newcomponent.BridgeUtil;
import android.taobao.atlas.runtime.newcomponent.activity.ActivityBridge;
import android.util.Log;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
/**
* Created by guanjie on 2017/4/5.
*/
public class ServiceBridge {
private static HandlerThread shandlerThread;
private static Handler sServicehandler;
private static ConcurrentHashMap<String,ServiceBridge> sBridges = new ConcurrentHashMap<>();
private IDelegateBinder mRemoteDelegate;
private CountDownLatch mCountDownLatch;
private String processName;
private Intent targetIntent;
HashMap<Intent,IServiceConnection> mActiveServiceInfo = new HashMap<>();
static {
shandlerThread = new HandlerThread("atlas_service_manager");
shandlerThread.start();
sServicehandler = new Handler(shandlerThread.getLooper());
}
private ServiceBridge(String processname) {
this.processName = processname;
}
private static ServiceBridge obtain(String processName) {
if (sBridges.get(processName) == null) {
synchronized (ServiceBridge.class) {
if (sBridges.get(processName) == null) {
ServiceBridge bridge = new ServiceBridge(processName);
sBridges.put(processName,bridge);
}
}
}
return sBridges.get(processName);
}
public IDelegateBinder getRemoteDelegate(){
if(mRemoteDelegate!=null && mRemoteDelegate.asBinder().isBinderAlive()){
return mRemoteDelegate;
}else{
connectDelegateService(processName);
return mRemoteDelegate;
}
}
private synchronized void connectDelegateService(String processName) {
if(mRemoteDelegate!=null && mRemoteDelegate.asBinder().isBinderAlive()){
return ;
}
mCountDownLatch = new CountDownLatch(1);
if(targetIntent==null){
Intent service = new Intent();
String delegateComponentName = BridgeUtil.getBridgeName(BridgeUtil.TYPE_SERVICEBRIDGE,processName);
service.setClassName(RuntimeVariables.androidApplication, delegateComponentName);
targetIntent = service;
}
RuntimeVariables.androidApplication.bindService(targetIntent, mDelegateConnection,
Context.BIND_AUTO_CREATE);
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static ComponentName startService(final Intent service) {
final ServiceInfo info = AdditionalPackageManager.getInstance().getNewComponentInfo(service.getComponent(),ServiceInfo.class);
if(info==null){
Log.e("ServiceBridge","can't find startservice | serviceinfo for intent: "+service.toString());
return null;
}
sServicehandler.post(new Runnable() {
@Override
public void run() {
String processName = info.processName;
try {
ServiceBridge.obtain(processName).getRemoteDelegate().startService(service,info);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
return service.getComponent();
}
public static boolean stopService(final Intent service){
final ServiceInfo info = AdditionalPackageManager.getInstance().getNewComponentInfo(service.getComponent(),ServiceInfo.class);
if(info==null){
Log.e("ServiceBridge","can't stopService | serviceinfo for intent: "+service.toString());
return false;
}
sServicehandler.post(new Runnable() {
@Override
public void run() {
try {
String processName = info.processName;
ServiceBridge.obtain(processName).getRemoteDelegate().stopService(service);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
return true;
}
public static int bindService(final IBinder token, final Intent service, final String resolvedType, final IServiceConnection connection) {
final ServiceInfo info = AdditionalPackageManager.getInstance().getNewComponentInfo(service.getComponent(),ServiceInfo.class);
if(info==null){
Log.e("ServiceBridge","can't bindService | serviceinfo for intent: "+service.toString());
return 0;
}
sServicehandler.post(new Runnable() {
@Override
public void run() {
try {
String processName = info.processName;
ServiceBridge.obtain(processName).mActiveServiceInfo.put(service,connection);
ServiceBridge.obtain(processName).getRemoteDelegate().bindService(service,token,connection);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
return 1;
}
public static boolean unbindService(final IServiceConnection conn) {
String tmp = null;
for (Map.Entry<String,ServiceBridge> entry : sBridges.entrySet()) {
if(entry.getValue().mActiveServiceInfo.containsValue(conn)){
tmp = entry.getKey();
}
}
final String processOfRemoteService = tmp;
if(processOfRemoteService!=null) {
sServicehandler.post(new Runnable() {
@Override
public void run() {
try {
ServiceBridge.obtain(processOfRemoteService).mRemoteDelegate.unbindService(conn);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
return true;
}else{
return false;
}
}
public static void handleActivityStack(final Intent intent, final ActivityInfo info, final ActivityBridge.OnIntentPreparedObserver observer){
sServicehandler.post(new Runnable() {
@Override
public void run() {
try {
Intent result = ServiceBridge.obtain(info.processName).getRemoteDelegate().handleActivityStack(intent,info);
if(observer!=null){
observer.onPrepared(result);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
public static void notifyonReceived(final Intent intent, final ActivityInfo info){
sServicehandler.post(new Runnable() {
@Override
public void run() {
try {
ServiceBridge.obtain(info.processName).getRemoteDelegate().handleReceiver(intent,info);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
/**
* process maybe killed,so active service need be restarted
* @param delegate
*/
private void recoverActiveServie(IDelegateBinder delegate){
if(mActiveServiceInfo.size()>0){
//do recover
Iterator iter = mActiveServiceInfo.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Intent,IServiceConnection> entry = (Map.Entry<Intent,IServiceConnection>) iter.next();
Intent intent = entry.getKey();
IServiceConnection connection = entry.getValue();
if(connection!=null){
//service has created
try {
delegate.bindService(intent,null,connection);
} catch (RemoteException e) {
e.printStackTrace();
}
}else{
try {
delegate.startService(intent,null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
private ServiceConnection mDelegateConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteDelegate = IDelegateBinder.Stub.asInterface(service);
try {
mRemoteDelegate.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
recoverActiveServie(mRemoteDelegate);
mCountDownLatch.countDown();
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mRemoteDelegate.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mRemoteDelegate = null;
connectDelegateService(processName);
}
};
}