当前位置: 代码迷 >> Android >> Android按键灯,指示灯小结【Android源码解析十一】
  详细解决方案

Android按键灯,指示灯小结【Android源码解析十一】

热度:99   发布时间:2016-04-28 08:01:06.0
Android按键灯,指示灯总结【Android源码解析十一】

       Android中有各种灯,背光灯,按键灯,指示灯,等等;前几天修改了这部分代码,整理下思路,其实都不难;

       首先,来说说指示灯(提示灯),即未接电话,未接短信的时候,会闪灯,这个其实就是NotificationManager这个类中的notify()方法来处理的;流程简单来过一下:


       Step 1:从应用层发送的notify(),到framework层被NotificationManager.java这个类接受了,来看看这个notify()这个方法:

public void notify(int id, Notification notification)    {        notify(null, id, notification);    }

 public void notify(String tag, int id, Notification notification)    {        int[] idOut = new int[1];        INotificationManager service = getService();        String pkg = mContext.getPackageName();        if (notification.sound != null) {            notification.sound = notification.sound.getCanonicalUri();        }        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");        try {            service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,                    UserHandle.myUserId());            if (id != idOut[0]) {                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);            }        } catch (RemoteException e) {        }    }
重点关注这个enqueueNotificationWithTag()这个方法,这个方法首先会取是否传递过来了声音的Uri,如果传递了,就保存下来,给等会播放用;


     Step 2:enqueueNotificationWithTag()这个方法调用到了NotificationManagerService.java这个类中去了,来看看这个方法:

public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,            int[] idOut, int userId)    {        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),                tag, id, notification, idOut, userId);    }

