Android Light

Light

note: Android O

1 LightsService启动

/frameworks/base/services/java/com/android/server/SystemServer.java
[SystemServer.java::run()]

 private void run() {
              ......
             // Start services.
             try {
                 traceBeginAndSlog("StartServices");
                 startBootstrapServices();
                 startCoreServices();
                 startOtherServices();
                 SystemServerInitThreadPool.shutdown();
             } catch (Throwable ex) {
                 Slog.e("System", "******************************************");
                 Slog.e("System", "************ Failure starting system services", ex);
                 throw ex;
             } finally {
                 traceEnd();
             }
             ......
 }

/frameworks/base/services/java/com/android/server/SystemServer.java
[SystemServer.java::startBootstrapServices()]

    private void startBootstrapServices() {
        Slog.i(TAG, "Reading configuration...");
        final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
        ......
        // Manages LEDs and display backlight so we need it to bring up the display.
        traceBeginAndSlog("StartLightsService");
        mSystemServiceManager.startService(LightsService.class);
        traceEnd();
       .....
    }

/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
[SystemServiceManager.java::startService(Class serviceClass)]

    public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            Slog.i(TAG, "Starting " + name);
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);

            // Create the service.
            if (!SystemService.class.isAssignableFrom(serviceClass)) {
                throw new RuntimeException("Failed to create " + name
                        + ": service must extend " + SystemService.class.getName());
            }
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (InstantiationException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service could not be instantiated", ex);
            } catch (IllegalAccessException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service must have a public constructor with a Context argument", ex);
            } catch (NoSuchMethodException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service must have a public constructor with a Context argument", ex);
            } catch (InvocationTargetException ex) {
                throw new RuntimeException("Failed to create service " + name
                        + ": service constructor threw an exception", ex);
            }

            startService(service);
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
[SystemServiceManager.java::startService(@NonNull final SystemService service)]

    public void startService(@NonNull final SystemService service) {
        // Register it.
        /**
           // Services that should receive lifecycle events.
           private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
         */
        mServices.add(service);
        // Start it.
        long time = System.currentTimeMillis();
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + service.getClass().getName()
                    + ": onStart threw an exception", ex);
        }
        warnIfTooLong(System.currentTimeMillis() - time, service, "onStart");
    }

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java

[LightsService.java::onStart()]

    @Override
    public void onStart() {
        publishLocalService(LightsManager.class, mService);
    }

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java

[LightsService.java::mService]

    private final LightsManager mService = new LightsManager() {
        @Override
        public Light getLight(int id) {
            if (0 <= id && id < LIGHT_ID_COUNT) {
                return mLights[id];
            } else {
                return null;
            }
        }
    };

/frameworks/base/services/core/java/com/android/server/lights/LightsManager.java
[LightsManager.java]

package com.android.server.lights;

import android.hardware.light.V2_0.Type;

public abstract class LightsManager {
    public static final int LIGHT_ID_BACKLIGHT = Type.BACKLIGHT;
    public static final int LIGHT_ID_KEYBOARD = Type.KEYBOARD;
    public static final int LIGHT_ID_BUTTONS = Type.BUTTONS;
    public static final int LIGHT_ID_BATTERY = Type.BATTERY;
    public static final int LIGHT_ID_NOTIFICATIONS = Type.NOTIFICATIONS;
    public static final int LIGHT_ID_ATTENTION = Type.ATTENTION;
    public static final int LIGHT_ID_BLUETOOTH = Type.BLUETOOTH;
    public static final int LIGHT_ID_WIFI = Type.WIFI;
    public static final int LIGHT_ID_COUNT = Type.COUNT;

    public abstract Light getLight(int id);
}

/frameworks/base/services/core/java/com/android/server/SystemService.java
[SystemService.java::publishLocalService(Class type, T service)]

    /**
     * Publish the service so it is only accessible to the system process.
     */
    protected final <T> void publishLocalService(Class<T> type, T service) {
        LocalServices.addService(type, service);
    }

