diff options
9 files changed, 259 insertions, 37 deletions
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 663c838..aea1585 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -19,6 +19,7 @@ package com.android.internal.statusbar; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; +import com.android.internal.statusbar.NotificationVisibility; import android.service.notification.StatusBarNotification; /** @hide */ @@ -53,8 +54,8 @@ interface IStatusBarService int uid, int initialPid, String message, int userId); void onClearAllNotifications(int userId); void onNotificationClear(String pkg, String tag, int id, int userId); - void onNotificationVisibilityChanged( - in String[] newlyVisibleKeys, in String[] noLongerVisibleKeys); + void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys, + in NotificationVisibility[] noLongerVisibleKeys); void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded); void setSystemUiVisibility(int vis, int mask, String cause); void setWindowState(int window, int state); diff --git a/core/java/com/android/internal/statusbar/NotificationVisibility.aidl b/core/java/com/android/internal/statusbar/NotificationVisibility.aidl new file mode 100644 index 0000000..c067551 --- /dev/null +++ b/core/java/com/android/internal/statusbar/NotificationVisibility.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2015, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.statusbar; + +parcelable NotificationVisibility; + diff --git a/core/java/com/android/internal/statusbar/NotificationVisibility.java b/core/java/com/android/internal/statusbar/NotificationVisibility.java new file mode 100644 index 0000000..2139ad0 --- /dev/null +++ b/core/java/com/android/internal/statusbar/NotificationVisibility.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.statusbar; + +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.ArrayDeque; +import java.util.Collection; + +public class NotificationVisibility implements Parcelable { + private static final String TAG = "NoViz"; + private static final int MAX_POOL_SIZE = 25; + private static ArrayDeque<NotificationVisibility> sPool = new ArrayDeque<>(MAX_POOL_SIZE); + private static int sNexrId = 0; + + public String key; + public int rank; + public boolean visible = true; + /*package*/ int id; + + private NotificationVisibility() { + id = sNexrId++; + } + + private NotificationVisibility(String key, int rank, boolean visibile) { + this(); + this.key = key; + this.rank = rank; + this.visible = visibile; + } + + @Override + public String toString() { + return "NotificationVisibility(id=" + id + + "key=" + key + + " rank=" + rank + + (visible?" visible":"") + + " )"; + } + + @Override + public NotificationVisibility clone() { + return obtain(this.key, this.rank, this.visible); + } + + @Override + public int hashCode() { + // allow lookups by key, which _should_ never be null. + return key == null ? 0 : key.hashCode(); + } + + @Override + public boolean equals(Object that) { + // allow lookups by key, which _should_ never be null. + if (that instanceof NotificationVisibility) { + NotificationVisibility thatViz = (NotificationVisibility) that; + return (key == null && thatViz.key == null) || key.equals(thatViz.key); + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(this.key); + out.writeInt(this.rank); + out.writeInt(this.visible ? 1 : 0); + } + + private void readFromParcel(Parcel in) { + this.key = in.readString(); + this.rank = in.readInt(); + this.visible = in.readInt() != 0; + } + + /** + * Return a new NotificationVisibility instance from the global pool. Allows us to + * avoid allocating new objects in many cases. + */ + public static NotificationVisibility obtain(String key, int rank, boolean visible) { + NotificationVisibility vo = obtain(); + vo.key = key; + vo.rank = rank; + vo.visible = visible; + return vo; + } + + private static NotificationVisibility obtain(Parcel in) { + NotificationVisibility vo = obtain(); + vo.readFromParcel(in); + return vo; + } + + private static NotificationVisibility obtain() { + synchronized (sPool) { + if (!sPool.isEmpty()) { + return sPool.poll(); + } + } + return new NotificationVisibility(); + } + + /** + * Return a NotificationVisibility instance to the global pool. + * <p> + * You MUST NOT touch the NotificationVisibility after calling this function because it has + * effectively been freed. + * </p> + */ + public void recycle() { + if (key == null) { + // do nothing on multiple recycles + return; + } + key = null; + if (sPool.size() < MAX_POOL_SIZE) { + synchronized (sPool) { + sPool.offer(this); + } + } + } + + /** + * Parcelable.Creator that instantiates NotificationVisibility objects + */ + public static final Parcelable.Creator<NotificationVisibility> CREATOR + = new Parcelable.Creator<NotificationVisibility>() + { + public NotificationVisibility createFromParcel(Parcel parcel) + { + return obtain(parcel); + } + + public NotificationVisibility[] newArray(int size) + { + return new NotificationVisibility[size]; + } + }; +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 16df64c..5d98fe9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -96,6 +96,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; +import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.StatusBarIcon; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.keyguard.ViewMediatorCallback; @@ -457,7 +458,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mDisabledUnmodified2; /** Keys of notifications currently visible to the user. */ - private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>(); + private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = + new ArraySet<>(); private long mLastVisibilityReportUptimeMs; private final ShadeUpdates mShadeUpdates = new ShadeUpdates(); @@ -498,12 +500,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Tracks notifications currently visible in mNotificationStackScroller and // emits visibility events via NoMan on changes. private final Runnable mVisibilityReporter = new Runnable() { - private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>(); - private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>(); + private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = + new ArraySet<>(); + private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = + new ArraySet<>(); + private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications = + new ArraySet<>(); @Override public void run() { mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); + final String mediaKey = getCurrentMediaNotificationKey(); // 1. Loop over mNotificationData entries: // A. Keep list of visible notifications. @@ -518,31 +525,45 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, for (int i = 0; i < N; i++) { Entry entry = activeNotifications.get(i); String key = entry.notification.getKey(); - boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key); - boolean currentlyVisible = + boolean isVisible = (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; - if (currentlyVisible) { + NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible); + boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj); + if (isVisible) { // Build new set of visible notifications. - mTmpCurrentlyVisibleNotifications.add(key); - } - if (!previouslyVisible && currentlyVisible) { - mTmpNewlyVisibleNotifications.add(key); + mTmpCurrentlyVisibleNotifications.add(visObj); + if (!previouslyVisible) { + mTmpNewlyVisibleNotifications.add(visObj); + } + } else { + // release object + visObj.recycle(); } } - ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications; - noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); + mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications); + mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); logNotificationVisibilityChanges( - mTmpNewlyVisibleNotifications, noLongerVisibleNotifications); + mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications); - mCurrentlyVisibleNotifications.clear(); + recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); - mTmpNewlyVisibleNotifications.clear(); + recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications); mTmpCurrentlyVisibleNotifications.clear(); + mTmpNewlyVisibleNotifications.clear(); + mTmpNoLongerVisibleNotifications.clear(); } }; + private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { + final int N = array.size(); + for (int i = 0 ; i < N; i++) { + array.valueAt(i).recycle(); + } + array.clear(); + } + private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -2987,9 +3008,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Report all notifications as invisible and turn down the // reporter. if (!mCurrentlyVisibleNotifications.isEmpty()) { - logNotificationVisibilityChanges( - Collections.<String>emptyList(), mCurrentlyVisibleNotifications); - mCurrentlyVisibleNotifications.clear(); + logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(), + mCurrentlyVisibleNotifications); + recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); } mHandler.removeCallbacks(mVisibilityReporter); mStackScroller.setChildLocationsChangedListener(null); @@ -3007,18 +3028,27 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } private void logNotificationVisibilityChanges( - Collection<String> newlyVisible, Collection<String> noLongerVisible) { + Collection<NotificationVisibility> newlyVisible, + Collection<NotificationVisibility> noLongerVisible) { if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { return; } - String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]); - String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]); + NotificationVisibility[] newlyVisibleAr = + newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]); + NotificationVisibility[] noLongerVisibleAr = + noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]); try { mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); } catch (RemoteException e) { // Ignore. } - setNotificationsShown(newlyVisibleAr); + + final int N = newlyVisible.size(); + String[] newlyVisibleKeyAr = new String[N]; + for (int i = 0; i < N; i++) { + newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; + } + setNotificationsShown(newlyVisibleKeyAr); } // State logging diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 49d4c22..43b640b 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -74,7 +74,7 @@ option java_package com.android.server # when a notification has been canceled 27530 notification_canceled (key|3),(reason|1),(lifespan|1),(freshness|1),(exposure|1) # replaces 27510 with a row per notification -27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1) +27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1) # a notification emited noise, vibration, or light 27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1) diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index fdb443e..87b4f8c 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import com.android.internal.statusbar.NotificationVisibility; + public interface NotificationDelegate { void onSetDisabled(int status); void onClearAll(int callingUid, int callingPid, int userId); @@ -30,6 +32,7 @@ public interface NotificationDelegate { void onPanelHidden(); void clearEffects(); void onNotificationVisibilityChanged( - String[] newlyVisibleKeys, String[] noLongerVisibleKeys); + NotificationVisibility[] newlyVisibleKeys, + NotificationVisibility[] 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 0dcad82..4524ff8 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -97,6 +97,7 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.android.internal.R; +import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.FastXmlSerializer; import com.android.server.EventLogTags; import com.android.server.LocalServices; @@ -622,22 +623,24 @@ public class NotificationManagerService extends SystemService { } @Override - public void onNotificationVisibilityChanged( - String[] newlyVisibleKeys, String[] noLongerVisibleKeys) { + public void onNotificationVisibilityChanged(NotificationVisibility[] newlyVisibleKeys, + NotificationVisibility[] noLongerVisibleKeys) { synchronized (mNotificationList) { - for (String key : newlyVisibleKeys) { - NotificationRecord r = mNotificationsByKey.get(key); + for (NotificationVisibility nv : newlyVisibleKeys) { + NotificationRecord r = mNotificationsByKey.get(nv.key); if (r == null) continue; - r.setVisibility(true); + r.setVisibility(true, nv.rank); + nv.recycle(); } // Note that we might receive this event after notifications // have already left the system, e.g. after dismissing from the // shade. Hence not finding notifications in // mNotificationsByKey is not an exceptional condition. - for (String key : noLongerVisibleKeys) { - NotificationRecord r = mNotificationsByKey.get(key); + for (NotificationVisibility nv : noLongerVisibleKeys) { + NotificationRecord r = mNotificationsByKey.get(nv.key); if (r == null) continue; - r.setVisibility(false); + r.setVisibility(false, nv.rank); + nv.recycle(); } } } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index c4773ca..b7aea9d 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -314,13 +314,15 @@ public final class NotificationRecord { /** * Set the visibility of the notification. */ - public void setVisibility(boolean visible) { + public void setVisibility(boolean visible, int rank) { final long now = System.currentTimeMillis(); mVisibleSinceMs = visible ? now : mVisibleSinceMs; stats.onVisibilityChanged(visible); EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0, (int) (now - mCreationTimeMs), - (int) (now - mUpdateTimeMs)); + (int) (now - mUpdateTimeMs), + 0, // exposure time + rank); } /** diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index a754379..7640837 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -29,6 +29,7 @@ import android.util.Slog; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.server.LocalServices; @@ -660,7 +661,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void onNotificationVisibilityChanged( - String[] newlyVisibleKeys, String[] noLongerVisibleKeys) throws RemoteException { + NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys) + throws RemoteException { enforceStatusBarService(); long identity = Binder.clearCallingIdentity(); try { |