FLAG_SHOW_LIGHTS //三色灯提醒
FLAG_ONGOING_EVENT //发起事件
FLAG_INSISTENT //振铃音将持续到Notification取消或Notification窗口打开
FLAG_ONLY_ALERT_ONCE //发起Notification后,振铃音或振动均只执行一次
FLAG_AUTO_CANCEL //用户单击后自动消失
FLAG_NO_CLEAR //只有全部清除时,Notification才会清除
FLAG_FOREGROUND_SERVICE //表示正运行的服务)
蓝灯 0 #000000(off) --(splash)-- 255 (on)#0000ff
绿灯 0 #000000(off) --(splash)-- 255 (on)#00ff00
红灯 0 #000000(off) --(splash)-- 255 (on)#ff0000
on led 232 (0.5, 5)
正常调用通知的流程
1.调用 NotificationManager.java notify方法
public void notify(int id, Notification notification)
{
notify(null, id, notification);
}
public void notify(String tag, int id, Notification notification)
{
notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
}
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
{
int[] idOut = new int[1];
//这里获取 INotificationManager对象, IBinder对象NotificationManagerService
INotificationManager service = getService();
String pkg = mContext.getPackageName();
// Fix the notification as best we can. 尽可能的保存notification的一些信息
Notification.addFieldsFromContext(mContext, notification);
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
if (StrictMode.vmFileUriExposureEnabled()) {
notification.sound.checkFileUriExposed("Notification.sound");
}
}
fixLegacySmallIcon(notification, pkg);
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (notification.getSmallIcon() == null) {
throw new IllegalArgumentException("Invalid notification (no valid small icon): "
+ notification);
}
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
final Notification copy = Builder.maybeCloneStrippedForDelivery(notification);
try {
//通过这里,就调入到了 framework 中的 enqueueNotificationWithTag
//通过aidl接口调用到 NotificationManagerService中的enqueueNotificationWithTag方法中
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
copy, idOut, user.getIdentifier());
if (id != idOut[0]) {
Log.v(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
2. 进入NotificationManagerService
//上面如何调用
2.0 在frameworks/base/core/Java/android/app/NotificationManager.java中
static public INotificationManager getService()
{
if (sService != null) {
return sService;
}
IBinder b = ServiceManager.getService("notification");
sService = INotificationManager.Stub.asInterface(b);
return sService;
}
//notificationmanagerservice.setimportance 设置 通知的重要级别
2.1.enqueueNotificationInternal (比如在其中对 importance 格式化 final NotificationRecord r = new NotificationRecord(getContext(), n);)
//enqueue说明notification的内部维护着一个队列,接收不同apk发送的通知请求。
2.2 EnqueueNotificationRunnable 中的 run 方法 (判断是不是系统应用 , 放入 mNotificationsByKey.put(n.getKey(), r); )
n.getKey()==0|com.android.server.telecom|1|null|1000==r==NotificationRecord(0x046e45d0: pkg=com.android.server.telecom user=UserHandle{0} id=1 tag=null
importance=2 key=0|com.android.server.telecom|1|null|1000: Notification(pri=0 contentView=null vibrate=null sound=null defaults=0x4 flags=0x11
color=0xff2a56c6 actions=2 vis=PRIVATE publicVersion=Notification(pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x10 color=0xff2a56c6 vis=PRIVATE)))
n.getKey()--0|com.example.aaaa|1|null|10099==r==NotificationRecord(0x0825ef88: pkg=com.example.aaaa user=UserHandle{0} id=1 tag=null importance=2 key=0|
com.example.aaaa|1|null|10099: Notification(pri=0 contentView=null vibrate=null sound=null tick defaults=0x0 flags=0x10 color=0x00000000 vis=PRIVATE))
2.3 buzzBeepBlinkLocked (对 Importance 等判断,添加到lights **)
2.4 updateLightsLocked
android/frameworks/base/core/java/android/app/Notification.java
public Builder(Context context) {
this(context, null);
}
/**
* @hide
*/
public Builder(Context context, Notification toAdopt) {
mContext = context;
//for drop down menu
isSupportWTRom = context.getResources().getBoolean(R.bool.config_supportWTRom);
if (toAdopt == null) {
mN = new Notification();
if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
}
mN.priority = PRIORITY_DEFAULT; //创建时,默认设置了通知的等级
mN.visibility = VISIBILITY_PRIVATE;
} else {
mN = toAdopt;
if (mN.actions != null) {
Collections.addAll(mActions, mN.actions);
}
......
.....
/** 当然也可以通过此方法设置你俄通知的等级
* Set the priority of this notification.
*
* @see Notification#priority
*/
public Builder setPriority(@Priority int pri) {
mN.priority = pri;
return this;
}
}
/android/frameworks/base/services/core/java/com/android/server/notification/NotificationRecord.java
这里,对通知的属性进行了格式化
public final class NotificationRecord {
NotificationUsageStats.SingleNotificationStats stats;
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn)
{
this.sbn = sbn;
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
mContext = context;
//得到 stats
stats = new NotificationUsageStats.SingleNotificationStats();
mImportance = defaultImportance(); //此处格式化 importance
}
// 下面的级别常量引入的是 /frameworks/base/core/java/android/service/notification/NotificationListenerService.java NotificationListenerService.Ranking 中定义的
// 而 NotificationListenerService.Ranking 实质是引用的 /frameworks/base/core/java/android/app/NotificationManager.java
//import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MIN;
//import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
//import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
//import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
//import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW;
//import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_MAX;
private int defaultImportance() {
final Notification n = sbn.getNotification();
int importance = IMPORTANCE_DEFAULT;
// Migrate notification flags to scores
if (0 != (n.flags & Notification.FLAG_HIGH_PRIORITY)) {
n.priority = Notification.PRIORITY_MAX;
}
//对 importance 也就是 proity 格式化 1
switch (n.priority) {
case Notification.PRIORITY_MIN:
importance = IMPORTANCE_MIN;
break;
case Notification.PRIORITY_LOW:
importance = IMPORTANCE_LOW;
break;
case Notification.PRIORITY_DEFAULT:
importance = IMPORTANCE_DEFAULT;
break;
case Notification.PRIORITY_HIGH:
importance = IMPORTANCE_HIGH;
break;
case Notification.PRIORITY_MAX:
importance = IMPORTANCE_MAX;
break;
}
stats.requestedImportance = importance;
boolean isNoisy = (n.defaults & Notification.DEFAULT_SOUND) != 0
|| (n.defaults & Notification.DEFAULT_VIBRATE) != 0
|| n.sound != null
|| n.vibrate != null;
stats.isNoisy = isNoisy;
//对 importance 也就是 proity 格式化 2
if (!isNoisy && importance > IMPORTANCE_LOW) {
importance = IMPORTANCE_LOW;
}
if (isNoisy) {
if (importance < IMPORTANCE_DEFAULT) {
importance = IMPORTANCE_DEFAULT;
}
}
if (n.fullScreenIntent != null) {
importance = IMPORTANCE_MAX;
}
//格式化后将属性插入
stats.naturalImportance = importance;
android.util.Log.e(TAG,"NotificationRecord ---> defaultImportance "
+ "; sbn.getKey() = " + sbn.getKey()
+ "; sbn.getNotification().priority = " + sbn.getNotification().priority
+ "; importance = " + importance
+ "; mPackagePriority = " + mPackagePriority
+ "; mContactAffinity = " + mContactAffinity);
return importance;
}
}
/frameworks/base/core/java/android/service/notification/NotificationListenerService.java ,定义的常量,上面引用
public class NotificationManager{
private static String TAG = "NotificationManager";
........
/** Value signifying that the user has not expressed a per-app visibility override value.
* @hide */
public static final int VISIBILITY_NO_OVERRIDE = -1000;
/**
* Value signifying that the user has not expressed an importance.
*
* This value is for persisting preferences, and should never be associated with
* an actual notification.
*/
public static final int IMPORTANCE_UNSPECIFIED = -1000;
/**
* A notification with no importance: shows nowhere, is blocked.
*/
public static final int IMPORTANCE_NONE = 0;
/**
* Min notification importance: only shows in the shade, below the fold.
*/
public static final int IMPORTANCE_MIN = 1;
/**
* Low notification importance: shows everywhere, but is not intrusive.
*/
public static final int IMPORTANCE_LOW = 2;
/**
* Default notification importance: shows everywhere, allowed to makes noise,
* but does not visually intrude.
*/
public static final int IMPORTANCE_DEFAULT = 3;
/**
* Higher notification importance: shows everywhere, allowed to makes noise and peek.
*/
public static final int IMPORTANCE_HIGH = 4;
/**
* Highest notification importance: shows everywhere, allowed to makes noise, peek, and
* use full screen intents.
*/
public static final int IMPORTANCE_MAX = 5;
........
}
/frameworks/base/core/java/android/service/notification/ NotificationManagerService
public class NotificationManagerService extends SystemService {
private boolean mNotificationPulseEnabled; // if notify light
private SettingsObserver mSettingsObserver;
@Override
public void onStart() {
........
........
mSettingsObserver = new SettingsObserver(mHandler);
}
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri NOTIFICATION_SOUND_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_SOUND);
private final Uri NOTIFICATION_RATE_LIMIT_URI
= Settings.Global.getUriFor(Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE);
private final Uri NOTIFICATION_LIGHT_SETTING
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_SETTING);
SettingsObserver(Handler handler) {
super(handler);
}
void observe() {
ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_SOUND_URI,
false, this, UserHandle.USER_ALL);
....
...
update(null);
}
@Override public void onChange(boolean selfChange, Uri uri) {
update(uri);
}
public void update(Uri uri) {
ContentResolver resolver = getContext().getContentResolver();
if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
boolean pulseEnabled = Settings.System.getInt(resolver,
Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
if (mNotificationPulseEnabled != pulseEnabled) {
mNotificationPulseEnabled = pulseEnabled;
updateNotificationPulse();
}
}
.......
.......
}
}
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_ON)) {
// Keep track of screen on/off state, but do not turn off the notification light
// until user passes through the lock screen or views the notification.
mScreenOn = true;
2 updateNotificationPulse(); //控制led
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
mScreenOn = false;
updateNotificationPulse(); //控制led
} else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
updateNotificationPulse(); //控制led
} else if( action.equals( Intent.ACTION_BATTERY_CHANGED) ){
batterylevel=intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);
batteryStatus=intent.getIntExtra(BatteryManager.EXTRA_STATUS,0);
updateBatteryLightsLocked(batterylevel,batteryStatus); //控制led
}else if (action.equals(Intent.ACTION_USER_STOPPED)) {
.....
.....
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
/** this let screen on , no light
mNotificationLight.turnOff();
if (mStatusBar != null) {
mStatusBar.notificationLightOff();
}
**/
........
........
}
void buzzBeepBlinkLocked(NotificationRecord record) {
boolean buzz = false;
boolean beep = false;
boolean blink = false;
final Notification notification = record.sbn.getNotification();
final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED? 在这里得到通知的重要级别
// IMPORTANCE_DEFAULT = 3
final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
........
........
// light
// release the light
boolean wasShowLights = mLights.remove(key);
// public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1 << 0; 1
// public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 1;
//Log.d(TAG,"record.getSuppressedVisualEffects()==="+ record.getSuppressedVisualEffects());
add notification.flags |= Notification.FLAG_SHOW_LIGHTS ;
// if should light
if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
&& ((record.getSuppressedVisualEffects()
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
Log.d(TAG,"add lights 数组");
mLights.add(key);
updateLightsLocked();
if (mUseAttentionLight) {
mAttentionLight.pulse();
}
blink = true;
} else if (wasShowLights) {
updateLightsLocked();
}
......
......
}
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);
Log.d(TAG,"updateLightsLocked get owner: "+ owner+ "==ledNotification=="+ ledNotification );
if (ledNotification == null) {
Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
mLights.remove(owner);
}
}
Log.d(TAG,"updateLightsLocked ledNotification:"+ ledNotification+ " mInCall:"+ mInCall + " mScreenOn:"+ mScreenOn);
// Don't flash while we are in a call or screen is on
if (ledNotification == null || mInCall || mScreenOn) {
//清除所有
//mLights.clear();
//mNotificationLight.turnOff();
ledstatus = 1;
//if (mStatusBar != null) {
// mStatusBar.notificationLightOff();
//}
/** here can do other thing on screen off
if(!mScreenOn && mNotificationPulseEnabled){
int resTEL = 0, resSMS = 0 , resMMS = 0;
Cursor csrSMS = getContext().getContentResolver().query(Uri.parse("content://sms"), null,"type = 1 and read = 0", null, null);
if (csrSMS != null) {
resSMS = csrSMS.getCount();
csrSMS.close();
}
Cursor csrMMS = getContext().getContentResolver().query(Uri.parse("content://mms/inbox"),null, "read = 0", null, null);
if (csrMMS != null) {
resMMS = csrMMS.getCount();
csrMMS.close();
}
Cursor csrTEL = getContext().getContentResolver().query(CallLog.Calls.CONTENT_URI, new String[] {Calls.TYPE}, " type=? and new=?",
new String[] {Calls.MISSED_TYPE + "", "1"}, "date desc");
if (csrTEL != null) {
resTEL = csrTEL.getCount();
csrTEL.close();
}
if(resTEL + resSMS + resMMS > 0 ){
ledstatus = 2;
mNotificationLight.setColor(0xFF00E800);
}else{
ledstatus = 1;
}
}
**/
updateBatteryLightsLocked(batterylevel,batteryStatus);
} else {
final Notification ledno = ledNotification.sbn.getNotification();
int ledARGB = ledno.ledARGB;
int ledOnMS = ledno.ledOnMS;
int ledOffMS = ledno.ledOffMS;
if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
ledARGB = mDefaultNotificationColor;
ledOnMS = mDefaultNotificationLedOn;
ledOffMS = mDefaultNotificationLedOff;
}
// mordify led req
if (mNotificationPulseEnabled) {
// pulse repeatedly
Log.d(TAG,"set light Flashing");
//mNotificationLight.turnOff();
mNotificationLight.setColor(0xFF00E800);
ledstatus = 2 ;
//mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
// ledOnMS, ledOffMS);
}
//if (mStatusBar != null) {
// let SystemUI make an independent decision
// mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
//}
}
}
//清除
@Override
public void clearEffects() {
synchronized (mNotificationList) {
if (DBG) Slog.d(TAG, "clearEffects");
clearSoundLocked();
clearVibrateLocked();
clearLightsLocked();
}
}
private void clearLightsLocked() {
// light
mLights.clear();
updateLightsLocked();
}
//battery
private int ledstatus = 0 ; // 0 : 0ff 1 :no pulse 2 :pulse
public void updateBatteryLightsLocked(int level, int status) {
if(ledstatus != 2){ //== when no flash
if (level < mLowBatteryWarningLevel) {
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
// Solid red when battery is charging
//mBatteryLight.setColor(mBatteryLowARGB1);
else
mNotificationLight.setColor(mBatteryLowARGB);
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
if (level >= 15) {
// Solid green when full or charging and nearly full
mNotificationLight.setColor(mBatteryFullARGB);
}
}else {
// No lights if not charging and not low
mNotificationLight.turnOff();
}
}else{
//do nothing
}
}
}
4.在frameworks/base/services/core/java/com/android/server/lights/LightsService.java中setFlashing方法里面setLightLocked来实现等效。
所有对灯效的操作都是通过setLightLocked实现。如下
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
+ Integer.toHexString(color));
mColor = color;
mMode = mode;
mOnMS = onMS;
mOffMS = offMS;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", " + color + ")");
try {
setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
}
private int mId;
private int mColor;
private int mMode;
private int mOnMS;
private int mOffMS;
private boolean mFlashing;
}
setLight_native最终调用了一个native方法,到JNI层去了。
在frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp中还是将LED灯颜色,模式,时间写入state中向下继续传递
static void setLight_native(JNIEnv *env, jobject clazz, jlong ptr,
jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
Devices* devices = (Devices*)ptr;
light_state_t state;
if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
return ;
}
memset(&state, 0, sizeof(light_state_t));
state.color = colorARGB;
state.flashMode = flashMode;
state.flashOnMS = onMS;
state.flashOffMS = offMS;
state.brightnessMode = brightnessMode;
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
devices->lights[light]->set_light(devices->lights[light], &state);
}
}
最后通过 hardwareinterface 控制led灯颜色节点。
小米, 亮屏后, 所有通知闪烁清除
魅族, 华为, 未接来电通知, 未读短信通知, 三方通知亮屏清除
普通三方通知 def 为 3 , 格式化后 为 2