// Not exposed via Binder; for system use only (otherwise malicious apps could spoof the    // uid/pid of another application)    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,            String tag, int id, Notification notification, int[] idOut, int userId)    {        if (DBG) {            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification + ", notification.ledARGB : "+ notification.ledARGB);        }        checkCallerIsSystemOrSameApp(pkg);        final boolean isSystemNotification = ("android".equals(pkg));        userId = ActivityManager.handleIncomingUser(callingPid,                callingUid, userId, true, false, "enqueueNotification", pkg);        UserHandle user = new UserHandle(userId);        // Limit the number of notifications that any given package except the android        // package can enqueue.  Prevents DOS attacks and deals with leaks.        if (!isSystemNotification) {            synchronized (mNotificationList) {                int count = 0;                int eldestIdx = 0;                long eldestTime = 0;                final int N = mNotificationList.size();                for (int i=0; i<N; i++) {                    NotificationRecord r = mNotificationList.get(i);                    if (r.pkg.equals(pkg) && r.userId == userId) {                        if (count == 0) {                            eldestTime = r.notification.when;                            eldestIdx = i;                        }                        else {                            if (r.notification.when < eldestTime) {                                eldestTime = r.notification.when;                                eldestIdx = i;                            }                        }                        count++;                        if (count >= MAX_PACKAGE_NOTIFICATIONS) {                            Slog.e(TAG, "Package has already posted " + count                                    + " notifications.  Not showing more.  package=" + pkg);                            //return;                            // [ALPS00447419] SystemUI OOM: remove eldest entry                            r = mNotificationList.get(eldestIdx);                            mNotificationList.remove(eldestIdx);                            cancelNotificationLocked(r, true);                            break;                        }                    }                }            }        }        // This conditional is a dirty hack to limit the logging done on        //     behalf of the download manager without affecting other apps.        if (!pkg.equals("com.android.providers.downloads")                || Log.isLoggable("DownloadManager", Log.VERBOSE)) {            EventLog.writeEvent(EventLogTags.NOTIFICATION_ENQUEUE, pkg, id, tag, userId,                    notification.toString());        }        if (pkg == null || notification == null) {            throw new IllegalArgumentException("null not allowed: pkg=" + pkg                    + " id=" + id + " notification=" + notification);        }        if (notification.icon != 0) {            if (notification.contentView == null) {                throw new IllegalArgumentException("contentView required: pkg=" + pkg                        + " id=" + id + " notification=" + notification);            }        }        // === Scoring ===        // 0. Sanitize inputs        notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX);        // Migrate notification flags to scores        if (0 != (notification.flags & Notification.FLAG_HIGH_PRIORITY)) {            if (notification.priority < Notification.PRIORITY_MAX) notification.priority = Notification.PRIORITY_MAX;        } else if (SCORE_ONGOING_HIGHER && 0 != (notification.flags & Notification.FLAG_ONGOING_EVENT)) {            if (notification.priority < Notification.PRIORITY_HIGH) notification.priority = Notification.PRIORITY_HIGH;        }        // 1. initial score: buckets of 10, around the app         int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]        // 2. Consult external heuristics (TBD)        // 3. Apply local rules        // blocked apps        if (ENABLE_BLOCKED_NOTIFICATIONS && !isSystemNotification && !areNotificationsEnabledForPackageInt(pkg)) {            score = JUNK_SCORE;            Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request.");        }        if (DBG) {            Slog.v(TAG, "Assigned score=" + score + " to " + notification);        }        if (score < SCORE_DISPLAY_THRESHOLD) {            // Notification will be blocked because the score is too low.            return;        }        // Should this notification make noise, vibe, or use the LED?        final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);        synchronized (mNotificationList) {            NotificationRecord r = new NotificationRecord(pkg, tag, id,                     callingUid, callingPid, userId,                    score,                    notification);            NotificationRecord old = null;            int index = indexOfNotificationLocked(pkg, tag, id, userId);            if (index < 0) {                mNotificationList.add(r);            } else {                old = mNotificationList.remove(index);                mNotificationList.add(index, r);                // Make sure we don't lose the foreground service state.                if (old != null) {                    notification.flags |=                        old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;                }            }            // Ensure if this is a foreground service that the proper additional            // flags are set.            if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {                notification.flags |= Notification.FLAG_ONGOING_EVENT                        | Notification.FLAG_NO_CLEAR;            }            final int currentUser;            final long token = Binder.clearCallingIdentity();            try {                currentUser = ActivityManager.getCurrentUser();            } finally {                Binder.restoreCallingIdentity(token);            }            if (notification.icon != 0) {                final StatusBarNotification n = new StatusBarNotification(                        pkg, id, tag, r.uid, r.initialPid, score, notification, user);                if (old != null && old.statusBarKey != null) {                    r.statusBarKey = old.statusBarKey;                    long identity = Binder.clearCallingIdentity();                    try {                        mStatusBar.updateNotification(r.statusBarKey, n);                    }                    finally {                        Binder.restoreCallingIdentity(identity);                    }                } else {                    long identity = Binder.clearCallingIdentity();                    try {                        r.statusBarKey = mStatusBar.addNotification(n);                        if ((n.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0                                && canInterrupt) {                            mAttentionLight.pulse();                        }                    }                    finally {                        Binder.restoreCallingIdentity(identity);                    }                }                // Send accessibility events only for the current user.                if (currentUser == userId) {                    sendAccessibilityEvent(notification, pkg);                }            } else {                Slog.e(TAG, "Ignoring notification with icon==0: " + notification);                if (old != null && old.statusBarKey != null) {                    long identity = Binder.clearCallingIdentity();                    try {                        mStatusBar.removeNotification(old.statusBarKey);                    }                    finally {                        Binder.restoreCallingIdentity(identity);                    }                }            }            // If we're not supposed to beep, vibrate, etc. then don't.            // ensure mms can send notification when a phone is calling            if ((((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) || pkg.equals("com.android.mms"))                    && (!(old != null                        && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))                    && (r.userId == UserHandle.USER_ALL ||                        (r.userId == userId && r.userId == currentUser))                    && canInterrupt                    && mSystemReady) {            	///M:             	if (DBG) {                    Log.d(TAG,"pakage="+pkg+",In NotificationMangerService, this notification soud, leds and vibrate enable");                }                final AudioManager audioManager = (AudioManager) mContext                .getSystemService(Context.AUDIO_SERVICE);                // sound                final boolean useDefaultSound =                    (notification.defaults & Notification.DEFAULT_SOUND) != 0;                ///M: log sound information             	if (DBG) {					Log.d(TAG,"useDefaultSound="+useDefaultSound);					Log.d(TAG,"notification.sound="+notification.sound);				}                Uri soundUri = null;                boolean hasValidSound = false;                if (useDefaultSound) {                    soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;                    // check to see if the default notification sound is silent                    ContentResolver resolver = mContext.getContentResolver();                    hasValidSound = Settings.System.getString(resolver,                           Settings.System.NOTIFICATION_SOUND) != null;                } else if (notification.sound != null) {                    soundUri = notification.sound;                    hasValidSound = (soundUri != null);                }                if (hasValidSound) {                    boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;                    int audioStreamType;                    if (notification.audioStreamType >= 0) {                        audioStreamType = notification.audioStreamType;                    } else {                        audioStreamType = DEFAULT_STREAM_TYPE;                    }                    mSoundNotification = r;                    ///M: log sound information                    if (DBG) {						Log.d(TAG,"looping="+looping);						Log.d(TAG,"audioStreamType="+audioStreamType);						Log.d(TAG,"StreamVolume="+audioManager.getStreamVolume(audioStreamType));				    }                    // do not play notifications if stream volume is 0                    // (typically because ringer mode is silent) or if speech recognition is active.                    if ((audioManager.getStreamVolume(audioStreamType) != 0)                            && !audioManager.isSpeechRecognitionActive()) {                        final long identity = Binder.clearCallingIdentity();                        try {                            final IRingtonePlayer player = mAudioService.getRingtonePlayer();                            if (player != null) {                                ///M: [ALPS00461691]No notification sound when it detects wifi networks                                if (user.getIdentifier() == UserHandle.USER_ALL) {                                    user = UserHandle.OWNER;                                }                                player.playAsync(soundUri, user, looping, audioStreamType);                            }                        } catch (RemoteException e) {                        } finally {                            Binder.restoreCallingIdentity(identity);                        }                    }                }                // vibrate                ///M: for device manager                 if (DBG) {		            Log.d(TAG,"mDmLock="+mDmLock);		        }                if (mDmLock == false){                // Does the notification want to specify its own vibration?                final boolean hasCustomVibrate = notification.vibrate != null;                // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,                // and no other vibration is specified, we fall back to vibration                final boolean convertSoundToVibration =                           !hasCustomVibrate                        && hasValidSound                        && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);                // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.                final boolean useDefaultVibrate =                        (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;                if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)                        && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) {                    mVibrateNotification = r;                    if (DBG) {                        Log.w(TAG, "set vibrate!");                    }                    if (useDefaultVibrate || convertSoundToVibration) {                        // Escalate privileges so we can use the vibrator even if the notifying app                        // does not have the VIBRATE permission.                        long identity = Binder.clearCallingIdentity();                        try {                            mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern                                                                : mFallbackVibrationPattern,                                ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);                        } finally {                            Binder.restoreCallingIdentity(identity);                        }                    } else if (notification.vibrate.length > 1) {                        // If you want your own vibration pattern, you need the VIBRATE permission                        mVibrator.vibrate(notification.vibrate,                            ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);                    }                }                } // mDmLock == false            }            // this option doesn't shut off the lights            // light            // the most recent thing gets the light            mLights.remove(old);            if (mLedNotification == old) {                mLedNotification = null;            }            //Slog.i(TAG, "notification.lights="            //        + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0));            if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0                    && canInterrupt) {                mLights.add(r);                updateLightsLocked();            } else {                if (old != null                        && ((old.notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0)) {                    updateLightsLocked();                }            }        }        idOut[0] = id;    }
上面这个方法做的操作有点多,代码有300多行。其中有设计播放声音的地方:

 player.playAsync(soundUri, user, looping, audioStreamType);