2 LightsService构造方法

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java
[LightsService.java]

public class LightsService extends SystemService {
     ......
     final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
     ......
     public LightsService(Context context) {
         super(context);

         for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
             mLights[i] = new LightImpl(i);
         }
     }
}

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java
[LightsService.java::LightImpl()]

    private final class LightImpl extends Light {

        private LightImpl(int id) {
            mId = id;
        }

        @Override
        public void setBrightness(int brightness) {
            setBrightness(brightness, BRIGHTNESS_MODE_USER);
        }

        @Override
        public void setBrightness(int brightness, int brightnessMode) {
            synchronized (this) {
                // LOW_PERSISTENCE cannot be manually set
                if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
                    Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
                            ": brightness=0x" + Integer.toHexString(brightness));
                    return;
                }

                int color = brightness & 0x000000ff;
                color = 0xff000000 | (color << 16) | (color << 8) | color;
                setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
            }
        }

        @Override
        public void setColor(int color) {
            synchronized (this) {
                setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0);
            }
        }

        @Override
        public void setFlashing(int color, int mode, int onMS, int offMS) {
            synchronized (this) {
                setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);
            }
        }

        @Override
        public void pulse() {
            pulse(0x00ffffff, 7);
        }

        @Override
        public void pulse(int color, int onMS) {
            synchronized (this) {
                if (mColor == 0 && !mFlashing) {
                    setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000,
                            BRIGHTNESS_MODE_USER);
                    mColor = 0;
                    mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);
                }
            }
        }

        @Override
        public void turnOff() {
            synchronized (this) {
                setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0);
            }
        }

        @Override
        public void setVrMode(boolean enabled) {
            synchronized (this) {
                if (mVrModeEnabled != enabled) {
                    mVrModeEnabled = enabled;

                    mUseLowPersistenceForVR =
                            (getVrDisplayMode() == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE);
                    if (shouldBeInLowPersistenceMode()) {
                        mLastBrightnessMode = mBrightnessMode;
                    }

                    // NOTE: We do not trigger a call to setLightLocked here.  We do not know the
                    // current brightness or other values when leaving VR so we avoid any incorrect
                    // jumps. The code that calls this method will immediately issue a brightness
                    // update which is when the change will occur.
                }
            }
        }

        private void stopFlashing() {
            synchronized (this) {
                setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_USER);
            }
        }

        private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
            if (shouldBeInLowPersistenceMode()) {
                brightnessMode = BRIGHTNESS_MODE_LOW_PERSISTENCE;
            } else if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
                brightnessMode = mLastBrightnessMode;
            }

            if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS ||
                    offMS != mOffMS || mBrightnessMode != brightnessMode) {
                if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
                        + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
                mInitialized = true;
                mLastColor = mColor;
                mColor = color;
                mMode = mode;
                mOnMS = onMS;
                mOffMS = offMS;
                mBrightnessMode = brightnessMode;
                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
                        + Integer.toHexString(color) + ")");
                try {
                    setLight_native(mId, color, mode, onMS, offMS, brightnessMode);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
                }
            }
        }

        private boolean shouldBeInLowPersistenceMode() {
            return mVrModeEnabled && mUseLowPersistenceForVR;
        }

        private int mId;
        private int mColor;
        private int mMode;
        private int mOnMS;
        private int mOffMS;
        private boolean mFlashing;
        private int mBrightnessMode;
        private int mLastBrightnessMode;
        private int mLastColor;
        private boolean mVrModeEnabled;
        private boolean mUseLowPersistenceForVR;
        private boolean mInitialized;
    }

/frameworks/base/services/core/java/com/android/server/lights/Light.java
[Light.java]


package com.android.server.lights;

import android.hardware.light.V2_0.Flash;
import android.hardware.light.V2_0.Brightness;

