/*----------------------------------------------------------------------------+ *| | *| 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; } }