有是否播放震动的地方:

 mVibrator.vibrate(notification.vibrate,                            ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
最后闪灯的地方在这个逻辑中:

 if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0                    && canInterrupt) {                mLights.add(r);                updateLightsLocked();            } 
重点看updateLightsLocked()这个方法,这个方法里面有逻辑的操作;


       Step 3:updateLightsLocked()这个方法的代码如下:

// lock on mNotificationList    private void updateLightsLocked()    {        // handle notification lights        if (mLedNotification == null) {            // get next notification, if any            int n = mLights.size();            if (n > 0) {                mLedNotification = mLights.get(n-1);            }        }        // Don't flash while we are in a call or screen is on        ///M: we need flash when screen is on        //mScreenOn add by lvmingfei for don't flash while screen is on in 2013-09-20        if (mLedNotification == null || mInCall || mCallRinging)) {            mNotificationLight.turnOff();        } else {            int ledARGB = mLedNotification.notification.ledARGB;            int ledOnMS = mLedNotification.notification.ledOnMS;            int ledOffMS = mLedNotification.notification.ledOffMS;            if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {                ledARGB = mDefaultNotificationColor;                ledOnMS = mDefaultNotificationLedOn;                ledOffMS = mDefaultNotificationLedOff;            }            if (mNotificationPulseEnabled) {                // pulse repeatedly            	///M: log lights information             	Log.d(TAG, "notification setFlashing ledOnMS = "+ledOnMS + " ledOffMS = "+ ledOffMS + ", ledARGB :" + ledARGB);                mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED,                        ledOnMS, ledOffMS);                ///M:             } else {                // pulse only once                mNotificationLight.pulse(ledARGB, ledOnMS);            }        }    }
这段代码中有如下操作,判断一些变量的状态,以决定时候关闭指示灯,还是闪光,还是只闪一次的操作;
电话的状态是否是offhook,或来电的状态,会操作:
mNotificationLight.turnOff();
下面这个条件也很重要:
if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
未解来电,或短信的时候这个值会设置为
notification.defaults |= Notification.DEFAULT_LIGHTS;
这个值是4,那么上面的这个判断就为true了,所以灯的颜色就不是由
mLedNotification.notification.ledARGB;
这个值决定的了,而是由mDefaultNotificationColor这个值决定的,这个值的定义在

