一、Android 呼吸灯的使用
在讲呼吸灯实现流程之前,我们先看一下如何使用它。
Android提供了呼吸灯的接口,我们可以通过该接口,控制呼吸灯的闪烁频率和占空比。具体代码如下:
- package com.example.test;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.EditText;
- import android.app.Activity;
- import android.app.Notification;
- import android.app.NotificationManager;
- public class MainActivity extends Activity {
- Button working;
- EditText ledOn;
- EditText ledOff;
- final int ID_LED=19871103;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- ledOn = (EditText)findViewById(R.id.LedOn);
- ledOff = (EditText)findViewById(R.id.LedOff);
- working = (Button)findViewById(R.id.bu1);
- working.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- NotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
- Notification notification = new Notification();
- notification.ledARGB = 0xffff0000; //这里是颜色,我们可以尝试改变,理论上0xFFff0000是红色
- // notification.ledOnMS = 350;
- // notification.ledOffMS = 300;
- notification.ledOnMS = Integer.parseInt((ledOn.getText().toString()));
- notification.ledOffMS = Integer.parseInt((ledOff.getText().toString()));
- notification.flags = Notification.FLAG_SHOW_LIGHTS;
- nm.notify(ID_LED, notification);
- }
- });
- //nm.cancel(ID_LED);
- }
- }
package com.example.test; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; public class MainActivity extends Activity { Button working; EditText ledOn; EditText ledOff; final int ID_LED=19871103; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ledOn = (EditText)findViewById(R.id.LedOn); ledOff = (EditText)findViewById(R.id.LedOff); working = (Button)findViewById(R.id.bu1); working.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub NotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE); Notification notification = new Notification(); notification.ledARGB = 0xffff0000; //这里是颜色,我们可以尝试改变,理论上0xFFff0000是红色 // notification.ledOnMS = 350; // notification.ledOffMS = 300; notification.ledOnMS = Integer.parseInt((ledOn.getText().toString())); notification.ledOffMS = Integer.parseInt((ledOff.getText().toString())); notification.flags = Notification.FLAG_SHOW_LIGHTS; nm.notify(ID_LED, notification); } }); //nm.cancel(ID_LED); } }通过该程序,便能自由控制呼吸灯的占空比与频率。
二、Android上层呼吸灯的实现
1、NotificationManager
(1).在apk中我们填充了结构notification,并调用了nm.notify。很显然,找到了关键点NotificationManager,对应文件为:
frameworks/base/core/java/android/app/NotificationManager.java
(2).在NotificationManager.java中找到了我们的调用方法notify。 对应如下:
- public void notify(int id, Notification notification)
- 107 {
- 108 notify(null, id, notification);
- 109 }
- 110
- 111 /**
- 112 * Post a notification to be shown in the status bar. If a notification with
- 113 * the same tag and id has already been posted by your application and has not yet been
- 114 * canceled, it will be replaced by the updated information.
- 115 *
- 116 * @param tag A string identifier for this notification. May be {@code null}.
- 117 * @param id An identifier for this notification. The pair (tag, id) must be unique
- 118 * within your application.
- 119 * @param notification A {@link Notification} object describing what to
- 120 * show the user. Must not be null.
- 121 */
- 122 public void notify(String tag, int id, Notification notification)
- 123 {
- 124 int[] idOut = new int[1];
- 125 INotificationManager service = getService();
- 126 String pkg = mContext.getPackageName();
- 127 if (notification.sound != null) {
- 128 notification.sound = notification.sound.getCanonicalUri();
- 129 }
- 130 if (localLOGV) Log.v(TAG, pkg + ”: notify(“ + id + ”, ” + notification + ”)”);
- 131 try {
- 132 service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
- 133 UserHandle.myUserId());
- 134 if (id != idOut[0]) {
- 135 Log.w(TAG, “notify: id corrupted: sent ” + id + ”, got back ” + idOut[0]);
- 136 }
- 137 } catch (RemoteException e) {
- 138 }
- 139 }
public void notify(int id, Notification notification) 107 { 108 notify(null, id, notification); 109 } 110 111 /** 112 * Post a notification to be shown in the status bar. If a notification with 113 * the same tag and id has already been posted by your application and has not yet been 114 * canceled, it will be replaced by the updated information. 115 * 116 * @param tag A string identifier for this notification. May be {@code null}. 117 * @param id An identifier for this notification. The pair (tag, id) must be unique 118 * within your application. 119 * @param notification A {@link Notification} object describing what to 120 * show the user. Must not be null. 121 */ 122 public void notify(String tag, int id, Notification notification) 123 { 124 int[] idOut = new int[1]; 125 INotificationManager service = getService(); 126 String pkg = mContext.getPackageName(); 127 if (notification.sound != null) { 128 notification.sound = notification.sound.getCanonicalUri(); 129 } 130 if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); 131 try { 132 service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut, 133 UserHandle.myUserId()); 134 if (id != idOut[0]) { 135 Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); 136 } 137 } catch (RemoteException e) { 138 } 139 }抓取到关键点:enqueueNotificationWithTag。它位于类NotificationManagerService.java中,具体位置如下:
frameworks/base/services/java/com/android/server/NotificationManagerService.java
(3).NotificationManagerService
进入类NotificationManagerService.java,找到我们在NotificationManager.java中调用的方法:enqueueNotificationWithTag,如下:
- public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
- 956 int[] idOut, int userId)
- 957 {
- 958 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
- 959 tag, id, notification, idOut, userId);
- 960 }
- 961
- 962 private final static int clamp(int x, int low, int high) {
- 963 return (x < low) ? low : ((x > high) ? high : x);
- 964 }
- 965
- 966 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
- 967 // uid/pid of another application)
- 968 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
- 969 String tag, int id, Notification notification, int[] idOut, int userId)
- 970 {
- 971 if (DBG) {
- 972 Slog.v(TAG, “enqueueNotificationInternal: pkg=” + pkg + ” id=” + id + ” notification=” + notification);
- 973 }
- 974 checkCallerIsSystemOrSameApp(pkg);
- 975 final boolean isSystemNotification = (“android”.equals(pkg));
- 976
- 977 userId = ActivityManager.handleIncomingUser(callingPid,
- 978 callingUid, userId, true, false, “enqueueNotification”, pkg);
- 979 UserHandle user = new UserHandle(userId);
- 980
- 981 // Limit the number of notifications that any given package except the android
- 982 // package can enqueue. Prevents DOS attacks and deals with leaks.
- 983 if (!isSystemNotification) {
public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, 956 int[] idOut, int userId) 957 { 958 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 959 tag, id, notification, idOut, userId); 960 } 961 962 private final static int clamp(int x, int low, int high) { 963 return (x < low) ? low : ((x > high) ? high : x); 964 } 965 966 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 967 // uid/pid of another application) 968 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, 969 String tag, int id, Notification notification, int[] idOut, int userId) 970 { 971 if (DBG) { 972 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); 973 } 974 checkCallerIsSystemOrSameApp(pkg); 975 final boolean isSystemNotification = ("android".equals(pkg)); 976 977 userId = ActivityManager.handleIncomingUser(callingPid, 978 callingUid, userId, true, false, "enqueueNotification", pkg); 979 UserHandle user = new UserHandle(userId); 980 981 // Limit the number of notifications that any given package except the android 982 // package can enqueue. Prevents DOS attacks and deals with leaks. 983 if (!isSystemNotification) {很显然我们进入了:enqueueNotificationInternal,该函数太长,不复制了就,~_~。这个函数中实现了不少功能,如是否播放声音,是否震动。最后找到控制呼吸灯的位置在这个方法中:
- 1321 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
- 1322 && canInterrupt) {
- 1323 mLights.add(r);
- 1324 updateLightsLocked();
- 1325 } else {
- 1326 if (old != null
- 1327 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
- 1328 updateLightsLocked();
- 1329 }
- 1330 }
1321 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1322 && canInterrupt) { 1323 mLights.add(r); 1324 updateLightsLocked(); 1325 } else { 1326 if (old != null 1327 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1328 updateLightsLocked(); 1329 } 1330 }很显然,关键点就是:updateLightsLocked()。进入之后会有一系列判断、赋值之类操作。之后进入:
- private LightsService.Light mNotificationLight;
- 1602 if (mNotificationPulseEnabled) {
- 1603 // pulse repeatedly
- 1604 ///M: log lights information
- 1605 Log.d(TAG, “notification setFlashing ledOnMS = ”+ledOnMS + ” ledOffMS = ”+ ledOffMS);
- 1606 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
- 1607 ledOnMS, ledOffMS);
- 1608 ///M:
- 1609 } else {
- 1610 // pulse only once
- 1611 mNotificationLight.pulse(ledARGB, ledOnMS);
- 1612 }
private LightsService.Light mNotificationLight; 1602 if (mNotificationPulseEnabled) { 1603 // pulse repeatedly 1604 ///M: log lights information 1605 Log.d(TAG, "notification setFlashing ledOnMS = "+ledOnMS + " ledOffMS = "+ ledOffMS); 1606 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1607 ledOnMS, ledOffMS); 1608 ///M: 1609 } else { 1610 // pulse only once 1611 mNotificationLight.pulse(ledARGB, ledOnMS); 1612 }然后,我们开始进入LightsService。
(4).LightsService
LightsService的位置如下:
frameworks/base/services/java/com/android/server/LightsService.java
在LightsService中,通过方法:setFlashing 调用:setLightLocked,最终到达了JNI的setLight_native;
- 116 private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
- 117 if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
- 118 if (DEBUG) Slog.v(TAG, “setLight #” + mId + ”: color=#”
- 119 + Integer.toHexString(color));
- 120 mColor = color;
- 121 mMode = mode;
- 122 mOnMS = onMS;
- 123 mOffMS = offMS;
- 124 setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
- 125 }
- 126 }
116 private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) { 117 if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) { 118 if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#" 119 + Integer.toHexString(color)); 120 mColor = color; 121 mMode = mode; 122 mOnMS = onMS; 123 mOffMS = offMS; 124 setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); 125 } 126 }2.JNI
通过方法setLight_native,进入到了JNI层中,位置如下:
frameworks/base/services/jni/com_android_server_LightsService.cpp
在方法:setLight_native中,一样的进行了相关的判断、接受上层赋值.之后根据参数,调用了对应的set_light:
- 127 ALOGD(“setLight_native: light=%d, colorARGB=0x%x, flashMode=%d, onMS=%d, offMS=%d, brightnessMode=%d”,
- 128 light, colorARGB, flashMode, onMS, offMS, brightnessMode);
- 129
- 130#if defined(MTK_AAL_SUPPORT)
- 131 if (light == LIGHT_INDEX_BACKLIGHT) {
- 132 if (AALClient::getInstance().setBacklightColor(colorARGB & 0x00ffffff) == 0)
- 133 return;
- 134 ALOGW(“Fail to set backlight from AAL service”);
- 135 }
- 136#endif
- 137
- 138 devices->lights[light]->set_light(devices->lights[light], &state);
127 ALOGD("setLight_native: light=%d, colorARGB=0x%x, flashMode=%d, onMS=%d, offMS=%d, brightnessMode=%d", 128 light, colorARGB, flashMode, onMS, offMS, brightnessMode); 129 130#if defined(MTK_AAL_SUPPORT) 131 if (light == LIGHT_INDEX_BACKLIGHT) { 132 if (AALClient::getInstance().setBacklightColor(colorARGB & 0x00ffffff) == 0) 133 return; 134 ALOGW("Fail to set backlight from AAL service"); 135 } 136#endif 137 138 devices->lights[light]->set_light(devices->lights[light], &state);最后通过set_light转入了HAL层。
3.HAL
呼吸灯的HAL层对应位置如下:
mediatek/hardware/liblights/lights.c
我们在NotificationManager下来的set_light对应为:
open_lights下的:
- 584 }
- 585 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
- 586 set_light = set_light_notifications;
- 587 }
584 } 585 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) { 586 set_light = set_light_notifications; 587 }进入set_light_notifications,通过如下调用:
set_light_notifications —-> handle_speaker_battery_locked —-> set_speaker_light_locked
在函数set_speaker_light_locked中,最后判断我们要控制的led是red,green还是blue,从我的范例上看,我传入的led参数为0xffff0000,对应为red。于是,进入如下的 red:
- 465 if (red) {
- 466 blink_green(0, 0, 0);
- 467 blink_blue(0, 0, 0);
- 468 blink_red(red, onMS, offMS);
- 469 }
- 470 else if (green) {
- 471 blink_red(0, 0, 0);
- 472 blink_blue(0, 0, 0);
- 473 blink_green(green, onMS, offMS);
- 474 }
- 475 else if (blue) {
- 476 blink_red(0, 0, 0);
- 477 blink_green(0, 0, 0);
- 478 blink_blue(blue, onMS, offMS);
- 479 }
- 480 else {
- 481 blink_red(0, 0, 0);
- 482 blink_green(0, 0, 0);
- 483 blink_blue(0, 0, 0);
- 484 }
465 if (red) { 466 blink_green(0, 0, 0); 467 blink_blue(0, 0, 0); 468 blink_red(red, onMS, offMS); 469 } 470 else if (green) { 471 blink_red(0, 0, 0); 472 blink_blue(0, 0, 0); 473 blink_green(green, onMS, offMS); 474 } 475 else if (blue) { 476 blink_red(0, 0, 0); 477 blink_green(0, 0, 0); 478 blink_blue(blue, onMS, offMS); 479 } 480 else { 481 blink_red(0, 0, 0); 482 blink_green(0, 0, 0); 483 blink_blue(0, 0, 0); 484 }进入了red函数之后,重点如下:
上层传下来的的level值为0,则直接关闭RED_LED_FILE(char const*const RED_LED_FILE = “/sys/class/leds/red/brightness”)
- 248 if (nowStatus == 0) {
- 249 write_int(RED_LED_FILE, 0);
- 250 }
248 if (nowStatus == 0) { 249 write_int(RED_LED_FILE, 0); 250 }上层传下的来的参数onMS和offMS都有值,则呼吸灯闪烁:
- 251 else if (nowStatus == 1) {
- 252// write_int(RED_LED_FILE, level); // default full brightness
- 253 write_str(RED_TRIGGER_FILE, “timer”);
- 254 while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {
- 255 ALOGD(“RED_DELAY_OFF_FILE doesn’t exist or cannot write!!\n”);
- 256 led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs
- 257 i++;
- 258 }
- 259 write_int(RED_DELAY_OFF_FILE, offMS);
- 260 write_int(RED_DELAY_ON_FILE, onMS);
- 261 }
251 else if (nowStatus == 1) { 252// write_int(RED_LED_FILE, level); // default full brightness 253 write_str(RED_TRIGGER_FILE, "timer"); 254 while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) { 255 ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n"); 256 led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs 257 i++; 258 } 259 write_int(RED_DELAY_OFF_FILE, offMS); 260 write_int(RED_DELAY_ON_FILE, onMS); 261 }其他情况下,我们直接就点亮红色呼吸灯:
- 262 else {
- 263 write_str(RED_TRIGGER_FILE, “none”);
- 264 write_int(RED_LED_FILE, 255); // default full brightness
- 265 }
262 else { 263 write_str(RED_TRIGGER_FILE, "none"); 264 write_int(RED_LED_FILE, 255); // default full brightness 265 }4.小结
到处Andoid上层的呼吸灯基本上就这个。
在HAL中最后,点亮,关闭和闪烁呼吸灯,点亮和关闭呼吸灯都是直接操作设备接口: RED_LED_FILE = “/sys/class/leds/red/brightness”;
闪烁则相对复杂一些,接下来驱动部分就以闪烁为为范例进行讲解。
一、Android 呼吸灯的使用
在讲呼吸灯实现流程之前,我们先看一下如何使用它。
Android提供了呼吸灯的接口,我们可以通过该接口,控制呼吸灯的闪烁频率和占空比。具体代码如下:
- package com.example.test;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.EditText;
- import android.app.Activity;
- import android.app.Notification;
- import android.app.NotificationManager;
- public class MainActivity extends Activity {
- Button working;
- EditText ledOn;
- EditText ledOff;
- final int ID_LED=19871103;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- ledOn = (EditText)findViewById(R.id.LedOn);
- ledOff = (EditText)findViewById(R.id.LedOff);
- working = (Button)findViewById(R.id.bu1);
- working.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- NotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
- Notification notification = new Notification();
- notification.ledARGB = 0xffff0000; //这里是颜色,我们可以尝试改变,理论上0xFFff0000是红色
- // notification.ledOnMS = 350;
- // notification.ledOffMS = 300;
- notification.ledOnMS = Integer.parseInt((ledOn.getText().toString()));
- notification.ledOffMS = Integer.parseInt((ledOff.getText().toString()));
- notification.flags = Notification.FLAG_SHOW_LIGHTS;
- nm.notify(ID_LED, notification);
- }
- });
- //nm.cancel(ID_LED);
- }
- }
package com.example.test; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; public class MainActivity extends Activity { Button working; EditText ledOn; EditText ledOff; final int ID_LED=19871103; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ledOn = (EditText)findViewById(R.id.LedOn); ledOff = (EditText)findViewById(R.id.LedOff); working = (Button)findViewById(R.id.bu1); working.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub NotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE); Notification notification = new Notification(); notification.ledARGB = 0xffff0000; //这里是颜色,我们可以尝试改变,理论上0xFFff0000是红色 // notification.ledOnMS = 350; // notification.ledOffMS = 300; notification.ledOnMS = Integer.parseInt((ledOn.getText().toString())); notification.ledOffMS = Integer.parseInt((ledOff.getText().toString())); notification.flags = Notification.FLAG_SHOW_LIGHTS; nm.notify(ID_LED, notification); } }); //nm.cancel(ID_LED); } }通过该程序,便能自由控制呼吸灯的占空比与频率。
二、Android上层呼吸灯的实现
1、NotificationManager
(1).在apk中我们填充了结构notification,并调用了nm.notify。很显然,找到了关键点NotificationManager,对应文件为:
frameworks/base/core/java/android/app/NotificationManager.java
(2).在NotificationManager.java中找到了我们的调用方法notify。 对应如下:
- public void notify(int id, Notification notification)
- 107 {
- 108 notify(null, id, notification);
- 109 }
- 110
- 111 /**
- 112 * Post a notification to be shown in the status bar. If a notification with
- 113 * the same tag and id has already been posted by your application and has not yet been
- 114 * canceled, it will be replaced by the updated information.
- 115 *
- 116 * @param tag A string identifier for this notification. May be {@code null}.
- 117 * @param id An identifier for this notification. The pair (tag, id) must be unique
- 118 * within your application.
- 119 * @param notification A {@link Notification} object describing what to
- 120 * show the user. Must not be null.
- 121 */
- 122 public void notify(String tag, int id, Notification notification)
- 123 {
- 124 int[] idOut = new int[1];
- 125 INotificationManager service = getService();
- 126 String pkg = mContext.getPackageName();
- 127 if (notification.sound != null) {
- 128 notification.sound = notification.sound.getCanonicalUri();
- 129 }
- 130 if (localLOGV) Log.v(TAG, pkg + ”: notify(“ + id + ”, ” + notification + ”)”);
- 131 try {
- 132 service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
- 133 UserHandle.myUserId());
- 134 if (id != idOut[0]) {
- 135 Log.w(TAG, “notify: id corrupted: sent ” + id + ”, got back ” + idOut[0]);
- 136 }
- 137 } catch (RemoteException e) {
- 138 }
- 139 }
public void notify(int id, Notification notification) 107 { 108 notify(null, id, notification); 109 } 110 111 /** 112 * Post a notification to be shown in the status bar. If a notification with 113 * the same tag and id has already been posted by your application and has not yet been 114 * canceled, it will be replaced by the updated information. 115 * 116 * @param tag A string identifier for this notification. May be {@code null}. 117 * @param id An identifier for this notification. The pair (tag, id) must be unique 118 * within your application. 119 * @param notification A {@link Notification} object describing what to 120 * show the user. Must not be null. 121 */ 122 public void notify(String tag, int id, Notification notification) 123 { 124 int[] idOut = new int[1]; 125 INotificationManager service = getService(); 126 String pkg = mContext.getPackageName(); 127 if (notification.sound != null) { 128 notification.sound = notification.sound.getCanonicalUri(); 129 } 130 if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); 131 try { 132 service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut, 133 UserHandle.myUserId()); 134 if (id != idOut[0]) { 135 Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); 136 } 137 } catch (RemoteException e) { 138 } 139 }抓取到关键点:enqueueNotificationWithTag。它位于类NotificationManagerService.java中,具体位置如下:
frameworks/base/services/java/com/android/server/NotificationManagerService.java
(3).NotificationManagerService
进入类NotificationManagerService.java,找到我们在NotificationManager.java中调用的方法:enqueueNotificationWithTag,如下:
- public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
- 956 int[] idOut, int userId)
- 957 {
- 958 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
- 959 tag, id, notification, idOut, userId);
- 960 }
- 961
- 962 private final static int clamp(int x, int low, int high) {
- 963 return (x < low) ? low : ((x > high) ? high : x);
- 964 }
- 965
- 966 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
- 967 // uid/pid of another application)
- 968 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
- 969 String tag, int id, Notification notification, int[] idOut, int userId)
- 970 {
- 971 if (DBG) {
- 972 Slog.v(TAG, “enqueueNotificationInternal: pkg=” + pkg + ” id=” + id + ” notification=” + notification);
- 973 }
- 974 checkCallerIsSystemOrSameApp(pkg);
- 975 final boolean isSystemNotification = (“android”.equals(pkg));
- 976
- 977 userId = ActivityManager.handleIncomingUser(callingPid,
- 978 callingUid, userId, true, false, “enqueueNotification”, pkg);
- 979 UserHandle user = new UserHandle(userId);
- 980
- 981 // Limit the number of notifications that any given package except the android
- 982 // package can enqueue. Prevents DOS attacks and deals with leaks.
- 983 if (!isSystemNotification) {
public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification, 956 int[] idOut, int userId) 957 { 958 enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(), 959 tag, id, notification, idOut, userId); 960 } 961 962 private final static int clamp(int x, int low, int high) { 963 return (x < low) ? low : ((x > high) ? high : x); 964 } 965 966 // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the 967 // uid/pid of another application) 968 public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid, 969 String tag, int id, Notification notification, int[] idOut, int userId) 970 { 971 if (DBG) { 972 Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); 973 } 974 checkCallerIsSystemOrSameApp(pkg); 975 final boolean isSystemNotification = ("android".equals(pkg)); 976 977 userId = ActivityManager.handleIncomingUser(callingPid, 978 callingUid, userId, true, false, "enqueueNotification", pkg); 979 UserHandle user = new UserHandle(userId); 980 981 // Limit the number of notifications that any given package except the android 982 // package can enqueue. Prevents DOS attacks and deals with leaks. 983 if (!isSystemNotification) {很显然我们进入了:enqueueNotificationInternal,该函数太长,不复制了就,~_~。这个函数中实现了不少功能,如是否播放声音,是否震动。最后找到控制呼吸灯的位置在这个方法中:
- 1321 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
- 1322 && canInterrupt) {
- 1323 mLights.add(r);
- 1324 updateLightsLocked();
- 1325 } else {
- 1326 if (old != null
- 1327 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {
- 1328 updateLightsLocked();
- 1329 }
- 1330 }
1321 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 1322 && canInterrupt) { 1323 mLights.add(r); 1324 updateLightsLocked(); 1325 } else { 1326 if (old != null 1327 && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) { 1328 updateLightsLocked(); 1329 } 1330 }很显然,关键点就是:updateLightsLocked()。进入之后会有一系列判断、赋值之类操作。之后进入:
- private LightsService.Light mNotificationLight;
- 1602 if (mNotificationPulseEnabled) {
- 1603 // pulse repeatedly
- 1604 ///M: log lights information
- 1605 Log.d(TAG, “notification setFlashing ledOnMS = ”+ledOnMS + ” ledOffMS = ”+ ledOffMS);
- 1606 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,
- 1607 ledOnMS, ledOffMS);
- 1608 ///M:
- 1609 } else {
- 1610 // pulse only once
- 1611 mNotificationLight.pulse(ledARGB, ledOnMS);
- 1612 }
private LightsService.Light mNotificationLight; 1602 if (mNotificationPulseEnabled) { 1603 // pulse repeatedly 1604 ///M: log lights information 1605 Log.d(TAG, "notification setFlashing ledOnMS = "+ledOnMS + " ledOffMS = "+ ledOffMS); 1606 mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, 1607 ledOnMS, ledOffMS); 1608 ///M: 1609 } else { 1610 // pulse only once 1611 mNotificationLight.pulse(ledARGB, ledOnMS); 1612 }然后,我们开始进入LightsService。
(4).LightsService
LightsService的位置如下:
frameworks/base/services/java/com/android/server/LightsService.java
在LightsService中,通过方法:setFlashing 调用:setLightLocked,最终到达了JNI的setLight_native;
- 116 private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
- 117 if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
- 118 if (DEBUG) Slog.v(TAG, “setLight #” + mId + ”: color=#”
- 119 + Integer.toHexString(color));
- 120 mColor = color;
- 121 mMode = mode;
- 122 mOnMS = onMS;
- 123 mOffMS = offMS;
- 124 setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
- 125 }
- 126 }
116 private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) { 117 if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) { 118 if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#" 119 + Integer.toHexString(color)); 120 mColor = color; 121 mMode = mode; 122 mOnMS = onMS; 123 mOffMS = offMS; 124 setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); 125 } 126 }2.JNI
通过方法setLight_native,进入到了JNI层中,位置如下:
frameworks/base/services/jni/com_android_server_LightsService.cpp
在方法:setLight_native中,一样的进行了相关的判断、接受上层赋值.之后根据参数,调用了对应的set_light:
- 127 ALOGD(“setLight_native: light=%d, colorARGB=0x%x, flashMode=%d, onMS=%d, offMS=%d, brightnessMode=%d”,
- 128 light, colorARGB, flashMode, onMS, offMS, brightnessMode);
- 129
- 130#if defined(MTK_AAL_SUPPORT)
- 131 if (light == LIGHT_INDEX_BACKLIGHT) {
- 132 if (AALClient::getInstance().setBacklightColor(colorARGB & 0x00ffffff) == 0)
- 133 return;
- 134 ALOGW(“Fail to set backlight from AAL service”);
- 135 }
- 136#endif
- 137
- 138 devices->lights[light]->set_light(devices->lights[light], &state);
127 ALOGD("setLight_native: light=%d, colorARGB=0x%x, flashMode=%d, onMS=%d, offMS=%d, brightnessMode=%d", 128 light, colorARGB, flashMode, onMS, offMS, brightnessMode); 129 130#if defined(MTK_AAL_SUPPORT) 131 if (light == LIGHT_INDEX_BACKLIGHT) { 132 if (AALClient::getInstance().setBacklightColor(colorARGB & 0x00ffffff) == 0) 133 return; 134 ALOGW("Fail to set backlight from AAL service"); 135 } 136#endif 137 138 devices->lights[light]->set_light(devices->lights[light], &state);最后通过set_light转入了HAL层。
3.HAL
呼吸灯的HAL层对应位置如下:
mediatek/hardware/liblights/lights.c
我们在NotificationManager下来的set_light对应为:
open_lights下的:
- 584 }
- 585 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
- 586 set_light = set_light_notifications;
- 587 }
584 } 585 else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) { 586 set_light = set_light_notifications; 587 }进入set_light_notifications,通过如下调用:
set_light_notifications —-> handle_speaker_battery_locked —-> set_speaker_light_locked
在函数set_speaker_light_locked中,最后判断我们要控制的led是red,green还是blue,从我的范例上看,我传入的led参数为0xffff0000,对应为red。于是,进入如下的 red:
- 465 if (red) {
- 466 blink_green(0, 0, 0);
- 467 blink_blue(0, 0, 0);
- 468 blink_red(red, onMS, offMS);
- 469 }
- 470 else if (green) {
- 471 blink_red(0, 0, 0);
- 472 blink_blue(0, 0, 0);
- 473 blink_green(green, onMS, offMS);
- 474 }
- 475 else if (blue) {
- 476 blink_red(0, 0, 0);
- 477 blink_green(0, 0, 0);
- 478 blink_blue(blue, onMS, offMS);
- 479 }
- 480 else {
- 481 blink_red(0, 0, 0);
- 482 blink_green(0, 0, 0);
- 483 blink_blue(0, 0, 0);
- 484 }
465 if (red) { 466 blink_green(0, 0, 0); 467 blink_blue(0, 0, 0); 468 blink_red(red, onMS, offMS); 469 } 470 else if (green) { 471 blink_red(0, 0, 0); 472 blink_blue(0, 0, 0); 473 blink_green(green, onMS, offMS); 474 } 475 else if (blue) { 476 blink_red(0, 0, 0); 477 blink_green(0, 0, 0); 478 blink_blue(blue, onMS, offMS); 479 } 480 else { 481 blink_red(0, 0, 0); 482 blink_green(0, 0, 0); 483 blink_blue(0, 0, 0); 484 }进入了red函数之后,重点如下:
上层传下来的的level值为0,则直接关闭RED_LED_FILE(char const*const RED_LED_FILE = “/sys/class/leds/red/brightness”)
- 248 if (nowStatus == 0) {
- 249 write_int(RED_LED_FILE, 0);
- 250 }
248 if (nowStatus == 0) { 249 write_int(RED_LED_FILE, 0); 250 }上层传下的来的参数onMS和offMS都有值,则呼吸灯闪烁:
- 251 else if (nowStatus == 1) {
- 252// write_int(RED_LED_FILE, level); // default full brightness
- 253 write_str(RED_TRIGGER_FILE, “timer”);
- 254 while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {
- 255 ALOGD(“RED_DELAY_OFF_FILE doesn’t exist or cannot write!!\n”);
- 256 led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs
- 257 i++;
- 258 }
- 259 write_int(RED_DELAY_OFF_FILE, offMS);
- 260 write_int(RED_DELAY_ON_FILE, onMS);
- 261 }
251 else if (nowStatus == 1) { 252// write_int(RED_LED_FILE, level); // default full brightness 253 write_str(RED_TRIGGER_FILE, "timer"); 254 while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) { 255 ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n"); 256 led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs 257 i++; 258 } 259 write_int(RED_DELAY_OFF_FILE, offMS); 260 write_int(RED_DELAY_ON_FILE, onMS); 261 }其他情况下,我们直接就点亮红色呼吸灯:
- 262 else {
- 263 write_str(RED_TRIGGER_FILE, “none”);
- 264 write_int(RED_LED_FILE, 255); // default full brightness
- 265 }
262 else { 263 write_str(RED_TRIGGER_FILE, "none"); 264 write_int(RED_LED_FILE, 255); // default full brightness 265 }4.小结
到处Andoid上层的呼吸灯基本上就这个。
在HAL中最后,点亮,关闭和闪烁呼吸灯,点亮和关闭呼吸灯都是直接操作设备接口: RED_LED_FILE = “/sys/class/leds/red/brightness”;
闪烁则相对复杂一些,接下来驱动部分就以闪烁为为范例进行讲解。