/* * Copyright (C) 2015-2016 Willi Ye <williye97@gmail.com> * * This file is part of Kernel Adiutor. * * Kernel Adiutor 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. * * Kernel Adiutor 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 Kernel Adiutor. If not, see <http://www.gnu.org/licenses/>. * */ package com.grarak.kerneladiutor.services.boot; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.annotation.Nullable; import android.support.v7.app.NotificationCompat; import com.grarak.kerneladiutor.BuildConfig; import com.grarak.kerneladiutor.R; import com.grarak.kerneladiutor.activities.StartActivity; import com.grarak.kerneladiutor.database.Settings; import com.grarak.kerneladiutor.database.tools.customcontrols.Controls; import com.grarak.kerneladiutor.database.tools.profiles.Profiles; import com.grarak.kerneladiutor.fragments.ApplyOnBootFragment; import com.grarak.kerneladiutor.fragments.kernel.CPUHotplugFragment; import com.grarak.kerneladiutor.services.profile.Tile; import com.grarak.kerneladiutor.utils.Prefs; import com.grarak.kerneladiutor.utils.Utils; import com.grarak.kerneladiutor.utils.kernel.cpu.CPUFreq; import com.grarak.kerneladiutor.utils.kernel.cpu.MSMPerformance; import com.grarak.kerneladiutor.utils.kernel.cpuhotplug.CoreCtl; import com.grarak.kerneladiutor.utils.kernel.cpuhotplug.MPDecision; import com.grarak.kerneladiutor.utils.root.Control; import com.grarak.kerneladiutor.utils.root.RootFile; import com.grarak.kerneladiutor.utils.root.RootUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * Created by willi on 03.05.16. */ public class Service extends android.app.Service { private static final String TAG = Service.class.getSimpleName(); private static boolean sCancel; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { Messenger messenger = null; if (intent != null) { Bundle extras = intent.getExtras(); if (extras != null) { messenger = (Messenger) extras.get("messenger"); } } if (messenger == null) { PackageManager pm = getPackageManager(); if (Utils.hideStartActivity()) { pm.setComponentEnabledSetting(new ComponentName(this, StartActivity.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); pm.setComponentEnabledSetting(new ComponentName(BuildConfig.APPLICATION_ID, BuildConfig.APPLICATION_ID + ".activities.StartActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } else { Utils.setStartActivity(Prefs.getBoolean("materialicon", false, this), this); } if (!Prefs.getBoolean(ApplyOnBootFragment.getAssignment(CPUHotplugFragment.class), false, this)) { Prefs.remove("core_ctl_min_cpus_big", this); } } boolean enabled = false; final Settings settings = new Settings(this); Controls controls = new Controls(this); final HashMap<String, Boolean> mCategoryEnabled = new HashMap<>(); final HashMap<String, String> mCustomControls = new HashMap<>(); final List<String> mProfiles = new ArrayList<>(); List<Profiles.ProfileItem> profiles = new Profiles(this).getAllProfiles(); if (messenger == null) { Tile.publishProfileTile(profiles, this); } for (Settings.SettingsItem item : settings.getAllSettings()) { if (!mCategoryEnabled.containsKey(item.getCategory())) { boolean categoryEnabled = Prefs.getBoolean(item.getCategory(), false, this); mCategoryEnabled.put(item.getCategory(), categoryEnabled); if (!enabled && categoryEnabled) { enabled = true; } } } for (Controls.ControlItem item : controls.getAllControls()) { if (item.isOnBootEnabled() && item.getArguments() != null) { mCustomControls.put(item.getApply(), item.getArguments()); } } for (Profiles.ProfileItem profileItem : profiles) { if (profileItem.isOnBootEnabled()) { for (Profiles.ProfileItem.CommandItem commandItem : profileItem.getCommands()) { mProfiles.add(commandItem.getCommand()); } } } final boolean initdEnabled = Prefs.getBoolean("initd_onboot", false, this); enabled = enabled || mCustomControls.size() > 0 || mProfiles.size() > 0 || initdEnabled; if (!enabled) { if (messenger != null) { try { Message message = Message.obtain(); message.arg1 = 1; messenger.send(message); } catch (RemoteException ignored) { } } stopSelf(); return START_NOT_STICKY; } final int seconds = Utils.strToInt(Prefs.getString("applyonbootdelay", "10", this)); final boolean hideNotification = Prefs.getBoolean("applyonboothide", false, this); final boolean confirmationNotification = Prefs.getBoolean("applyonbootconfirmationnotification", true, this); final boolean toast = Prefs.getBoolean("applyonboottoast", false, this); final boolean script = Prefs.getBoolean("applyonbootscript", false, this); PendingIntent cancelIntent = PendingIntent.getBroadcast(this, 1, new Intent(this, CancelReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT); PackageManager pm = getPackageManager(); Intent launchIntent = pm.getLaunchIntentForPackage(BuildConfig.APPLICATION_ID); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launchIntent, 0); final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); final NotificationCompat.Builder builder = new NotificationCompat.Builder(this); if (!hideNotification) { builder.setContentTitle(getString(R.string.app_name)) .setContentText(getString(R.string.apply_on_boot_text, seconds)) .setSmallIcon(Prefs.getBoolean("materialicon", false, this) ? R.mipmap.ic_launcher_material : R.mipmap.ic_launcher) .addAction(0, getString(R.string.cancel), cancelIntent) .setAutoCancel(true) .setOngoing(true) .setContentIntent(contentIntent) .setWhen(0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { builder.setPriority(Notification.PRIORITY_MAX); } } final NotificationCompat.Builder builderComplete = new NotificationCompat.Builder(this); if (!hideNotification) { builderComplete.setContentTitle(getString(R.string.app_name)) .setSmallIcon(Prefs.getBoolean("materialicon", false, this) ? R.mipmap.ic_launcher_material : R.mipmap.ic_launcher) .setContentIntent(contentIntent); } final Handler handler = new Handler(); new Thread(new Runnable() { @Override public void run() { sCancel = false; for (int i = 0; i < seconds; i++) { if (!hideNotification) { if (sCancel) { break; } builder.setContentText(getString(R.string.apply_on_boot_text, seconds - i)); builder.setProgress(seconds, i, false); notificationManager.notify(0, builder.build()); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } if (!hideNotification) { if (confirmationNotification) { builderComplete.setContentText(getString(sCancel ? R.string.apply_on_boot_canceled : R.string.apply_on_boot_complete)); notificationManager.notify(0, builderComplete.build()); } else { notificationManager.cancel(0); } if (sCancel) { sCancel = false; stopSelf(); return; } } RootUtils.SU su = new RootUtils.SU(true, TAG); if (initdEnabled) { RootUtils.mount(true, "/system", su); su.runCommand("for i in `ls /system/etc/init.d`;do chmod 755 $i;done"); su.runCommand("[ -d /system/etc/init.d ] && run-parts /system/etc/init.d"); RootUtils.mount(false, "/system", su); } List<String> commands = new ArrayList<>(); for (Settings.SettingsItem item : settings.getAllSettings()) { String category = item.getCategory(); String setting = item.getSetting(); String id = item.getId(); CPUFreq.ApplyCpu applyCpu; if (mCategoryEnabled.get(category)) { if (category.equals(ApplyOnBootFragment.CPU) && id.contains("%d") && setting.startsWith("#") && ((applyCpu = new CPUFreq.ApplyCpu(setting.substring(1))).toString() != null)) { synchronized (this) { commands.addAll(getApplyCpu(applyCpu, su, Service.this)); } } else { commands.add(setting); } } } if (script) { StringBuilder s = new StringBuilder("#!/system/bin/sh\n\n"); for (String command : commands) { s.append(command).append("\n"); } RootFile file = new RootFile("/data/local/tmp/kerneladiutortmp.sh", su); file.mkdir(); file.write(s.toString(), false); file.execute(); } else { for (String command : commands) { synchronized (this) { su.runCommand(command); } } } for (String script : mCustomControls.keySet()) { RootFile file = new RootFile("/data/local/tmp/kerneladiutortmp.sh", su); file.mkdir(); file.write(script, false); file.execute(mCustomControls.get(script)); } List<String> profileCommands = new ArrayList<>(); for (String command : mProfiles) { CPUFreq.ApplyCpu applyCpu; if (command.startsWith("#") && ((applyCpu = new CPUFreq.ApplyCpu(command.substring(1))).toString() != null)) { synchronized (this) { profileCommands.addAll(getApplyCpu(applyCpu, su, Service.this)); } } profileCommands.add(command); } if (script) { StringBuilder s = new StringBuilder("#!/system/bin/sh\n\n"); for (String command : profileCommands) { s.append(command).append("\n"); } RootFile file = new RootFile("/data/local/tmp/kerneladiutortmp.sh", su); file.mkdir(); file.write(s.toString(), false); file.execute(); } else { for (String command : profileCommands) { synchronized (this) { su.runCommand(command); } } } su.close(); if (toast) { handler.post(new Runnable() { @Override public void run() { Utils.toast(R.string.apply_on_boot_complete, Service.this); } }); } stopSelf(); } }).start(); return START_NOT_STICKY; } public static List<String> getApplyCpu(CPUFreq.ApplyCpu applyCpu, RootUtils.SU su) { return getApplyCpu(applyCpu, su, null); } public static List<String> getApplyCpu(CPUFreq.ApplyCpu applyCpu, RootUtils.SU su, Context context) { List<String> commands = new ArrayList<>(); boolean cpulock = Utils.existFile(CPUFreq.CPU_LOCK_FREQ, su); if (cpulock) { commands.add(Control.write("0", CPUFreq.CPU_LOCK_FREQ)); } boolean mpdecision = Utils.hasProp(MPDecision.HOTPLUG_MPDEC, su) && Utils.isPropRunning(MPDecision.HOTPLUG_MPDEC, su); if (mpdecision) { commands.add(Control.stopService(MPDecision.HOTPLUG_MPDEC)); } for (int i = applyCpu.getMin(); i <= applyCpu.getMax(); i++) { boolean offline = !Utils.existFile(Utils.strFormat(applyCpu.getPath(), i), su); List<Integer> bigCpuRange = applyCpu.getBigCpuRange(); List<Integer> LITTLECpuRange = applyCpu.getLITTLECpuRange(); String coreCtlMinPath = null; String msmPerformanceMinPath = null; if (offline) { if (applyCpu.isBigLITTLE()) { if (Utils.existFile(Utils.strFormat(CoreCtl.CORE_CTL, i), su)) { coreCtlMinPath = Utils.strFormat(CoreCtl.CORE_CTL + CoreCtl.MIN_CPUS, i); commands.add(Control.write(String.valueOf(bigCpuRange.size()), coreCtlMinPath)); } if (Utils.existFile(MSMPerformance.MAX_CPUS, su)) { msmPerformanceMinPath = MSMPerformance.MAX_CPUS; commands.add(Control.write(LITTLECpuRange.size() + ":" + bigCpuRange.size(), msmPerformanceMinPath)); } } commands.add(Control.write("1", Utils.strFormat(CPUFreq.CPU_ONLINE, i))); } commands.add(Control.chmod("644", Utils.strFormat(applyCpu.getPath(), i))); commands.add(Control.write(applyCpu.getValue(), Utils.strFormat(applyCpu.getPath(), i))); commands.add(Control.chmod("444", Utils.strFormat(applyCpu.getPath(), i))); if (offline) { if (coreCtlMinPath != null) { commands.add(Control.write(String.valueOf(context == null ? CPUFreq.sCoreCtlMinCpu : Prefs.getInt("core_ctl_min_cpus_big", applyCpu.getCoreCtlMin(), context)), coreCtlMinPath)); } if (msmPerformanceMinPath != null) { commands.add(Control.write("-1:-1", msmPerformanceMinPath)); } commands.add(Control.write("0", Utils.strFormat(CPUFreq.CPU_ONLINE, i))); } } if (mpdecision) { commands.add(Control.startService(MPDecision.HOTPLUG_MPDEC)); } if (cpulock) { commands.add(Control.write("1", CPUFreq.CPU_LOCK_FREQ)); } return commands; } public static class CancelReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { sCancel = true; } } }