summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2015-04-17 10:02:15 -0700
committerAmith Yamasani <yamasani@google.com>2015-04-24 16:12:01 -0700
commitf47e51ec605fccf7fed9e50d1adc98fbd4e8b340 (patch)
tree68c2fe8acf0144544d498ca413283d70d7666c53
parent4bc704655abee70c1998b9a62777cdf643920467 (diff)
downloadframeworks_base-f47e51ec605fccf7fed9e50d1adc98fbd4e8b340.zip
frameworks_base-f47e51ec605fccf7fed9e50d1adc98fbd4e8b340.tar.gz
frameworks_base-f47e51ec605fccf7fed9e50d1adc98fbd4e8b340.tar.bz2
More usage tracking
Notification listeners can now report that a notification has been seen by the user and that package is then marked as active. Bug: 20066058 Change-Id: I336040a52c44c21fd0d78b02ec9a19d448c64b40
-rw-r--r--api/current.txt17
-rw-r--r--api/system-current.txt17
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java41
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java12
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java5
9 files changed, 112 insertions, 19 deletions
diff --git a/api/current.txt b/api/current.txt
index 0d96042..3901721 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16430,6 +16430,14 @@ package android.media {
method public abstract void onAudioDeviceConnection();
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class PlaybackSettings {
ctor public PlaybackSettings();
method public android.media.PlaybackSettings allowDefaults();
@@ -16448,14 +16456,6 @@ package android.media {
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
- public abstract interface OnAudioRecordRoutingListener {
- method public abstract void onAudioRecordRouting(android.media.AudioRecord);
- }
-
- public abstract interface OnAudioTrackRoutingListener {
- method public abstract void onAudioTrackRouting(android.media.AudioTrack);
- }
-
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -28912,6 +28912,7 @@ package android.service.notification {
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
+ method public final void setNotificationsShown(java.lang.String[]);
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 18199b7..f3ceadb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17646,6 +17646,14 @@ package android.media {
method public abstract void onAudioDeviceConnection();
}
+ public abstract interface OnAudioRecordRoutingListener {
+ method public abstract void onAudioRecordRouting(android.media.AudioRecord);
+ }
+
+ public abstract interface OnAudioTrackRoutingListener {
+ method public abstract void onAudioTrackRouting(android.media.AudioTrack);
+ }
+
public final class PlaybackSettings {
ctor public PlaybackSettings();
method public android.media.PlaybackSettings allowDefaults();
@@ -17664,14 +17672,6 @@ package android.media {
field public static final int AUDIO_STRETCH_MODE_VOICE = 1; // 0x1
}
- public abstract interface OnAudioRecordRoutingListener {
- method public abstract void onAudioRecordRouting(android.media.AudioRecord);
- }
-
- public abstract interface OnAudioTrackRoutingListener {
- method public abstract void onAudioTrackRouting(android.media.AudioTrack);
- }
-
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -30957,6 +30957,7 @@ package android.service.notification {
method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
+ method public final void setNotificationsShown(java.lang.String[]);
method public final void setOnNotificationPostedTrim(int);
method public void unregisterAsSystemService() throws android.os.RemoteException;
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e275df0..ac8d5d8 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -67,6 +67,8 @@ interface INotificationManager
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
+ void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);
+
ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index cc7f880..35b8819 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -358,6 +358,20 @@ public abstract class NotificationListenerService extends Service {
}
/**
+ * Inform the notification manager that these notifications have been viewed by the
+ * user.
+ * @param keys Notifications to mark as seen.
+ */
+ public final void setNotificationsShown(String[] keys) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* Sets the notification trim that will be received via {@link #onNotificationPosted}.
*
* <p>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index de4874f..e542264 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -700,6 +700,26 @@ public abstract class BaseStatusBar extends SystemUI implements
return isCurrentProfile(notificationUserId);
}
+ protected void setNotificationShown(StatusBarNotification n) {
+ mNotificationListener.setNotificationsShown(new String[] { n.getKey() });
+ }
+
+ protected void setNotificationsShown(String[] keys) {
+ mNotificationListener.setNotificationsShown(keys);
+ }
+
+ protected void setNotificationsShownAll() {
+ ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+ final int N = activeNotifications.size();
+
+ String[] keys = new String[N];
+ for (int i = 0; i < N; i++) {
+ NotificationData.Entry entry = activeNotifications.get(i);
+ keys[i] = entry.key;
+ }
+ setNotificationsShown(keys);
+ }
+
protected boolean isCurrentProfile(int userId) {
synchronized (mCurrentProfiles) {
return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
@@ -1681,6 +1701,7 @@ public abstract class BaseStatusBar extends SystemUI implements
boolean clearNotificationEffects =
(mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
mBarService.onPanelRevealed(clearNotificationEffects);
+ setNotificationsShownAll();
} else {
mBarService.onPanelHidden();
}
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 c854d63..d058892 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1119,6 +1119,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
boolean isHeadsUped = mUseHeadsUp && shouldInterrupt(notification);
if (isHeadsUped) {
mHeadsUpManager.showNotification(shadeEntry);
+ // Mark as seen immediately
+ setNotificationShown(notification);
}
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1008653..25998da 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -36,6 +36,9 @@ import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -97,6 +100,7 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
@@ -236,6 +240,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<String> mLights = new ArrayList<>();
private AppOpsManager mAppOps;
+ private UsageStatsManagerInternal mAppUsageStats;
private Archive mArchive;
@@ -871,6 +876,7 @@ public class NotificationManagerService extends SystemService {
mAm = ActivityManagerNative.getDefault();
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
+ mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
mHandler = new WorkerHandler();
mRankingThread.start();
@@ -1405,6 +1411,41 @@ public class NotificationManagerService extends SystemService {
}
}
+ @Override
+ public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationList) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ if (keys != null) {
+ final int N = keys.length;
+ for (int i = 0; i < N; i++) {
+ NotificationRecord r = mNotificationsByKey.get(keys[i]);
+ if (r == null) continue;
+ final int userId = r.sbn.getUserId();
+ if (userId != info.userid && userId != UserHandle.USER_ALL &&
+ !mUserProfiles.isCurrentProfile(userId)) {
+ throw new SecurityException("Disallowed call from listener: "
+ + info.service);
+ }
+ if (!r.isSeen()) {
+ if (DBG) Slog.d(TAG, "Marking notification as seen " + keys[i]);
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ userId == UserHandle.USER_ALL ? UserHandle.USER_OWNER
+ : userId,
+ UsageEvents.Event.INTERACTION);
+ r.setSeen();
+ }
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 5569a09..e106a4a 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -50,6 +50,8 @@ public final class NotificationRecord {
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
int score;
+ /** Whether the notification was seen by the user via one of the notification listeners. */
+ boolean mIsSeen;
// These members are used by NotificationSignalExtractors
// to communicate with the ranking module.
@@ -301,6 +303,16 @@ public final class NotificationRecord {
return mGlobalSortKey;
}
+ /** Check if any of the listeners have marked this notification as seen by the user. */
+ public boolean isSeen() {
+ return mIsSeen;
+ }
+
+ /** Mark the notification as seen by the user. */
+ public void setSeen() {
+ mIsSeen = true;
+ }
+
public void setAuthoritativeRank(int authoritativeRank) {
mAuthoritativeRank = authoritativeRank;
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index edeeaba..04984d3 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -28,6 +28,7 @@ import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
+import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -435,9 +436,7 @@ public class UsageStatsService extends SystemService implements
DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
if (dpm == null) return false;
List<ComponentName> components = dpm.getActiveAdminsAsUser(userId);
- if (components == null) {
- return false;
- }
+ if (components == null) return false;
final int size = components.size();
for (int i = 0; i < size; i++) {
if (components.get(i).getPackageName().equals(packageName)) {