mDefaultNotificationColor = resources.getColor(                com.android.internal.R.color.config_defaultNotificationColor);
这个值的定义在framework/base/core/res/res/values/config.xml中定义的,

<color name="config_defaultNotificationColor">#ff0000ff</color>
这个表示是蓝灯;具体想改成其他值;

  1. #ff0000ff 表示蓝灯
  2. #ff00ff00 表示绿灯
  3. #ffff0000 表示红灯
后面的逻辑就调用到LightsService.java中去了,这个类是管理灯的类,(背光灯,按键灯,指示灯等等);


     拓展:

             如果想实现屏幕亮的时候,指示灯灭,屏幕灭的时候指示灯亮;可以监听ACTION_SCREEN_ON/ACTION_SCREEN_OFF的广播,搞一个全局的变量控制下;再调用updateNotificationPulse()这个方法,把变量的判断加载updateNotificationPulse()这个方法的灯亮灭判断的地方即可;


        其次,我们来看看返回键的灯,即按键灯;

        Step 1 :先来看看PowerManagerService.java这个类。按键灯的定义

 private LightsService.Light mButtonLight;
        初始化方法:
mButtonLight = mLightsService.getLight(LightsService.LIGHT_ID_BUTTONS);
 if ( (newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) && (mWakefulness == WAKEFULNESS_AWAKE) && !mIPOShutdown && !mShutdownFlag) {                if ( ( (mWakeLockSummary & WAKE_LOCK_BUTTON_BRIGHT) != 0 ) ||                        ( (mUserActivitySummary & USER_ACTIVITY_BUTTON_BRIGHT) != 0) ) {                    mButtonLight.setBrightness(mScreenBrightness);                    Slog.i(TAG, "setBrightness mButtonLight, mScreenBrightness=" + mScreenBrightness);                } else {                    mButtonLight.turnOff();                    Slog.i(TAG, "setBrightness mButtonLight 0 ===.");                }            } else {                mButtonLight.turnOff();                Slog.i(TAG, "setBrightness mButtonLight else 0.");            }
灯的点亮的方法setBrightness()

灯关闭的方法turnOff()

要想修改按键灯随p-sensor的灯的亮灭同步,可以参考Android4.2中Phone的P-sensor的应用的分析。

然后再加上上述控制灯亮灭的方法就可实现同步;


     总结:灯的亮灭最后都会调用到LightsService.java这个类的,最后通过c代码调用底层的接口实现灯的颜色和闪烁的变化的;
















  相关解决方案