public abstract class Light {
    public static final int LIGHT_FLASH_NONE = Flash.NONE;
    public static final int LIGHT_FLASH_TIMED = Flash.TIMED;
    public static final int LIGHT_FLASH_HARDWARE = Flash.HARDWARE;

    /**
     * Light brightness is managed by a user setting.
     */
    public static final int BRIGHTNESS_MODE_USER = Brightness.USER;

    /**
     * Light brightness is managed by a light sensor.
     */
    public static final int BRIGHTNESS_MODE_SENSOR = Brightness.SENSOR;

    /**
     * Low-persistence light mode.
     */
    public static final int BRIGHTNESS_MODE_LOW_PERSISTENCE = Brightness.LOW_PERSISTENCE;

    public abstract void setBrightness(int brightness);
    public abstract void setBrightness(int brightness, int brightnessMode);
    public abstract void setColor(int color);
    public abstract void setFlashing(int color, int mode, int onMS, int offMS);
    public abstract void pulse();
    public abstract void pulse(int color, int onMS);
    public abstract void turnOff();
    public abstract void setVrMode(boolean enabled);
}

3 LightsService使用

1 电量指示灯

/frameworks/base/services/core/java/com/android/server/BatteryService.java

[BatteryService.java::updateLightsLocked()]

        /**
         * Synchronize on BatteryService.
         */
        public void updateLightsLocked() {
            final int level = mBatteryProps.batteryLevel;
            final int status = mBatteryProps.batteryStatus;
            if (level < mLowBatteryWarningLevel) {
                if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
                    // Solid red when battery is charging
                    /**
                       设置指示灯颜色
                     */
                    mBatteryLight.setColor(mBatteryLowARGB);
                } else {
                    // Flash red when battery is low and not charging
                    /**
                       设置指示灯
                     */
                    mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
                            mBatteryLedOn, mBatteryLedOff);
                }
            } 
            ......
        }

/frameworks/base/services/core/java/com/android/server/BatteryService.java
[BatteryService.java::Led ]


import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;

private final class Led {
      private final Light mBatteryLight;

      public Led(Context context, LightsManager lights) {
            mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
            ......
        }
}

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java
[LightsService.java::mService ]

    private final LightsManager mService = new LightsManager() {
        @Override
        public Light getLight(int id) {
            if (0 <= id && id < LIGHT_ID_COUNT) {
                return mLights[id];
            } else {
                return null;
            }
        }
    };

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java
[LightsService.java::LightImpl::setFlashing(int color, int mode, int onMS, int offMS) ]

        @Override
        public void setFlashing(int color, int mode, int onMS, int offMS) {
            synchronized (this) {
                setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);
            }
        }

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java
[LightsService.java::LightImpl::setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) ]

        private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
            if (shouldBeInLowPersistenceMode()) {
                brightnessMode = BRIGHTNESS_MODE_LOW_PERSISTENCE;
            } else if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
                brightnessMode = mLastBrightnessMode;
            }

            if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS ||
                    offMS != mOffMS || mBrightnessMode != brightnessMode) {
                if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
                        + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
                mInitialized = true;
                mLastColor = mColor;
                mColor = color;
                mMode = mode;
                mOnMS = onMS;
                mOffMS = offMS;
                mBrightnessMode = brightnessMode;
                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
                        + Integer.toHexString(color) + ")");
                try {
                    setLight_native(mId, color, mode, onMS, offMS, brightnessMode);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
                }
            }
        }

/frameworks/base/services/core/java/com/android/server/lights/LightsService.java
[LightsService.java::setLight_native]

   static native void setLight_native(int light, int color, int mode,
            int onMS, int offMS, int brightnessMode);

/frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
[com_android_server_lights_LightsService.cpp::setLight_native]

