Notification Light

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空白的泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值