summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/jni/android_media_MediaExtractor.cpp4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java9
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java45
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java271
-rw-r--r--services/java/com/android/server/SystemServer.java2
5 files changed, 322 insertions, 9 deletions
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index a78f16d..3dbf77b 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -87,7 +87,7 @@ class JavaDataSourceBridge : public DataSource {
jbyteArray byteArrayObj = env->NewByteArray(size);
env->DeleteLocalRef(env->GetObjectClass(mDataSource));
env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
- ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, size);
+ ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, (jint)size);
env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
env->DeleteLocalRef(byteArrayObj);
if (env->ExceptionCheck()) {
@@ -632,7 +632,7 @@ static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
env->CallVoidMethod(
cryptoInfoObj,
gFields.cryptoInfoSetID,
- numSubSamples,
+ (jint)numSubSamples,
numBytesOfPlainDataObj,
numBytesOfEncryptedDataObj,
keyObj,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5fa9084..004d4fe 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -945,6 +945,11 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
boolean mDidDexOpt;
+ /**
+ * Set if the systemServer made a call to enterSafeMode.
+ */
+ boolean mSafeMode;
+
String mDebugApp = null;
boolean mWaitForDebugger = false;
boolean mDebugTransient = false;
@@ -2737,7 +2742,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Run the app in safe mode if its manifest requests so or the
// system is booted in safe mode.
if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
- Zygote.systemInSafeMode == true) {
+ mSafeMode == true) {
debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
}
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
@@ -8963,6 +8968,8 @@ public final class ActivityManagerService extends ActivityManagerNative
} catch (RemoteException e) {
}
}
+
+ mSafeMode = true;
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ccd6f60..b4f7ae6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -78,6 +78,7 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.notification.NotificationScorer;
import com.android.server.EventLogTags;
+import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.SystemService;
import com.android.server.lights.Light;
@@ -205,6 +206,8 @@ public class NotificationManagerService extends SystemService {
final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
+ private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
+
private int mZenMode;
// temporary, until we update apps to provide metadata
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
@@ -791,6 +794,7 @@ public class NotificationManagerService extends SystemService {
public static final class NotificationRecord
{
final StatusBarNotification sbn;
+ final SingleNotificationStats stats = new SingleNotificationStats();
IBinder statusBarKey;
NotificationRecord(StatusBarNotification sbn)
@@ -861,6 +865,7 @@ public class NotificationManagerService extends SystemService {
}
pw.println(prefix + " }");
}
+ pw.println(prefix + " stats=" + stats.toString());
}
@Override
@@ -1758,6 +1763,9 @@ public class NotificationManagerService extends SystemService {
}
}
+ pw.println("\n Usage Stats:");
+ mUsageStats.dump(pw, " ");
+
}
}
@@ -1905,9 +1913,11 @@ public class NotificationManagerService extends SystemService {
int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index < 0) {
mNotificationList.add(r);
+ mUsageStats.registerPostedByApp(r);
} else {
old = mNotificationList.remove(index);
mNotificationList.add(index, r);
+ mUsageStats.registerUpdatedByApp(r);
// Make sure we don't lose the foreground service state.
if (old != null) {
notification.flags |=
@@ -2300,7 +2310,7 @@ public class NotificationManagerService extends SystemService {
manager.sendAccessibilityEvent(event);
}
- private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
+ private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
// tell the app
if (sendDelete) {
if (r.getNotification().deleteIntent != null) {
@@ -2359,6 +2369,26 @@ public class NotificationManagerService extends SystemService {
mLedNotification = null;
}
+ // Record usage stats
+ switch (reason) {
+ case REASON_DELEGATE_CANCEL:
+ case REASON_DELEGATE_CANCEL_ALL:
+ case REASON_LISTENER_CANCEL:
+ case REASON_LISTENER_CANCEL_ALL:
+ mUsageStats.registerDismissedByUser(r);
+ break;
+ case REASON_NOMAN_CANCEL:
+ case REASON_NOMAN_CANCEL_ALL:
+ mUsageStats.registerRemovedByApp(r);
+ break;
+ case REASON_DELEGATE_CLICK:
+ mUsageStats.registerCancelDueToClick(r);
+ break;
+ default:
+ mUsageStats.registerCancelUnknown(r);
+ break;
+ }
+
// Save it for users of getHistoricalNotifications()
mArchive.record(r.sbn);
}
@@ -2387,6 +2417,12 @@ public class NotificationManagerService extends SystemService {
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
+ // Ideally we'd do this in the caller of this method. However, that would
+ // require the caller to also find the notification.
+ if (reason == REASON_DELEGATE_CLICK) {
+ mUsageStats.registerClickedByUser(r);
+ }
+
if ((r.getNotification().flags & mustHaveFlags) != mustHaveFlags) {
return;
}
@@ -2397,7 +2433,7 @@ public class NotificationManagerService extends SystemService {
mNotificationList.remove(index);
mNotificationsByKey.remove(r.sbn.getKey());
- cancelNotificationLocked(r, sendDelete);
+ cancelNotificationLocked(r, sendDelete, reason);
updateLightsLocked();
}
}
@@ -2469,7 +2505,7 @@ public class NotificationManagerService extends SystemService {
}
mNotificationList.remove(i);
mNotificationsByKey.remove(r.sbn.getKey());
- cancelNotificationLocked(r, false);
+ cancelNotificationLocked(r, false, reason);
}
if (canceledSomething) {
updateLightsLocked();
@@ -2521,6 +2557,7 @@ public class NotificationManagerService extends SystemService {
EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
null, userId, 0, 0, reason,
listener == null ? null : listener.component.toShortString());
+
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
NotificationRecord r = mNotificationList.get(i);
@@ -2532,7 +2569,7 @@ public class NotificationManagerService extends SystemService {
| Notification.FLAG_NO_CLEAR)) == 0) {
mNotificationList.remove(i);
mNotificationsByKey.remove(r.sbn.getKey());
- cancelNotificationLocked(r, true);
+ cancelNotificationLocked(r, true, reason);
}
}
updateLightsLocked();
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
new file mode 100644
index 0000000..faa5a43
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2014 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.server.notification;
+
+import com.android.server.notification.NotificationManagerService.NotificationRecord;
+
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Keeps track of notification activity, display, and user interaction.
+ *
+ * <p>This class receives signals from NoMan and keeps running stats of
+ * notification usage. Some metrics are updated as events occur. Others, namely
+ * those involving durations, are updated as the notification is canceled.</p>
+ *
+ * <p>This class is thread-safe.</p>
+ *
+ * {@hide}
+ */
+public class NotificationUsageStats {
+
+ // Guarded by synchronized(this).
+ private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>();
+
+ /**
+ * Called when a notification has been posted.
+ */
+ public synchronized void registerPostedByApp(NotificationRecord notification) {
+ notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime();
+ for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
+ stats.numPostedByApp++;
+ }
+ }
+
+ /**
+ * Called when a notification has been updated.
+ */
+ public void registerUpdatedByApp(NotificationRecord notification) {
+ for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
+ stats.numUpdatedByApp++;
+ }
+ }
+
+ /**
+ * Called when the originating app removed the notification programmatically.
+ */
+ public synchronized void registerRemovedByApp(NotificationRecord notification) {
+ for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
+ stats.numRemovedByApp++;
+ stats.collect(notification.stats);
+ }
+ }
+
+ /**
+ * Called when the user dismissed the notification via the UI.
+ */
+ public synchronized void registerDismissedByUser(NotificationRecord notification) {
+ notification.stats.onDismiss();
+ for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
+ stats.numDismissedByUser++;
+ stats.collect(notification.stats);
+ }
+ }
+
+ /**
+ * Called when the user clicked the notification in the UI.
+ */
+ public synchronized void registerClickedByUser(NotificationRecord notification) {
+ notification.stats.onClick();
+ for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
+ stats.numClickedByUser++;
+ }
+ }
+
+ /**
+ * Called when the notification is canceled because the user clicked it.
+ *
+ * <p>Called after {@link #registerClickedByUser(NotificationRecord)}.</p>
+ */
+ public synchronized void registerCancelDueToClick(NotificationRecord notification) {
+ // No explicit stats for this (the click has already been registered in
+ // registerClickedByUser), just make sure the single notification stats
+ // are folded up into aggregated stats.
+ for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
+ stats.collect(notification.stats);
+ }
+ }
+
+ /**
+ * Called when the notification is canceled due to unknown reasons.
+ *
+ * <p>Called for notifications of apps being uninstalled, for example.</p>
+ */
+ public synchronized void registerCancelUnknown(NotificationRecord notification) {
+ // Fold up individual stats.
+ for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
+ stats.collect(notification.stats);
+ }
+ }
+
+ // Locked by this.
+ private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) {
+ StatusBarNotification n = record.sbn;
+
+ String user = String.valueOf(n.getUserId());
+ String userPackage = user + ":" + n.getPackageName();
+
+ // TODO: Use pool of arrays.
+ return new AggregatedStats[] {
+ getOrCreateAggregatedStatsLocked(user),
+ getOrCreateAggregatedStatsLocked(userPackage),
+ getOrCreateAggregatedStatsLocked(n.getKey()),
+ };
+ }
+
+ // Locked by this.
+ private AggregatedStats getOrCreateAggregatedStatsLocked(String key) {
+ AggregatedStats result = mStats.get(key);
+ if (result == null) {
+ result = new AggregatedStats(key);
+ mStats.put(key, result);
+ }
+ return result;
+ }
+
+ public synchronized void dump(PrintWriter pw, String indent) {
+ for (AggregatedStats as : mStats.values()) {
+ as.dump(pw, indent);
+ }
+ }
+
+ /**
+ * Aggregated notification stats.
+ */
+ private static class AggregatedStats {
+ public final String key;
+
+ // ---- Updated as the respective events occur.
+ public int numPostedByApp;
+ public int numUpdatedByApp;
+ public int numRemovedByApp;
+ public int numClickedByUser;
+ public int numDismissedByUser;
+
+ // ---- Updated when a notification is canceled.
+ public final Aggregate posttimeMs = new Aggregate();
+ public final Aggregate posttimeToDismissMs = new Aggregate();
+ public final Aggregate posttimeToFirstClickMs = new Aggregate();
+
+ public AggregatedStats(String key) {
+ this.key = key;
+ }
+
+ public void collect(SingleNotificationStats singleNotificationStats) {
+ posttimeMs.addSample(
+ SystemClock.elapsedRealtime() - singleNotificationStats.posttimeElapsedMs);
+ if (singleNotificationStats.posttimeToDismissMs >= 0) {
+ posttimeToDismissMs.addSample(singleNotificationStats.posttimeToDismissMs);
+ }
+ if (singleNotificationStats.posttimeToFirstClickMs >= 0) {
+ posttimeToFirstClickMs.addSample(singleNotificationStats.posttimeToFirstClickMs);
+ }
+ }
+
+ public void dump(PrintWriter pw, String indent) {
+ pw.println(toStringWithIndent(indent));
+ }
+
+ @Override
+ public String toString() {
+ return toStringWithIndent("");
+ }
+
+ private String toStringWithIndent(String indent) {
+ return indent + "AggregatedStats{\n" +
+ indent + " key='" + key + "',\n" +
+ indent + " numPostedByApp=" + numPostedByApp + ",\n" +
+ indent + " numUpdatedByApp=" + numUpdatedByApp + ",\n" +
+ indent + " numRemovedByApp=" + numRemovedByApp + ",\n" +
+ indent + " numClickedByUser=" + numClickedByUser + ",\n" +
+ indent + " numDismissedByUser=" + numDismissedByUser + ",\n" +
+ indent + " posttimeMs=" + posttimeMs + ",\n" +
+ indent + " posttimeToDismissMs=" + posttimeToDismissMs + ",\n" +
+ indent + " posttimeToFirstClickMs=" + posttimeToFirstClickMs + ",\n" +
+ indent + "}";
+ }
+ }
+
+ /**
+ * Tracks usage of an individual notification that is currently active.
+ */
+ public static class SingleNotificationStats {
+ /** 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. */
+ public long posttimeToFirstClickMs = -1;
+ /** Elpased time since the notification was posted until it was dismissed by the user. */
+ public long posttimeToDismissMs = -1;
+
+ /**
+ * Called when the user clicked the notification.
+ */
+ public void onClick() {
+ if (posttimeToFirstClickMs < 0) {
+ posttimeToFirstClickMs = SystemClock.elapsedRealtime() - posttimeElapsedMs;
+ }
+ }
+
+ /**
+ * Called when the user removed the notification.
+ */
+ public void onDismiss() {
+ if (posttimeToDismissMs < 0) {
+ posttimeToDismissMs = SystemClock.elapsedRealtime() - posttimeElapsedMs;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SingleNotificationStats{" +
+ "posttimeElapsedMs=" + posttimeElapsedMs +
+ ", posttimeToFirstClickMs=" + posttimeToFirstClickMs +
+ ", posttimeToDismissMs=" + posttimeToDismissMs +
+ '}';
+ }
+ }
+
+ /**
+ * Aggregates long samples to sum and averages.
+ */
+ public static class Aggregate {
+ long numSamples;
+ long sum;
+ long avg;
+
+ public void addSample(long sample) {
+ numSamples++;
+ sum += sample;
+ avg = sum / numSamples;
+ }
+
+ @Override
+ public String toString() {
+ return "Aggregate{" +
+ "numSamples=" + numSamples +
+ ", sum=" + sum +
+ ", avg=" + avg +
+ '}';
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7b55f4b..d98139f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -921,8 +921,6 @@ public final class SystemServer {
final boolean safeMode = wm.detectSafeMode();
if (safeMode) {
mActivityManagerService.enterSafeMode();
- // Post the safe mode state in the Zygote class
- Zygote.systemInSafeMode = true;
// Disable the JIT for the system_server process
VMRuntime.getRuntime().disableJitCompilation();
} else {