static void setLight_native(
        JNIEnv* /* env */,
        jobject /* clazz */,
        jint light,
        jint colorARGB,
        jint flashMode,
        jint onMS,
        jint offMS,
        jint brightnessMode) {

    if (!validate(light, flashMode, brightnessMode)) {
        return;
    }
    /**
       1. 获取ILight服务
     */
    sp<ILight> hal = LightHal::associate();

    if (hal == nullptr) {
        return;
    }

    Type type = static_cast<Type>(light);
    /**
       2. 构建lightState
     */
    LightState state = constructState(
        colorARGB, flashMode, onMS, offMS, brightnessMode);

    {
        ALOGD_IF_SLOW(50, "Excessive delay setting light");
        /**
           3. 调用ILight服务的setLight()函数
         */
        Return<Status> ret = hal->setLight(type, state);
        processReturn(ret, type, state);
    }
}
ILight.hal

/hardware/interfaces/light/2.0/ILight.hal
[ILight.hal]

package android.hardware.light@2.0;

interface ILight {

    /**
     * Set the provided lights to the provided values.
     *
     * @param type logical light to set
     * @param state describes what the light should look like.
     * @return status result of applying state transformation.
     */
    setLight(Type type, LightState state) generates (Status status);

    /**
     * Discover what indicator lights are available.
     *
     * @return types list of available lights
     */
    getSupportedTypes() generates (vec<Type> types);

};

/hardware/interfaces/light/2.0/default/Light.h
[Light.h]

struct Light : public ILight {
    Light(std::map<Type, light_device_t*> &&lights);

    // Methods from ::android::hardware::light::V2_0::ILight follow.
    Return<Status> setLight(Type type, const LightState& state)  override;
    Return<void> getSupportedTypes(getSupportedTypes_cb _hidl_cb)  override;

private:
    std::map<Type, light_device_t*> mLights;
};
1 获取ILight服务

/frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
[com_android_server_lights_LightsService.cpp::LightHal::associate()]


class LightHal {
private:
    static sp<ILight> sLight;
    static bool sLightInit;

    LightHal() {}

public:
    static void disassociate() {
        sLightInit = false;
        sLight = nullptr;
    }

    static sp<ILight> associate() {
        if ((sLight == nullptr && !sLightInit) ||
                (sLight != nullptr && !sLight->ping().isOk())) {
            // will return the hal if it exists the first time.
            sLight = ILight::getService();
            sLightInit = true;

            if (sLight == nullptr) {
                ALOGE("Unable to get ILight interface.");
            }
        }

        return sLight;
    }
};
2 构建lightState

/frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
[com_android_server_lights_LightsService.cpp::constructState()]

static LightState constructState(
        jint colorARGB,
        jint flashMode,
        jint onMS,
        jint offMS,
        jint brightnessMode){
    Flash flash = static_cast<Flash>(flashMode);
    Brightness brightness = static_cast<Brightness>(brightnessMode);

    LightState state{};

    if (brightness == Brightness::LOW_PERSISTENCE) {
        state.flashMode = Flash::NONE;
    } else {
        // Only set non-brightness settings when not in low-persistence mode
        state.flashMode = flash;
        state.flashOnMs = onMS;
        state.flashOffMs = offMS;
    }

    state.color = colorARGB;
    state.brightnessMode = brightness;

    return state;
}
3 调用ILight服务的setLight()函数

/hardware/interfaces/light/2.0/default/Light.cpp
[Light.cpp]

// Methods from ::android::hardware::light::V2_0::ILight follow.
Return<Status> Light::setLight(Type type, const LightState& state)  {
    auto it = mLights.find(type);

    if (it == mLights.end()) {
        return Status::LIGHT_NOT_SUPPORTED;
    }

    light_device_t* hwLight = it->second;

    light_state_t legacyState {
        .color = state.color,
        .flashMode = static_cast<int>(state.flashMode),
        .flashOnMS = state.flashOnMs,
        .flashOffMS = state.flashOffMs,
        .brightnessMode = static_cast<int>(state.brightnessMode),
    };

    /**
       调用hwLight->set_light()
     */
    int ret = hwLight->set_light(hwLight, &legacyState);

    switch (ret) {
        case -ENOSYS:
            return Status::BRIGHTNESS_NOT_SUPPORTED;
        case 0:
            return Status::SUCCESS;
        default:
            return Status::UNKNOWN;
    }
}

