org.durka.hallmonitor.CoreService.java Source code

Java tutorial

Introduction

Here is the source code for org.durka.hallmonitor.CoreService.java

Source

/* Copyright 2013 Alex Burka
    
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.durka.hallmonitor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.ActivityManager;
import android.app.Service;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.KeyEvent;
import eu.chainfire.libsuperuser.Shell;

public class CoreService extends Service {
    private final String LOG_TAG = "Hall.CS";

    private CoreStateManager mStateManager;

    private Looper mTouchCoverLooper;
    private TouchCoverHandler mTouchCoverHandler;
    private Boolean lastTouchCoverRequest;
    private LocalBroadcastManager mLocalBroadcastManager;
    private CoreService localCoreService;
    private Method startActivityAsUser;
    private Intent launchDefaultActivity;
    private UserHandle mUserHandle;

    @Override
    public void onCreate() {
        Log.d(LOG_TAG + ".oC", "Core service creating");
        localCoreService = this;

        mStateManager = ((CoreApp) getApplicationContext()).getStateManager();

        Log.d(LOG_TAG + ".oC", "Register special actions");
        mStateManager.registerCoreService(this);

        mStateManager.registerCoreReceiver();

        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);

        HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler
        mTouchCoverLooper = thread.getLooper();
        mTouchCoverHandler = new TouchCoverHandler(mTouchCoverLooper);
        lastTouchCoverRequest = mStateManager.getCoverClosed();

        try {
            startActivityAsUser = ((ContextWrapper) this).getClass().getMethod("startActivityAsUser", Intent.class,
                    UserHandle.class);
            Log.d(LOG_TAG, "startActivityAsUser registred");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        launchDefaultActivity = new Intent(localCoreService, DefaultActivity.class)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION
                        | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);

        mUserHandle = android.os.Process.myUserHandle();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // We don't provide binding, so return null
        return null;
    }

    @Override
    public void onDestroy() {
        mStateManager.unregisterCoreReceiver();
        mStateManager.unregisterCoreService();

        Log.d(LOG_TAG + ".oD", "Core service stopped");

        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (!mStateManager.getMainLaunched() && mStateManager.getPreference().getBoolean("pref_enabled", false)) {
            Message msg = Message.obtain();
            msg.arg1 = startId;
            msg.what = CoreApp.CS_TASK_MAINLAUNCH;
            ServiceThread svcThread = new ServiceThread(msg);
            svcThread.start();
        }

        if (intent != null && intent.hasExtra(CoreApp.CS_EXTRA_TASK)) {
            int requestedTaskMode = intent.getIntExtra(CoreApp.CS_EXTRA_TASK, 0);
            if (requestedTaskMode > 0) {
                int msgArg2 = 0;
                switch (requestedTaskMode) {
                case CoreApp.CS_TASK_CHANGE_TOUCHCOVER:
                    boolean sendTouchCoverRequest = intent.getBooleanExtra(CoreApp.CS_EXTRA_STATE, false);
                    if (sendTouchCoverRequest != lastTouchCoverRequest) {
                        mStateManager.acquireCPUGlobal();
                        lastTouchCoverRequest = sendTouchCoverRequest;
                        Message msgTCH = mTouchCoverHandler.obtainMessage();
                        msgTCH.arg1 = startId;
                        if (sendTouchCoverRequest) {
                            msgTCH.arg2 = 1;
                        } else {
                            msgTCH.arg2 = 0;
                        }
                        mTouchCoverHandler.sendMessage(msgTCH);
                    }
                    return START_STICKY;
                case CoreApp.CS_TASK_AUTO_BLACKSCREEN:
                    if (mStateManager.getInActivity()) {
                        Log.d(LOG_TAG + ".oSC", "Blackscreen requested canceled during activity");
                        return START_STICKY;
                    } else if (mStateManager.getBlackScreenTime() > 0) {
                        Log.d(LOG_TAG + ".oSC", "Blackscreen already requested");
                        return START_STICKY;
                    }
                    break;
                case CoreApp.CS_TASK_LAUNCH_ACTIVITY:
                    mStateManager.acquireCPUGlobal();
                    if (intent.getBooleanExtra(CoreApp.CS_EXTRA_STATE, false)) {
                        msgArg2 = 1;
                    }
                    break;
                case CoreApp.CS_TASK_TORCH_STATE:
                    if (intent.getBooleanExtra(CoreApp.CS_EXTRA_STATE, false)) {
                        msgArg2 = 1;
                    }
                    break;
                }
                Log.d(LOG_TAG + ".oSC", "Request starting: " + requestedTaskMode);
                Message msg = Message.obtain();
                msg.arg1 = startId;
                msg.arg2 = msgArg2;
                msg.what = requestedTaskMode;
                ServiceThread svcThread = new ServiceThread(msg);
                svcThread.start();
            }
        }
        // If we get killed, after returning from here, restart
        return START_STICKY;
    }

    private final class TouchCoverHandler extends Handler {
        public TouchCoverHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            Boolean enable = (msg.arg2 == 1);
            // if we are running in root enabled mode then lets up the
            // sensitivity on the view screen
            // so we can use the screen through the window
            if (mStateManager.getRootApp()) {
                if (enable) {
                    Log.d(LOG_TAG + ".enableCoverTouch", "We're root enabled so lets boost the sensitivity...");
                    if (Build.DEVICE.equals(CoreApp.DEV_SERRANO_LTE_CM10)
                            || Build.DEVICE.equals(CoreApp.DEV_SERRANO_LTE_CM11)
                            || Build.DEVICE.equals(CoreApp.DEV_SERRANO_DS_CM10)
                            || Build.DEVICE.equals(CoreApp.DEV_SERRANO_DS_CM11)
                            || Build.DEVICE.equals(CoreApp.DEV_SERRANO_3G_CM11)) {
                        Shell.SU.run(new String[] {
                                "echo module_on_master > /sys/class/sec/tsp/cmd && cat /sys/class/sec/tsp/cmd_result",
                                "echo clear_cover_mode,3 > /sys/class/sec/tsp/cmd && cat /sys/class/sec/tsp/cmd_result" });
                    } else { // others devices
                        Shell.SU.run(new String[] { "echo clear_cover_mode,1 > /sys/class/sec/tsp/cmd" });
                    }
                    Log.d(LOG_TAG + ".enableCoverTouch", "...Sensitivity boosted, hold onto your hats!");
                } else {
                    Log.d(LOG_TAG + ".enableCoverTouch", "We're root enabled so lets revert the sensitivity...");
                    Shell.SU.run(new String[] {
                            "echo clear_cover_mode,0 > /sys/class/sec/tsp/cmd && cat /sys/class/sec/tsp/cmd_result" });
                    Log.d(LOG_TAG + ".enableCoverTouch", "...Sensitivity reverted, sanity is restored!");
                }
            }
            mStateManager.releaseCPUGlobal();
        }
    }

    private class ServiceThread extends Thread {
        private final Message msg;
        private Message internalMsg;

        public ServiceThread(Message msgSend) {
            super();
            this.msg = msgSend;
        }

        @Override
        public void run() {
            runCustom(msg);
        }

        public void runCustom(Message msg) {
            Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": started");

            switch (msg.what) {
            case CoreApp.CS_TASK_TORCH_STATE:
                Intent torchDAIntent = new Intent(CoreApp.DA_ACTION_TORCH_STATE_CHANGED);
                if (msg.arg2 == 1) {
                    mStateManager.setTorchOn(true);
                    torchDAIntent.putExtra(CoreApp.DA_EXTRA_STATE, true);
                    if (mStateManager.getCoverClosed()) {
                        bringDefaultActivityToFront(true);
                    }
                } else {
                    mStateManager.setTorchOn(false);
                    torchDAIntent.putExtra(CoreApp.DA_EXTRA_STATE, false);
                    if (mStateManager.getCoverClosed()) {
                        bringDefaultActivityToFront(false);
                    }
                }
                mLocalBroadcastManager.sendBroadcastSync(torchDAIntent);
                break;
            case CoreApp.CS_TASK_TORCH_TOGGLE:
                if (mStateManager.getPreference().getBoolean("pref_flash_controls", false)) {
                    Intent intent = new Intent(CoreReceiver.TOGGLE_FLASHLIGHT);
                    intent.putExtra("strobe", false);
                    intent.putExtra("period", 100);
                    intent.putExtra("bright", false);
                    sendBroadcastAsUser(intent, mUserHandle);
                } else if (mStateManager.getPreference().getBoolean("pref_flash_controls_alternative", false)) {
                    if (!mStateManager.getTorchOn()) {
                        mStateManager.turnOnFlash();
                        internalMsg = msg;
                        internalMsg.what = CoreApp.CS_TASK_TORCH_STATE;
                        internalMsg.arg2 = 1;
                        runCustom(internalMsg);
                    } else {
                        mStateManager.turnOffFlash();
                        internalMsg = msg;
                        internalMsg.what = CoreApp.CS_TASK_TORCH_STATE;
                        internalMsg.arg2 = 0;
                        runCustom(internalMsg);
                    }
                }
                break;
            case CoreApp.CS_TASK_HEADSET_PLUG:
                Intent headSetIntent = new Intent(CoreApp.DA_ACTION_WIDGET_REFRESH);
                mLocalBroadcastManager.sendBroadcastSync(headSetIntent);
                if (mStateManager.getCoverClosed()) {
                    bringDefaultActivityToFront(false);
                }
                break;
            case CoreApp.CS_TASK_HANGUP_CALL:
                Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": hanging up! goodbye");

                KeyEvent keyHangup = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK);
                keyHangup = KeyEvent.changeTimeRepeat(keyHangup, System.currentTimeMillis(), 1,
                        keyHangup.getFlags() | KeyEvent.FLAG_LONG_PRESS);
                keyHangup = KeyEvent.changeFlags(keyHangup, keyHangup.getFlags() | KeyEvent.FLAG_LONG_PRESS);
                Intent pressHangUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
                pressHangUp.putExtra(Intent.EXTRA_KEY_EVENT, keyHangup);
                sendOrderedBroadcast(pressHangUp, "android.permission.CALL_PRIVILEGED");
                break;
            case CoreApp.CS_TASK_PICKUP_CALL:
                Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": picking up! hello");
                Intent pressPickupCall = new Intent(Intent.ACTION_MEDIA_BUTTON);
                pressPickupCall.putExtra(Intent.EXTRA_KEY_EVENT,
                        new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
                sendOrderedBroadcast(pressPickupCall, "android.permission.CALL_PRIVILEGED");
                break;
            case CoreApp.CS_TASK_INCOMMING_CALL:
                mStateManager.acquireCPUDA();
                wait_package_front_launched(CoreApp.PACKAGE_PHONE_APP);
                if (mStateManager.getCoverClosed()) {
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": the screen is closed. screen my calls");

                    bringDefaultActivityToFront(true);
                }
                break;
            case CoreApp.CS_TASK_SNOOZE_ALARM:
                // Broadcast alarm snooze event
                Intent alarmSnooze = new Intent(CoreReceiver.ALARM_SNOOZE_ACTION);
                sendBroadcastAsUser(alarmSnooze, mUserHandle);
                break;
            case CoreApp.CS_TASK_DISMISS_ALARM:
                // Broadcast alarm Dismiss event
                Intent alarmDismiss = new Intent(CoreReceiver.ALARM_DISMISS_ACTION);
                sendBroadcastAsUser(alarmDismiss, mUserHandle);
                break;
            case CoreApp.CS_TASK_INCOMMING_ALARM:
                mStateManager.acquireCPUDA();
                wait_package_front_launched(CoreApp.PACKAGE_ALARM_APP);
                if (mStateManager.getCoverClosed()) {
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": the screen is closed. screen alarm");

                    bringDefaultActivityToFront(true);
                }
                break;
            case CoreApp.CS_TASK_LAUNCH_ACTIVITY:
                mStateManager.acquireCPUDA();
                boolean noBlackScreen = false;
                if (msg.arg2 == 1) {
                    noBlackScreen = true;
                }
                Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Launch activity / " + noBlackScreen);
                bringDefaultActivityToFront(noBlackScreen);
                mStateManager.releaseCPUGlobal();
                break;
            case CoreApp.CS_TASK_AUTO_BLACKSCREEN:
                // already request running
                if (mStateManager.getBlackScreenTime() > 0) {
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Blackscreen already requested");
                    break;
                }
                mStateManager.setBlackScreenTime(
                        System.currentTimeMillis() + mStateManager.getPreference().getInt("pref_delay", 10000));
                Log.d(LOG_TAG + ".handler",
                        "Thread " + msg.arg1 + ": Blackscreen time set to: " + mStateManager.getBlackScreenTime());
                while (System.currentTimeMillis() < mStateManager.getBlackScreenTime()) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e1) {
                    }
                }
                if (mStateManager.getBlackScreenTime() > 0) {
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Launch blackscreen at: "
                            + mStateManager.getBlackScreenTime());
                    launchBlackScreen();
                    mStateManager.setBlackScreenTime(0);
                } else {
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Blackscreen canceled");
                }
                break;
            case CoreApp.CS_TASK_WAKEUP_DEVICE:
                Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Launch wakeup");
                wakeUpDevice();
                break;
            case CoreApp.CS_TASK_MAINLAUNCH:
                if (!mStateManager.getMainLaunched()) {
                    mStateManager.setMainLaunched(true);
                    mStateManager.startServices();
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Mainthread launched");
                    while (mStateManager.getMainLaunched()
                            && mStateManager.getPreference().getBoolean("pref_enabled", false)) {
                        try {
                            Thread.sleep(10000);
                        } catch (InterruptedException e1) {
                        }
                    }
                    mStateManager.setMainLaunched(false);
                    stopSelf();
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Mainthread stopped");
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Core Service stopping");
                } else {
                    Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": Mainthread already launched");
                }
                break;
            }

            Log.d(LOG_TAG + ".handler", "Thread " + msg.arg1 + ": ended");
            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
        }

        private void wait_package_front_launched(String pacakgeName) {
            Log.d(LOG_TAG + ".wpl", "Wait launch of " + pacakgeName);
            ActivityManager am = (ActivityManager) localCoreService.getSystemService(Context.ACTIVITY_SERVICE);
            long maxWaitTime = System.currentTimeMillis() + 10 * 1000;
            while (System.currentTimeMillis() < maxWaitTime) {
                // The first in the list of RunningTasks is always the
                // foreground task.
                if (am.getRunningTasks(1).get(0).topActivity.getPackageName().equalsIgnoreCase(pacakgeName)) {
                    Log.d(LOG_TAG + ".wpl", pacakgeName + " detected");
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e1) {
                }
            }
        }

        private void launchBlackScreen() {
            if (mStateManager.getCoverClosed()) {
                Log.d(LOG_TAG + ".lBS", "Cover closed.");
                if (mStateManager.getLockMode()) {
                    final DevicePolicyManager dpm = (DevicePolicyManager) localCoreService
                            .getSystemService(Context.DEVICE_POLICY_SERVICE);
                    Log.d(LOG_TAG + ".lBS", "Lock now.");
                    dpm.lockNow();
                    Intent freeScreenDAIntent = new Intent(CoreApp.DA_ACTION_FREE_SCREEN);
                    mLocalBroadcastManager.sendBroadcastSync(freeScreenDAIntent);
                } else if (mStateManager.getOsPowerManagement()) {
                    Log.d(LOG_TAG + ".lBS", "OS must manage screen off.");
                    Intent freeScreenDAIntent = new Intent(CoreApp.DA_ACTION_FREE_SCREEN);
                    mLocalBroadcastManager.sendBroadcastSync(freeScreenDAIntent);
                } else if (mStateManager.getInternalPowerManagement()) {
                    if (mStateManager.getPowerManager().isScreenOn()) {
                        Log.d(LOG_TAG + ".lBS", "Go to sleep now.");
                        mStateManager.getPowerManager().goToSleep(SystemClock.uptimeMillis());
                    } else {
                        Log.d(LOG_TAG + ".lBS", "Screen already off.");
                    }
                }
            } else {
                Log.d(LOG_TAG + ".lBS", "Cover open???.");
            }
        }

        private void wakeUpDevice() {
            if (mStateManager.getInternalPowerManagement()) {
                if (!mStateManager.getPowerManager().isScreenOn()) {
                    Log.d(LOG_TAG + ".wUD", "WakeUp device.");
                    mStateManager.getPowerManager().wakeUp(SystemClock.uptimeMillis());
                } else {
                    Log.d(LOG_TAG + ".wUD", "Screen already on.");
                }
            } else {
                // FIXME Would be nice to remove the deprecated FULL_WAKE_LOCK
                // if possible
                Log.d(LOG_TAG + ".wUD", "aww why can't I hit snooze");
                @SuppressWarnings("deprecation")
                PowerManager.WakeLock wl = mStateManager.getPowerManager().newWakeLock(
                        PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
                        localCoreService.getString(R.string.app_name));
                wl.acquire();
                wl.release();
            }
        }

        private void bringDefaultActivityToFront(boolean noBlackScreen) {

            Log.d(LOG_TAG + ".bDATF", "Launching default activity");
            mStateManager.acquireCPUDA();

            if (noBlackScreen) {
                mStateManager.setBlackScreenTime(0);
            }

            if (startActivityAsUser != null) {
                try {
                    startActivityAsUser.invoke(localCoreService, launchDefaultActivity, mUserHandle);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } else {
                Log.w(LOG_TAG, "No startActivityAsUser registred");
            }

            Log.d(LOG_TAG + ".bDATF", "Started activity.");

            if (!noBlackScreen) {
                // step 2: wait for the delay period and turn the screen off
                internalMsg = msg;
                internalMsg.what = CoreApp.CS_TASK_AUTO_BLACKSCREEN;
                this.runCustom(internalMsg);
            }
        }
    }
}