/*----------------------------------------------------------------------------+
*| |
*| Android's Hooker |
*| |
*+---------------------------------------------------------------------------+
*| Copyright (C) 2011 Georges Bossert and Dimitri Kirchner |
*| This program is free software: you can redistribute it and/or modify |
*| it under the terms of the GNU General Public License as published by |
*| the Free Software Foundation, either version 3 of the License, or |
*| (at your option) any later version. |
*| |
*| This program is distributed in the hope that it will be useful, |
*| but WITHOUT ANY WARRANTY; without even the implied warranty of |
*| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
*| GNU General Public License for more details. |
*| |
*| You should have received a copy of the GNU General Public License |
*| along with this program. If not, see <http://www.gnu.org/licenses/>. |
*+---------------------------------------------------------------------------+
*| @url : http://www.amossys.fr |
*| @contact : android-hooker@amossys.fr |
*| @sponsors : Amossys, http://www.amossys.fr |
*+---------------------------------------------------------------------------+
*/
package com.amossys.hooker.service;
import java.util.LinkedList;
import java.util.Queue;
import com.amossys.hooker.SubstrateMain;
import com.amossys.hooker.common.InterceptEvent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
/**
* @author Georges Bossert
*
*/
public class InstrumentationServiceConnection implements ServiceConnection {
/**
* INTENT collect
*/
private static final String COLLECT_INTENT_NAME = "com.amossys.hooker.COLLECT";
// To communicate with service
Messenger mService = null;
// Flag indicating whether we have called bind on the service
boolean boundToTheService;
private Queue<InterceptEvent> localCacheOfEvents = new LinkedList<InterceptEvent>();
public void sendEvent(InterceptEvent event) {
if (event == null) {
return;
}
if (!this.isConnected()) {
// We are not yet connected to the remote service so we store locally
this.localCacheOfEvents.add(event);
} else {
this.sendEventsInCache();
this.sendEventToService(event);
}
}
private void sendEventsInCache() {
boolean error = false;
while (this.localCacheOfEvents.size() > 0 && !error) {
SubstrateMain.log("Sending previous stored event before current one.");
InterceptEvent cachedEventToSend = this.localCacheOfEvents.poll();
error = !this.sendEventToService(cachedEventToSend);
if (error) {
// re-add the event we just removed if we failed to send it
this.localCacheOfEvents.add(cachedEventToSend);
}
}
}
/**
* @param event
* @return true if we have an error, false otherwise.
*/
private boolean sendEventToService(InterceptEvent event) {
if (event == null) {
return true;
}
boolean result = false;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, InstrumentationService.Event, 0, 0);
Bundle eventToSend = new Bundle();
try {
// Add parcelable object to event
eventToSend.putParcelable("eventkey", event);
msg.setData(eventToSend);
// Send !
mService.send(msg);
} catch (RemoteException e) {
result = true;
SubstrateMain.log("Remote exception when trying to send to service");
e.printStackTrace();
}
return result;
}
public boolean isConnected() {
return (boundToTheService && mService != null);
}
/**
* Build an intent and start the service.
*/
public void doBindService(Context appContext) {
if (appContext == null) {
SubstrateMain
.log("Oups, impossible to bind to the service since no appContext has been retrieved!");
return;
}
if (!boundToTheService) {
boundToTheService =
appContext.bindService(new Intent(COLLECT_INTENT_NAME), this, Context.BIND_AUTO_CREATE);
SubstrateMain.log("Binding to the service is " + boundToTheService);
}
}
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service
mService = new Messenger(service);
boundToTheService = true;
try {
Message msg = Message.obtain(null, InstrumentationService.ConnectToService);
mService.send(msg);
} catch (RemoteException e) {
// service has crashed, nothing to do...
SubstrateMain.log("<!> Thats a very bad news: service has crashed", e);
}
/**
* Send previously stored events
*/
this.sendEventsInCache();
}
@Override
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
boundToTheService = false;
SubstrateMain.log("Hooker disconnected from service.");
}
/**
* @return the boundToTheService
*/
public boolean isBoundToTheService() {
return boundToTheService;
}
}