/hardware/libhardware/include/hardware/lights.h
[lights.h::light_device_t]

struct light_device_t {
    struct hw_device_t common;

    /**
     * Set the provided lights to the provided values.
     *
     * Returns: 0 on succes, error code on failure.
     */
    int (*set_light)(struct light_device_t* dev,
            struct light_state_t const* state);
};

/hardware/qcom/display/liblight/lights.c
[lights.c::open_lights]


/**
 * module methods
 */

/** Open a new instance of a lights device using name */
static int open_lights(const struct hw_module_t* module, char const* name,
        struct hw_device_t** device)
{
    int (*set_light)(struct light_device_t* dev,
            struct light_state_t const* state);

    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
        char property[PROPERTY_VALUE_MAX];
        property_get("persist.extend.brightness", property, "0");

        if(!(strncmp(property, "1", PROPERTY_VALUE_MAX)) ||
           !(strncmp(property, "true", PROPERTY_VALUE_MAX))) {
            property_get("persist.display.max_brightness", property, "255");
            g_brightness_max = atoi(property);
            set_brightness_ext_init();
            set_light = set_light_backlight_ext;
        } else
            set_light = set_light_backlight;
    } else if (0 == strcmp(LIGHT_ID_BATTERY, name))
        /**
           根据调用参数为电量指示灯,因此set_light对应set_light_battery
         */
        set_light = set_light_battery;
    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
        set_light = set_light_notifications;
    else if (0 == strcmp(LIGHT_ID_BUTTONS, name))
        set_light = set_light_buttons;
    else if (0 == strcmp(LIGHT_ID_ATTENTION, name))
        set_light = set_light_attention;
    else
        return -EINVAL;

    pthread_once(&g_init, init_globals);

    struct light_device_t *dev = malloc(sizeof(struct light_device_t));

    if(!dev)
        return -ENOMEM;

    memset(dev, 0, sizeof(*dev));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;
    dev->common.module = (struct hw_module_t*)module;
    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    dev->set_light = set_light;

    *device = (struct hw_device_t*)dev;
    return 0;
}

/hardware/qcom/display/liblight/lights.c
[lights.c::set_light_battery()]

static int
set_light_battery(struct light_device_t* dev,
        struct light_state_t const* state)
{
    pthread_mutex_lock(&g_lock);
    g_battery = *state;
    handle_speaker_battery_locked(dev);
    pthread_mutex_unlock(&g_lock);
    return 0;
}

/hardware/qcom/display/liblight/lights.c
[lights.c::handle_speaker_battery_locked()]

static void
handle_speaker_battery_locked(struct light_device_t* dev)
{
    if (is_lit(&g_battery)) {
        set_speaker_light_locked(dev, &g_battery);
    } else {
        set_speaker_light_locked(dev, &g_notification);
    }
}

/hardware/qcom/display/liblight/lights.c
[lights.c::set_speaker_light_locked()]

static int
set_speaker_light_locked(struct light_device_t* dev,
        struct light_state_t const* state)
{
    int red, green, blue;
    int blink;
    int onMS, offMS;
    unsigned int colorRGB;

    if(!dev) {
        return -1;
    }

    switch (state->flashMode) {
        case LIGHT_FLASH_TIMED:
            onMS = state->flashOnMS;
            offMS = state->flashOffMS;
            break;
        case LIGHT_FLASH_NONE:
        default:
            onMS = 0;
            offMS = 0;
            break;
    }

    colorRGB = state->color;

#if 0
    ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",
            state->flashMode, colorRGB, onMS, offMS);
