diff options
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 { |