diff options
8 files changed, 166 insertions, 5 deletions
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index f3430e7..243ce97 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -49,6 +49,7 @@ interface IStatusBarService void onNotificationClear(String pkg, String tag, int id, int userId); void onNotificationVisibilityChanged( in String[] newlyVisibleKeys, in String[] noLongerVisibleKeys); + void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded); void setSystemUiVisibility(int vis, int mask); void setHardKeyboardEnabled(boolean enabled); void setWindowState(int window, int state); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index dff3f55..87b1f8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -96,7 +96,7 @@ import static com.android.keyguard.KeyguardHostView.OnDismissAction; public abstract class BaseStatusBar extends SystemUI implements CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener, - RecentsComponent.Callbacks { + RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger { public static final String TAG = "StatusBar"; public static final boolean DEBUG = false; public static final boolean MULTIUSER_DEBUG = false; @@ -1035,6 +1035,7 @@ public abstract class BaseStatusBar extends SystemUI implements Context.LAYOUT_INFLATER_SERVICE); row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row, parent, false); + row.setExpansionLogger(this, entry.notification.getKey()); } // the notification inspector (see SwipeHelper.setLongPressListener) @@ -1784,4 +1785,13 @@ public abstract class BaseStatusBar extends SystemUI implements } return contextForUser.getPackageManager(); } + + @Override + public void logNotificationExpansion(String key, boolean userAction, boolean expanded) { + try { + mBarService.onNotificationExpansionChanged(key, userAction, expanded); + } catch (RemoteException e) { + // Ignore. + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index e042a53..4b0af11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -54,6 +54,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private int mMaxExpandHeight; private View mVetoButton; private boolean mClearable; + private ExpansionLogger mLogger; + private String mLoggingKey; + + public interface ExpansionLogger { + public void logNotificationExpansion(String key, boolean userAction, boolean expanded); + } public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); @@ -66,6 +72,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { public void reset() { super.reset(); mRowMinHeight = 0; + final boolean wasExpanded = isExpanded(); mRowMaxHeight = 0; mExpandable = false; mHasUserChangedExpansion = false; @@ -76,6 +83,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mPublicLayout.reset(); mPrivateLayout.reset(); mMaxExpandHeight = 0; + logExpansionEvent(false, wasExpanded); } @Override @@ -131,8 +139,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { */ public void setUserExpanded(boolean userExpanded) { if (userExpanded && !mExpandable) return; + final boolean wasExpanded = isExpanded(); mHasUserChangedExpansion = true; mUserExpanded = userExpanded; + logExpansionEvent(true, wasExpanded); } public boolean isUserLocked() { @@ -156,15 +166,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { * @param expand whether the system wants this notification to be expanded. */ public void setSystemExpanded(boolean expand) { + final boolean wasExpanded = isExpanded(); mIsSystemExpanded = expand; notifyHeightChanged(); + logExpansionEvent(false, wasExpanded); } /** * @param expansionDisabled whether to prevent notification expansion */ public void setExpansionDisabled(boolean expansionDisabled) { + final boolean wasExpanded = isExpanded(); mExpansionDisabled = expansionDisabled; + logExpansionEvent(false, wasExpanded); notifyHeightChanged(); } @@ -302,4 +316,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private NotificationContentView getShowingLayout() { return mShowingPublic ? mPublicLayout : mPrivateLayout; } + + public void setExpansionLogger(ExpansionLogger logger, String key) { + mLogger = logger; + mLoggingKey = key; + } + + + private void logExpansionEvent(boolean userAction, boolean wasExpanded) { + final boolean nowExpanded = isExpanded(); + if (wasExpanded != nowExpanded && mLogger != null) { + mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ; + } + } } diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 6fab37c..3c50947 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -65,6 +65,8 @@ option java_package com.android.server 27501 notification_panel_hidden # when notifications are newly displayed on screen, or disappear from screen 27510 notification_visibility_changed (newlyVisibleKeys|3),(noLongerVisibleKeys|3) +# when notifications are expanded, or contracted +27511 notification_expansion (key|3),(user_action|1),(expanded|1) # when a notification has been clicked 27520 notification_clicked (key|3) diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index b41b478..1b59f52 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -32,4 +32,5 @@ public interface NotificationDelegate { boolean allowDisable(int what, IBinder token, String pkg); void onNotificationVisibilityChanged( String[] newlyVisibleKeys, String[] noLongerVisibleKeys); + void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bedd369..f9230a3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -611,6 +611,18 @@ public class NotificationManagerService extends SystemService { } } } + + @Override + public void onNotificationExpansionChanged(String key, + boolean userAction, boolean expanded) { + EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0); + synchronized (mNotificationList) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r != null) { + r.stats.onExpansionChanged(userAction, expanded); + } + } + } }; private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index adf9516..9b56464 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -200,6 +200,9 @@ public class NotificationUsageStats { public final Aggregate airtimeCount = new Aggregate(); public final Aggregate airtimeMs = new Aggregate(); public final Aggregate posttimeToFirstAirtimeMs = new Aggregate(); + public final Aggregate userExpansionCount = new Aggregate(); + public final Aggregate airtimeExpandedMs = new Aggregate(); + public final Aggregate posttimeToFirstVisibleExpansionMs = new Aggregate(); public AggregatedStats(String key) { this.key = key; @@ -222,6 +225,14 @@ public class NotificationUsageStats { posttimeToFirstAirtimeMs.addSample( singleNotificationStats.posttimeToFirstAirtimeMs); } + if (singleNotificationStats.posttimeToFirstVisibleExpansionMs >= 0) { + posttimeToFirstVisibleExpansionMs.addSample( + singleNotificationStats.posttimeToFirstVisibleExpansionMs); + } + userExpansionCount.addSample(singleNotificationStats.userExpansionCount); + if (singleNotificationStats.airtimeExpandedMs >= 0) { + airtimeExpandedMs.addSample(singleNotificationStats.airtimeExpandedMs); + } } public void dump(PrintWriter pw, String indent) { @@ -247,6 +258,9 @@ public class NotificationUsageStats { indent + " airtimeCount=" + airtimeCount + ",\n" + indent + " airtimeMs=" + airtimeMs + ",\n" + indent + " posttimeToFirstAirtimeMs=" + posttimeToFirstAirtimeMs + ",\n" + + indent + " userExpansionCount=" + userExpansionCount + ",\n" + + indent + " airtimeExpandedMs=" + airtimeExpandedMs + ",\n" + + indent + " posttimeToFVEMs=" + posttimeToFirstVisibleExpansionMs + ",\n" + indent + "}"; } } @@ -255,6 +269,8 @@ public class NotificationUsageStats { * Tracks usage of an individual notification that is currently active. */ public static class SingleNotificationStats { + private boolean isVisible = false; + private boolean isExpanded = false; /** SystemClock.elapsedRealtime() when the notification was posted. */ public long posttimeElapsedMs = -1; /** Elapsed time since the notification was posted until it was first clicked, or -1. */ @@ -272,6 +288,20 @@ public class NotificationUsageStats { public long currentAirtimeStartElapsedMs = -1; /** Accumulated visible time. */ public long airtimeMs = 0; + /** + * Time in ms between the notification being posted and when it first + * became visible and expanded; -1 if it was never visibly expanded. + */ + public long posttimeToFirstVisibleExpansionMs = -1; + /** + * If currently visible, SystemClock.elapsedRealtime() when the notification was made + * visible; -1 otherwise. + */ + public long currentAirtimeExpandedStartElapsedMs = -1; + /** Accumulated visible expanded time. */ + public long airtimeExpandedMs = 0; + /** Number of times the notification has been expanded by the user. */ + public long userExpansionCount = 0; public long getCurrentPosttimeMs() { if (posttimeElapsedMs < 0) { @@ -284,7 +314,16 @@ public class NotificationUsageStats { long result = airtimeMs; // Add incomplete airtime if currently shown. if (currentAirtimeStartElapsedMs >= 0) { - result+= (SystemClock.elapsedRealtime() - currentAirtimeStartElapsedMs); + result += (SystemClock.elapsedRealtime() - currentAirtimeStartElapsedMs); + } + return result; + } + + public long getCurrentAirtimeExpandedMs() { + long result = airtimeExpandedMs; + // Add incomplete expanded airtime if currently shown. + if (currentAirtimeExpandedStartElapsedMs >= 0) { + result += (SystemClock.elapsedRealtime() - currentAirtimeExpandedStartElapsedMs); } return result; } @@ -318,6 +357,8 @@ public class NotificationUsageStats { public void onVisibilityChanged(boolean visible) { long elapsedNowMs = SystemClock.elapsedRealtime(); + final boolean wasVisible = isVisible; + isVisible = visible; if (visible) { if (currentAirtimeStartElapsedMs < 0) { airtimeCount++; @@ -332,6 +373,37 @@ public class NotificationUsageStats { currentAirtimeStartElapsedMs = -1; } } + + if (wasVisible != isVisible) { + updateVisiblyExpandedStats(); + } + } + + public void onExpansionChanged(boolean userAction, boolean expanded) { + isExpanded = expanded; + if (isExpanded && userAction) { + userExpansionCount++; + } + updateVisiblyExpandedStats(); + } + + private void updateVisiblyExpandedStats() { + long elapsedNowMs = SystemClock.elapsedRealtime(); + if (isExpanded && isVisible) { + // expanded and visible + if (currentAirtimeExpandedStartElapsedMs < 0) { + currentAirtimeExpandedStartElapsedMs = elapsedNowMs; + } + if (posttimeToFirstVisibleExpansionMs < 0) { + posttimeToFirstVisibleExpansionMs = elapsedNowMs - posttimeElapsedMs; + } + } else { + // not-expanded or not-visible + if (currentAirtimeExpandedStartElapsedMs >= 0) { + airtimeExpandedMs += (elapsedNowMs - currentAirtimeExpandedStartElapsedMs); + currentAirtimeExpandedStartElapsedMs = -1; + } + } } /** The notification is leaving the system. Finalize. */ @@ -348,6 +420,9 @@ public class NotificationUsageStats { ", airtimeCount=" + airtimeCount + ", airtimeMs=" + airtimeMs + ", currentAirtimeStartElapsedMs=" + currentAirtimeStartElapsedMs + + ", airtimeExpandedMs=" + airtimeExpandedMs + + ", posttimeToFirstVisibleExpansionMs=" + posttimeToFirstVisibleExpansionMs + + ", currentAirtimeExpandedSEMs=" + currentAirtimeExpandedStartElapsedMs + '}'; } } @@ -393,7 +468,7 @@ public class NotificationUsageStats { private static final int MSG_DISMISS = 4; private static final String DB_NAME = "notification_log.db"; - private static final int DB_VERSION = 2; + private static final int DB_VERSION = 3; /** Age in ms after which events are pruned from the DB. */ private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week @@ -419,6 +494,10 @@ public class NotificationUsageStats { private static final String COL_ACTION_COUNT = "action_count"; private static final String COL_POSTTIME_MS = "posttime_ms"; private static final String COL_AIRTIME_MS = "airtime_ms"; + private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms"; + private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms"; + private static final String COL_EXPAND_COUNT = "expansion_count"; + private static final int EVENT_TYPE_POST = 1; private static final int EVENT_TYPE_CLICK = 2; @@ -481,6 +560,9 @@ public class NotificationUsageStats { COL_ACTION_COUNT + " INT," + COL_POSTTIME_MS + " INT," + COL_AIRTIME_MS + " INT" + + COL_FIRST_EXPANSIONTIME_MS + " INT" + + COL_AIRTIME_EXPANDED_MS + " INT" + + COL_EXPAND_COUNT + " INT" + ")"); } @@ -493,6 +575,16 @@ public class NotificationUsageStats { COL_POSTTIME_MS + " INT"); db.execSQL("ALTER TABLE " + TAB_LOG + " ADD COLUMN " + COL_AIRTIME_MS + " INT"); + case 2: + // Add COL_EXPANSIONTIME_MS column + db.execSQL("ALTER TABLE " + TAB_LOG + " ADD COLUMN " + + COL_FIRST_EXPANSIONTIME_MS + " INT"); + // Add COL_AIRTIME_EXPANDED_MS column + db.execSQL("ALTER TABLE " + TAB_LOG + " ADD COLUMN " + + COL_AIRTIME_EXPANDED_MS + " INT"); + // Add COL_EXPAND_COUNT column + db.execSQL("ALTER TABLE " + TAB_LOG + " ADD COLUMN " + + COL_EXPAND_COUNT + " INT"); } } }; @@ -553,7 +645,7 @@ public class NotificationUsageStats { if (eventType == EVENT_TYPE_POST) { putNotificationDetails(r, cv); } else { - putPosttimeAirtime(r, cv); + putPosttimeVisibility(r, cv); } SQLiteDatabase db = mHelper.getWritableDatabase(); if (db.insert(TAB_LOG, null, cv) < 0) { @@ -597,9 +689,12 @@ public class NotificationUsageStats { r.getNotification().actions.length : 0); } - private static void putPosttimeAirtime(NotificationRecord r, ContentValues outCv) { + private static void putPosttimeVisibility(NotificationRecord r, ContentValues outCv) { outCv.put(COL_POSTTIME_MS, r.stats.getCurrentPosttimeMs()); outCv.put(COL_AIRTIME_MS, r.stats.getCurrentAirtimeMs()); + outCv.put(COL_EXPAND_COUNT, r.stats.userExpansionCount); + outCv.put(COL_AIRTIME_EXPANDED_MS, r.stats.getCurrentAirtimeExpandedMs()); + outCv.put(COL_FIRST_EXPANSIONTIME_MS, r.stats.posttimeToFirstVisibleExpansionMs); } public void dump(PrintWriter pw, String indent, DumpFilter filter) { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index d0013aa..f33943d 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -573,6 +573,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub } @Override + public void onNotificationExpansionChanged(String key, boolean userAction, + boolean expanded) throws RemoteException { + enforceStatusBarService(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationExpansionChanged( + key, userAction, expanded); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void onClearAllNotifications(int userId) { enforceStatusBarService(); final int callingUid = Binder.getCallingUid(); |