#endif

    red = (colorRGB >> 16) & 0xFF;
    green = (colorRGB >> 8) & 0xFF;
    blue = colorRGB & 0xFF;

    if (onMS > 0 && offMS > 0) {
        /*
         * if ON time == OFF time
         *   use blink mode 2
         * else
         *   use blink mode 1
         */
        if (onMS == offMS)
            blink = 2;
        else
            blink = 1;
    } else {
        blink = 0;
    }

    if (blink) {
        if (red) {
            if (write_int(RED_BLINK_FILE, blink))
                write_int(RED_LED_FILE, 0);
    }
        if (green) {
            if (write_int(GREEN_BLINK_FILE, blink))
                write_int(GREEN_LED_FILE, 0);
    }
        if (blue) {
            if (write_int(BLUE_BLINK_FILE, blink))
                write_int(BLUE_LED_FILE, 0);
    }
    } else {
        write_int(RED_LED_FILE, red);
        write_int(GREEN_LED_FILE, green);
        write_int(BLUE_LED_FILE, blue);
    }

    return 0;
}

/hardware/qcom/display/liblight/lights.c
[lights.c::write_int()]

static int
write_int(char const* path, int value)
{
    int fd;
    static int already_warned = 0;

    fd = open(path, O_RDWR);
    if (fd >= 0) {
        char buffer[20];
        int bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
        ssize_t amt = write(fd, buffer, (size_t)bytes);
        close(fd);
        return amt == -1 ? -errno : 0;
    } else {
        if (already_warned == 0) {
            ALOGE("write_int failed to open %s\n", path);
            already_warned = 1;
        }
        return -errno;
    }
}

/hardware/qcom/display/liblight/lights.c
[lights.c]

static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static struct light_state_t g_notification;
static struct light_state_t g_battery;
static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
static int g_attention = 0;
static int g_brightness_max = 0;

char const*const RED_LED_FILE
        = "/sys/class/leds/red/brightness";

char const*const GREEN_LED_FILE
        = "/sys/class/leds/green/brightness";

char const*const BLUE_LED_FILE
        = "/sys/class/leds/blue/brightness";

char const*const LCD_FILE
        = "/sys/class/leds/lcd-backlight/brightness";

char const*const LCD_FILE2
        = "/sys/class/backlight/panel0-backlight/brightness";

char const*const BUTTON_FILE
        = "/sys/class/leds/button-backlight/brightness";

char const*const RED_BLINK_FILE
        = "/sys/class/leds/red/blink";

char const*const GREEN_BLINK_FILE
        = "/sys/class/leds/green/blink";

char const*const BLUE_BLINK_FILE
        = "/sys/class/leds/blue/blink";

char const*const PERSISTENCE_FILE
        = "/sys/class/graphics/fb0/msm_fb_persist_mode";
2 通知指示灯

/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
[NotificationManagerService.java::updateLightsLocked()]

  @GuardedBy("mNotificationLock")
    void updateLightsLocked()
    {
        // handle notification lights
        NotificationRecord ledNotification = null;
        while (ledNotification == null && !mLights.isEmpty()) {
            final String owner = mLights.get(mLights.size() - 1);
            ledNotification = mNotificationsByKey.get(owner);
            if (ledNotification == null) {
                Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
                mLights.remove(owner);
            }
        }

        // Don't flash while we are in a call or screen is on
        if (ledNotification == null || mInCall || mScreenOn) {
            mNotificationLight.turnOff();
        } else {
            NotificationRecord.Light light = ledNotification.getLight();
            if (light != null && mNotificationPulseEnabled) {
                // pulse repeatedly
                mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
                        light.onMs, light.offMs);
            }
        }
    }

/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
[NotificationManagerService.java::init()]

   // TODO: Tests should call onStart instead once the methods above are removed.
    @VisibleForTesting
    void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
            LightsManager lightsManager, NotificationListeners notificationListeners,
            ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
            NotificationUsageStats usageStats) {
                    ......

                    mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
                    mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
                    ......
    }

