Add a log and statistics for notification expansion.
Bug: 16618854 Change-Id: I501f396fa495e1e55a27d7d0b65aac66495418c1
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -572,6 +572,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();
|
||||
|
||||
Reference in New Issue
Block a user