[Nexus 5 Themes and Apps] JoshMiers:
I've been trying to figure out why the stock Nexus 5 (and presumably all AOSP) LEDs don't work that well with the existing LED managers.

I have come up with 2 Xposed module solutions. I think the issue seems to be that, in AOSP code, if a unique package requests the LED, then it will have control of the LED until the notification is cancelled.

This means that apps like Frozen LED won't reliably work for built-in apps or for apps that show their own notification.

Approach 1:

Only let a single app control the LED by forcibly removing any LED flags when a notification is posted before NotificationManagerService.enqueueNotificationInternal is called.

Approach 2:

Force NotificationManagerService.updateLightsLocked to recalculate the current LED each time it is called.

I have been using the NotificationManagerService code for 4.4.2 along with Xposed to try and print out information to figure out what is going on: https://android.googlesource.com/pl...ndroid/server/NotificationManagerService.java

Below is code for both approaches in a simple Xposed module. Approach 1 is sufficient for me, since I will just use Frozen LED to show any LEDs that I want, but I realize it might not be ideal for everybody. I just got Approach 2 working, but haven't tested it as much as the first approach.

package dev.led;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

import java.lang.reflect.Field;

import android.app.Notification;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

public class XposedMod
implements IXposedHookLoadPackage
{
@Override
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable
{
// here is where we call approach1() or approach2()
}

private static void approach1(LoadPackageParam lpparam)
{
String packageName = lpparam.packageName;
if (packageName != null && packageName.equals("android"))
{
findAndHookMethod("com.android.server.NotificationManagerService", null, "enqueueNotificationInternal",
String.class, String.class, int.class, int.class, String.class, int.class, Notification.class, int[].class,
int.class, new XC_MethodHook()
{
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
try
{
final String pkg = (String) param.args[0];
// here is where you check the package and optionally strip the LED flags
if (pkg.equals("com.sakisds.frozenled") == false)
{
Notification n = (Notification) param.args[6];
// strip out the led info
n.flags = n.flags & ~Notification.FLAG_SHOW_LIGHTS;
n.flags = n.flags & ~Notification.DEFAULT_LIGHTS;
}
}
catch (Exception e)
{
// TODO: log any errors
}
}
});
}
}

private static void approach2(LoadPackageParam lpparam)
{
findAndHookMethod("com.android.server.NotificationManagerService", null, "updateLightsLocked", new XC_MethodHook()
{
protected void beforeHookedMethod(MethodHookParam param) throws Throwable
{
try
{
Field mLedNotification = param.thisObject.getClass().getDeclaredField("mLedNotification");
mLedNotification.setAccessible(true);
// force mLedNotification to be recalculated on entry to the method
mLedNotification.set(param.thisObject, null);
}
catch (Exception e)
{
// TODO: log any errors
}
}
});
}
}