/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
[NotificationManagerService.java::onStart()]

public class NotificationManagerService extends SystemService {
    ......
    @Override
    public void onStart() {
        SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
            @Override
            public void repost(int userId, NotificationRecord r) {
                try {
                    if (DBG) {
                        Slog.d(TAG, "Reposting " + r.getKey());
                    }
                    enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
                            r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
                            r.sbn.getNotification(), userId);
                } catch (Exception e) {
                    Slog.e(TAG, "Cannot un-snooze notification", e);
                }
            }
        }, mUserProfiles);

        init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
                getLocalService(LightsManager.class), new NotificationListeners(),
                null, snoozeHelper, new NotificationUsageStats(getContext()));
        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
        publishLocalService(NotificationManagerInternal.class, mInternalService);
    }
    ......
}

mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
light.onMs, light.offMs);调用流程和电源指示灯调用流程类似,区别在于使用LightsManager.getLight()传入的Id不同。

通知中指示灯的初始化在NotificationRecord中

/frameworks/base/services/core/java/com/android/server/notification/NotificationRecord.java
[NotificationRecord.java]

public final class NotificationRecord {
......
private Light mLight;

......

  @VisibleForTesting
    public NotificationRecord(Context context, StatusBarNotification sbn,
            NotificationChannel channel)
    {
        this.sbn = sbn;
        mOriginalFlags = sbn.getNotification().flags;
        mRankingTimeMs = calculateRankingTimeMs(0L);
        mCreationTimeMs = sbn.getPostTime();
        mUpdateTimeMs = mCreationTimeMs;
        mContext = context;
        stats = new NotificationUsageStats.SingleNotificationStats();
        mChannel = channel;
        mPreChannelsNotification = isPreChannelsNotification();
        mSound = calculateSound();
        mVibration = calculateVibration();
        mAttributes = calculateAttributes();
        mImportance = calculateImportance();
        mLight = calculateLights();
    }
......
}

/frameworks/base/services/core/java/com/android/server/notification/NotificationRecord.java
[NotificationRecord.java::calculateLights()]

    private Light calculateLights() {
        int defaultLightColor = mContext.getResources().getColor(
                com.android.internal.R.color.config_defaultNotificationColor);
        int defaultLightOn = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_defaultNotificationLedOn);
        int defaultLightOff = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_defaultNotificationLedOff);

        int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
                : defaultLightColor;

        /**
           判断通知是否需要指示灯
         */
        Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
                defaultLightOn, defaultLightOff) : null;
        if (mPreChannelsNotification
                && (getChannel().getUserLockedFields()
                & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
            final Notification notification = sbn.getNotification();
            if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
                light = new Light(notification.ledARGB, notification.ledOnMS,
                        notification.ledOffMS);
                if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
                    light = new Light(defaultLightColor, defaultLightOn,
                            defaultLightOff);
                }
            } else {
                light = null;
            }
        }
        return light;
    }

/frameworks/base/services/core/java/com/android/server/notification/NotificationRecord.java
[NotificationRecord.java::Light]

    @VisibleForTesting
    static final class Light {
        public final int color;
        public final int onMs;
        public final int offMs;

        public Light(int color, int onMs, int offMs) {
            this.color = color;
            this.onMs = onMs;
            this.offMs = offMs;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Light light = (Light) o;

            if (color != light.color) return false;
            if (onMs != light.onMs) return false;
            return offMs == light.offMs;

        }

        @Override
        public int hashCode() {
            int result = color;
            result = 31 * result + onMs;
            result = 31 * result + offMs;
            return result;
        }

        @Override
        public String toString() {
            return "Light{" +
                    "color=" + color +
                    ", onMs=" + onMs +
                    ", offMs=" + offMs +
                    '}';
        }
    }
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值