先给出问题:Android从O升级到P之后,未接来电无呼吸灯闪烁提示了。
处理未接来电的逻辑在platform/package/service/Telecomm里面,有未接来电时,会向系统发送通知,具体代码位置:
Telecomm/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
private void showMissedCallNotification(@NonNull CallInfo callInfo, UserHandle userHandle) {
Log.i(this, "showMissedCallNotification: userHandle=%d", userHandle.getIdentifier());
mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0));
int missCallCounts = mMissedCallCounts.get(userHandle).incrementAndGet();
if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
sendNotificationThroughDefaultDialer(callInfo, userHandle);
return;
}
final int titleResId;
final String expandedText; // The text in the notification's line 1 and 2.
// Display the first line of the notification:
// 1 missed call: <caller name || handle>
// More than 1 missed call: <number of calls> + "missed calls"
if (missCallCounts == 1) {
expandedText = getNameForMissedCallNotification(callInfo);
CallerInfo ci = callInfo.getCallerInfo();
if (ci != null && ci.userType == CallerInfo.USER_TYPE_WORK) {
titleResId = R.string.notification_missedWorkCallTitle;
} else {
titleResId = R.string.notification_missedCallTitle;
}
} else {
titleResId = R.string.notification_missedCallsTitle;
expandedText =
mContext.getString(R.string.notification_missedCallsMsg, missCallCounts);
}
// Create a public viewable version of the notification, suitable for display when sensitive
// notification content is hidden.
// We use user's context here to make sure notification is badged if it is a managed user.
Context contextForUser = getContextForUser(userHandle);
Notification.Builder publicBuilder = mNotificationBuilderFactory.getBuilder(contextForUser);
publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
.setColor(mContext.getResources().getColor(R.color.theme_color))
.setWhen(callInfo.getCreationTimeMillis())
.setShowWhen(true)
// Show "Phone" for notification title.
.setContentTitle(mContext.getText(R.string.userCallActivityLabel))
// Notification details shows that there are missed call(s), but does not reveal
// the missed caller information.
.setContentText(mContext.getText(titleResId))
.setContentIntent(createCallLogPendingIntent(userHandle))
.setAutoCancel(true)
.setDeleteIntent(createClearMissedCallsPendingIntent(userHandle));
// Create the notification suitable for display when sensitive information is showing.
Notification.Builder builder = mNotificationBuilderFactory.getBuilder(contextForUser);
builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
.setColor(mContext.getResources().getColor(R.color.theme_color))
.setWhen(callInfo.getCreationTimeMillis())
.setShowWhen(true)
.setContentTitle(mContext.getText(titleResId))
.setContentText(expandedText)
.setContentIntent(createCallLogPendingIntent(userHandle))
.setAutoCancel(true)
.setDeleteIntent(createClearMissedCallsPendingIntent(userHandle))
// Include a public version of the notification to be shown when the missed call
// notification is shown on the user's lock screen and they have chosen to hide
// sensitive notification information.
.setPublicVersion(publicBuilder.build())
.setChannelId(NotificationChannelManager.CHANNEL_ID_MISSED_CALLS);
Uri handleUri = callInfo.getHandle();
String handle = callInfo.getHandleSchemeSpecificPart();
// Add additional actions when there is only 1 missed call, like call-back and SMS.
if (missCallCounts == 1) {
Log.d(this, "Add actions with number %s.", Log.piiHandle(handle));
if (!TextUtils.isEmpty(handle)
&& !TextUtils.equals(handle, mContext.getString(R.string.handle_restricted))) {
builder.addAction(R.drawable.ic_phone_24dp,
mContext.getString(R.string.notification_missedCall_call_back),
createCallBackPendingIntent(handleUri, userHandle));
if (canRespondViaSms(callInfo)) {
builder.addAction(R.drawable.ic_message_24dp,
mContext.getString(R.string.notification_missedCall_message),
createSendSmsFromNotificationPendingIntent(handleUri, userHandle));
}
}
Bitmap photoIcon = callInfo.getCallerInfo() == null ?
null : callInfo.getCallerInfo().cachedPhotoIcon;
if (photoIcon != null) {
builder.setLargeIcon(photoIcon);
} else {
Drawable photo = callInfo.getCallerInfo() == null ?
null : callInfo.getCallerInfo().cachedPhoto;
if (photo != null && photo instanceof BitmapDrawable) {
builder.setLargeIcon(((BitmapDrawable) photo).getBitmap());
}
}
} else {
Log.d(this, "Suppress actions. handle: %s, missedCalls: %d.", Log.piiHandle(handle),
missCallCounts);
}
Notification notification = builder.build();
//这里会配置是否闪烁呼吸灯
configureLedOnNotification(notification);
Log.i(this, "Adding missed call notification for %s.", Log.pii(callInfo.getHandle()));
long token = Binder.clearCallingIdentity();
try {
mNotificationManager.notifyAsUser(
NOTIFICATION_TAG, MISSED_CALL_NOTIFICATION_ID, notification, userHandle);
} finally {
Binder.restoreCallingIdentity(token);
}
}
配置通知是否显示灯光
private void configureLedOnNotification(Notification notification) {
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
notification.defaults |= Notification.DEFAULT_LIGHTS;
}
到此发送端已完成,接下来看看处理部分。
处理是否显示呼吸灯的逻辑在NotificationManagerService.java中。
处理流程 enqueueNotificationInternal ==> EnqueueNotificationRunnable ==> PostNotificationRunnable ==> buzzBeepBlinkLocked ==> updateLightsLocked
其中最后一步updateLightsLocked 就是控制呼吸灯的地方。
但是我遇到的问题是在上一层调用buzzBeepBlinkLocked中,贴出部分代码。系统维护了一个需要显示呼吸灯的通知容器,即mLights。只要mLights不为空,那么关掉屏幕的时候就会有呼吸灯显示。
void buzzBeepBlinkLocked(NotificationRecord record) {
......
if (wasBuzz && !hasValidVibrate) {
clearVibrateLocked();
}
// light
// release the light
boolean wasShowLights = mLights.remove(key);
if (canShowLightsLocked(record, aboveThreshold)) {
mLights.add(key);
updateLightsLocked();
if (mUseAttentionLight) {
mAttentionLight.pulse();
}
blink = true;
} else if (wasShowLights) {
updateLightsLocked();
}
......
}
在调用canShowLightsLocked的时候返回了false,导致当前通知的key未被加入到mLights中,继续看看这个函数的实现
boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
// device lacks light
if (!mHasLight) {
return false;
}
// user turned lights off globally
if (!mNotificationPulseEnabled) {
return false;
}
// the notification/channel has no light
if (record.getLight() == null) {
return false;
}
// unimportant notification
if (!aboveThreshold) {
return false;
}
// suppressed due to DND
if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
return false;
}
// Suppressed because it's a silent update
final Notification notification = record.getNotification();
if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
return false;
}
// Suppressed because another notification in its group handles alerting
if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
return false;
}
// not if in call or the screen's on
if (mInCall || mScreenOn) {
return false;
}
return true;
}
每一种条件都写了注释,十分清晰 ,看上去没什么问题,但是最后一个条件中在mScreenOn的时候则不显示呼吸灯,问题就出在此处。当有未接来电的时候,屏幕一定是打开的,因此未接来电就不可能满足这个条件。估计修改者原意是当屏幕打开的时候,那么说明用户正在操作设备,就不显示呼吸灯提示。忽略了未接来电这种特殊情形。
因此只需要对未接来电进行单独处理即可。我是通过Notification中的channel id来区别未接来电的。
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -264,6 +264,10 @@ public class NotificationManagerService extends SystemService {
// notification led colour blue
private static final int LED_BLUE = 0x000000ff;
+ // channel id for missed call, it was defined in
+ // packages/apps/Dialer/java/com/android/dialer/notification/NotificationChannelId.java
+ private static final String CHANNEL_ID_MISSED_CALL = "phone_missed_call";
+
// ranking thread messages
private static final int MESSAGE_RECONSIDER_RANKING = 1000;
private static final int MESSAGE_RANKING_SORT = 1001;
@@ -4982,7 +4986,12 @@ public class NotificationManagerService extends SystemService {
}
// not if in call or the screen's on
if (mInCall || mScreenOn) {
- return false;
+ // When missed a phone call, "mScreenOn" will be always true
+ // at that time, so if it was a notification of MISSED_CALL, we allow the led show
+ // even if mScreenOn was true.
+ if (mIncall || !CHANNEL_ID_MISSED_CALL.equals(record.getChannel().getId())) {
+ return false;
+ }
}
return true;
修改后验证成功。
希望能对遇到同样问题的朋友有所帮助。
原文地址:https://blog.csdn.net/chengchaooppo/article/details/105529783