summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt18
-rw-r--r--core/java/android/content/ContentResolver.java14
-rw-r--r--core/java/android/content/ContentService.java25
-rw-r--r--core/java/android/database/AbstractCursor.java2
-rw-r--r--core/java/android/database/ContentObservable.java55
-rw-r--r--core/java/android/database/ContentObserver.java191
-rw-r--r--core/java/android/database/CursorToBulkCursorAdaptor.java5
-rw-r--r--core/java/android/database/DataSetObservable.java15
-rwxr-xr-xcore/java/android/database/IContentObserver.aidl4
-rw-r--r--core/java/android/database/Observable.java13
-rw-r--r--core/java/android/net/NetworkStats.java39
-rw-r--r--core/java/android/net/NetworkStatsHistory.java78
-rw-r--r--core/java/android/net/NetworkTemplate.java10
-rw-r--r--core/java/android/net/TrafficStats.java2
-rw-r--r--core/java/android/os/IPowerManager.aidl1
-rw-r--r--core/java/android/provider/Settings.java38
-rw-r--r--core/java/android/text/MeasuredText.java3
-rw-r--r--core/java/android/view/GLES20Canvas.java13
-rw-r--r--core/java/android/view/ViewTreeObserver.java19
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java16
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java13
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java4
-rw-r--r--core/java/android/webkit/WebView.java10
-rw-r--r--core/java/android/webkit/WebViewCore.java1
-rw-r--r--core/java/android/widget/ActivityChooserView.java8
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java4
-rw-r--r--core/java/android/widget/NumberPicker.java18
-rw-r--r--core/java/android/widget/ShareActionProvider.java1
-rw-r--r--core/java/android/widget/TextView.java28
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java8
-rw-r--r--core/java/com/android/internal/util/FileRotator.java121
-rw-r--r--core/java/com/android/internal/util/IndentingPrintWriter.java63
-rw-r--r--core/java/com/android/internal/view/InputConnectionWrapper.java4
-rw-r--r--core/jni/android/graphics/Canvas.cpp3
-rw-r--r--core/jni/android/graphics/Paint.cpp36
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp10
-rw-r--r--core/jni/android/graphics/TextLayoutCache.h10
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp17
-rw-r--r--core/res/res/anim/screen_rotate_180_enter.xml16
-rw-r--r--core/res/res/anim/screen_rotate_180_exit.xml14
-rw-r--r--core/res/res/anim/screen_rotate_finish_enter.xml36
-rw-r--r--core/res/res/anim/screen_rotate_finish_exit.xml41
-rw-r--r--core/res/res/anim/screen_rotate_minus_90_enter.xml36
-rw-r--r--core/res/res/anim/screen_rotate_minus_90_exit.xml37
-rw-r--r--core/res/res/anim/screen_rotate_plus_90_enter.xml36
-rw-r--r--core/res/res/anim/screen_rotate_plus_90_exit.xml37
-rw-r--r--core/res/res/anim/screen_rotate_start_enter.xml29
-rw-r--r--core/res/res/anim/screen_rotate_start_exit.xml29
-rw-r--r--docs/html/guide/developing/device.jd8
-rw-r--r--docs/html/guide/topics/graphics/hardware-accel.jd31
-rw-r--r--graphics/java/android/graphics/AvoidXfermode.java1
-rw-r--r--graphics/java/android/graphics/PaintFlagsDrawFilter.java6
-rw-r--r--graphics/java/android/graphics/PixelXorXfermode.java1
-rw-r--r--libs/hwui/DisplayListRenderer.cpp103
-rw-r--r--libs/hwui/DisplayListRenderer.h9
-rw-r--r--libs/hwui/OpenGLRenderer.cpp26
-rw-r--r--libs/hwui/OpenGLRenderer.h11
-rw-r--r--media/libstagefright/codecs/aacenc/src/adj_thr.c2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java65
-rw-r--r--packages/VpnDialogs/AndroidManifest.xml4
-rw-r--r--packages/VpnDialogs/res/values/styles.xml7
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java46
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java55
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java6
-rw-r--r--services/java/com/android/server/EventLogTags.logtags4
-rw-r--r--services/java/com/android/server/PowerManagerService.java125
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java3
-rw-r--r--services/java/com/android/server/net/NetworkStatsCollection.java510
-rw-r--r--services/java/com/android/server/net/NetworkStatsRecorder.java341
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java1276
-rw-r--r--services/java/com/android/server/wm/ScreenRotationAnimation.java416
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java112
-rw-r--r--services/java/com/android/server/wm/WindowState.java13
-rw-r--r--services/tests/servicestests/res/raw/netstats_uid_v4bin0 -> 156516 bytes
-rw-r--r--services/tests/servicestests/res/raw/netstats_v1bin0 -> 18742 bytes
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java175
-rw-r--r--telephony/java/com/android/internal/telephony/cat/CatService.java4
-rw-r--r--telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java1
-rw-r--r--tests/ActivityTests/res/anim/slow_enter.xml30
-rw-r--r--tests/ActivityTests/res/anim/slow_exit.xml30
-rw-r--r--tests/ActivityTests/res/interpolator/slow_enter.xml22
-rw-r--r--tests/ActivityTests/res/values/themes.xml25
-rw-r--r--tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java17
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml9
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java64
-rw-r--r--tests/TileBenchmark/res/values/strings.xml16
-rw-r--r--tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java205
-rw-r--r--tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java107
-rw-r--r--tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java69
-rw-r--r--tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java103
92 files changed, 3709 insertions, 1657 deletions
diff --git a/api/current.txt b/api/current.txt
index 7c05ed6..85f3633 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6791,16 +6791,19 @@ package android.database {
public class ContentObservable extends android.database.Observable {
ctor public ContentObservable();
- method public void dispatchChange(boolean);
- method public void notifyChange(boolean);
+ method public deprecated void dispatchChange(boolean);
+ method public void dispatchChange(boolean, android.net.Uri);
+ method public deprecated void notifyChange(boolean);
method public void registerObserver(android.database.ContentObserver);
}
public abstract class ContentObserver {
ctor public ContentObserver(android.os.Handler);
method public boolean deliverSelfNotifications();
- method public final void dispatchChange(boolean);
+ method public final deprecated void dispatchChange(boolean);
+ method public final void dispatchChange(boolean, android.net.Uri);
method public void onChange(boolean);
+ method public void onChange(boolean, android.net.Uri);
}
public abstract interface CrossProcessCursor implements android.database.Cursor {
@@ -7785,7 +7788,7 @@ package android.gesture {
package android.graphics {
- public class AvoidXfermode extends android.graphics.Xfermode {
+ public deprecated class AvoidXfermode extends android.graphics.Xfermode {
ctor public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode);
}
@@ -8564,7 +8567,7 @@ package android.graphics {
field public int bytesPerPixel;
}
- public class PixelXorXfermode extends android.graphics.Xfermode {
+ public deprecated class PixelXorXfermode extends android.graphics.Xfermode {
ctor public PixelXorXfermode(int);
}
@@ -17560,6 +17563,7 @@ package android.provider {
field public static final java.lang.String RADIO_NFC = "nfc";
field public static final java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String RINGTONE = "ringtone";
+ field public static final java.lang.String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness";
field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
field public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; // 0x1
@@ -23817,8 +23821,9 @@ package android.view {
method public final void dispatchOnGlobalLayout();
method public final boolean dispatchOnPreDraw();
method public boolean isAlive();
- method public void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
+ method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
+ method public void removeOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
method public void removeOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
@@ -26671,6 +26676,7 @@ package android.widget {
method public void setOnValueChangedListener(android.widget.NumberPicker.OnValueChangeListener);
method public void setValue(int);
method public void setWrapSelectorWheel(boolean);
+ field public static final int SELECTOR_WHEEL_ITEM_COUNT = 5; // 0x5
}
public static abstract interface NumberPicker.Formatter {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index cc3219b..0debb84 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1034,8 +1034,11 @@ public abstract class ContentResolver {
* To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
* By default, CursorAdapter objects will get this notification.
*
- * @param uri
- * @param observer The observer that originated the change, may be <code>null</null>
+ * @param uri The uri of the content that was changed.
+ * @param observer The observer that originated the change, may be <code>null</null>.
+ * The observer that originated the change will only receive the notification if it
+ * has requested to receive self-change notifications by implementing
+ * {@link ContentObserver#deliverSelfNotifications()} to return true.
*/
public void notifyChange(Uri uri, ContentObserver observer) {
notifyChange(uri, observer, true /* sync to network */);
@@ -1046,8 +1049,11 @@ public abstract class ContentResolver {
* To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
* By default, CursorAdapter objects will get this notification.
*
- * @param uri
- * @param observer The observer that originated the change, may be <code>null</null>
+ * @param uri The uri of the content that was changed.
+ * @param observer The observer that originated the change, may be <code>null</null>.
+ * The observer that originated the change will only receive the notification if it
+ * has requested to receive self-change notifications by implementing
+ * {@link ContentObserver#deliverSelfNotifications()} to return true.
* @param syncToNetwork If true, attempt to sync the change to the network.
*/
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 0e83dc0..fc4c262 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -176,7 +176,7 @@ public final class ContentService extends IContentService.Stub {
for (int i=0; i<numCalls; i++) {
ObserverCall oc = calls.get(i);
try {
- oc.mObserver.onChange(oc.mSelfNotify);
+ oc.mObserver.onChange(oc.mSelfChange, uri);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
}
@@ -218,13 +218,12 @@ public final class ContentService extends IContentService.Stub {
public static final class ObserverCall {
final ObserverNode mNode;
final IContentObserver mObserver;
- final boolean mSelfNotify;
+ final boolean mSelfChange;
- ObserverCall(ObserverNode node, IContentObserver observer,
- boolean selfNotify) {
+ ObserverCall(ObserverNode node, IContentObserver observer, boolean selfChange) {
mNode = node;
mObserver = observer;
- mSelfNotify = selfNotify;
+ mSelfChange = selfChange;
}
}
@@ -668,7 +667,7 @@ public final class ContentService extends IContentService.Stub {
}
private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
- boolean selfNotify, ArrayList<ObserverCall> calls) {
+ boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) {
int N = mObservers.size();
IBinder observerBinder = observer == null ? null : observer.asBinder();
for (int i = 0; i < N; i++) {
@@ -676,28 +675,29 @@ public final class ContentService extends IContentService.Stub {
// Don't notify the observer if it sent the notification and isn't interesed
// in self notifications
- if (entry.observer.asBinder() == observerBinder && !selfNotify) {
+ boolean selfChange = (entry.observer.asBinder() == observerBinder);
+ if (selfChange && !observerWantsSelfNotifications) {
continue;
}
// Make sure the observer is interested in the notification
if (leaf || (!leaf && entry.notifyForDescendents)) {
- calls.add(new ObserverCall(this, entry.observer, selfNotify));
+ calls.add(new ObserverCall(this, entry.observer, selfChange));
}
}
}
public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
- boolean selfNotify, ArrayList<ObserverCall> calls) {
+ boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) {
String segment = null;
int segmentCount = countUriSegments(uri);
if (index >= segmentCount) {
// This is the leaf node, notify all observers
- collectMyObserversLocked(true, observer, selfNotify, calls);
+ collectMyObserversLocked(true, observer, observerWantsSelfNotifications, calls);
} else if (index < segmentCount){
segment = getUriSegment(uri, index);
// Notify any observers at this level who are interested in descendents
- collectMyObserversLocked(false, observer, selfNotify, calls);
+ collectMyObserversLocked(false, observer, observerWantsSelfNotifications, calls);
}
int N = mChildren.size();
@@ -705,7 +705,8 @@ public final class ContentService extends IContentService.Stub {
ObserverNode node = mChildren.get(i);
if (segment == null || node.mName.equals(segment)) {
// We found the child,
- node.collectObserversLocked(uri, index + 1, observer, selfNotify, calls);
+ node.collectObserversLocked(uri, index + 1,
+ observer, observerWantsSelfNotifications, calls);
if (segment != null) {
break;
}
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 22df272..b28ed8d 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -300,7 +300,7 @@ public abstract class AbstractCursor implements CrossProcessCursor {
*/
protected void onChange(boolean selfChange) {
synchronized (mSelfObserverLock) {
- mContentObservable.dispatchChange(selfChange);
+ mContentObservable.dispatchChange(selfChange, null);
if (mNotifyUri != null && selfChange) {
mContentResolver.notifyChange(mNotifyUri, mSelfObserver);
}
diff --git a/core/java/android/database/ContentObservable.java b/core/java/android/database/ContentObservable.java
index 8d7b7c5..7692bb3 100644
--- a/core/java/android/database/ContentObservable.java
+++ b/core/java/android/database/ContentObservable.java
@@ -16,40 +16,75 @@
package android.database;
+import android.net.Uri;
+
/**
- * A specialization of Observable for ContentObserver that provides methods for
- * invoking the various callback methods of ContentObserver.
+ * A specialization of {@link Observable} for {@link ContentObserver}
+ * that provides methods for sending notifications to a list of
+ * {@link ContentObserver} objects.
*/
public class ContentObservable extends Observable<ContentObserver> {
-
+ // Even though the generic method defined in Observable would be perfectly
+ // fine on its own, we can't delete this overridden method because it would
+ // potentially break binary compatibility with existing applications.
@Override
public void registerObserver(ContentObserver observer) {
super.registerObserver(observer);
}
/**
- * invokes dispatchUpdate on each observer, unless the observer doesn't want
- * self-notifications and the update is from a self-notification
- * @param selfChange
+ * Invokes {@link ContentObserver#dispatchChange(boolean)} on each observer.
+ * <p>
+ * If <code>selfChange</code> is true, only delivers the notification
+ * to the observer if it has indicated that it wants to receive self-change
+ * notifications by implementing {@link ContentObserver#deliverSelfNotifications}
+ * to return true.
+ * </p>
+ *
+ * @param selfChange True if this is a self-change notification.
+ *
+ * @deprecated Use {@link #dispatchChange(boolean, Uri)} instead.
*/
+ @Deprecated
public void dispatchChange(boolean selfChange) {
+ dispatchChange(selfChange, null);
+ }
+
+ /**
+ * Invokes {@link ContentObserver#dispatchChange(boolean, Uri)} on each observer.
+ * Includes the changed content Uri when available.
+ * <p>
+ * If <code>selfChange</code> is true, only delivers the notification
+ * to the observer if it has indicated that it wants to receive self-change
+ * notifications by implementing {@link ContentObserver#deliverSelfNotifications}
+ * to return true.
+ * </p>
+ *
+ * @param selfChange True if this is a self-change notification.
+ * @param uri The Uri of the changed content, or null if unknown.
+ */
+ public void dispatchChange(boolean selfChange, Uri uri) {
synchronized(mObservers) {
for (ContentObserver observer : mObservers) {
if (!selfChange || observer.deliverSelfNotifications()) {
- observer.dispatchChange(selfChange);
+ observer.dispatchChange(selfChange, uri);
}
}
}
}
/**
- * invokes onChange on each observer
- * @param selfChange
+ * Invokes {@link ContentObserver#onChange} on each observer.
+ *
+ * @param selfChange True if this is a self-change notification.
+ *
+ * @deprecated Use {@link #dispatchChange} instead.
*/
+ @Deprecated
public void notifyChange(boolean selfChange) {
synchronized(mObservers) {
for (ContentObserver observer : mObservers) {
- observer.onChange(selfChange);
+ observer.onChange(selfChange, null);
}
}
}
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index 3b829a3..e4fbc28 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -16,65 +16,23 @@
package android.database;
+import android.net.Uri;
import android.os.Handler;
/**
- * Receives call backs for changes to content. Must be implemented by objects which are added
- * to a {@link ContentObservable}.
+ * Receives call backs for changes to content.
+ * Must be implemented by objects which are added to a {@link ContentObservable}.
*/
public abstract class ContentObserver {
+ private final Object mLock = new Object();
+ private Transport mTransport; // guarded by mLock
- private Transport mTransport;
-
- // Protects mTransport
- private Object lock = new Object();
-
- /* package */ Handler mHandler;
-
- private final class NotificationRunnable implements Runnable {
-
- private boolean mSelf;
-
- public NotificationRunnable(boolean self) {
- mSelf = self;
- }
-
- public void run() {
- ContentObserver.this.onChange(mSelf);
- }
- }
-
- private static final class Transport extends IContentObserver.Stub {
- ContentObserver mContentObserver;
-
- public Transport(ContentObserver contentObserver) {
- mContentObserver = contentObserver;
- }
-
- public boolean deliverSelfNotifications() {
- ContentObserver contentObserver = mContentObserver;
- if (contentObserver != null) {
- return contentObserver.deliverSelfNotifications();
- }
- return false;
- }
-
- public void onChange(boolean selfChange) {
- ContentObserver contentObserver = mContentObserver;
- if (contentObserver != null) {
- contentObserver.dispatchChange(selfChange);
- }
- }
-
- public void releaseContentObserver() {
- mContentObserver = null;
- }
- }
+ Handler mHandler;
/**
- * onChange() will happen on the provider Handler.
+ * Creates a content observer.
*
- * @param handler The handler to run {@link #onChange} on.
+ * @param handler The handler to run {@link #onChange} on, or null if none.
*/
public ContentObserver(Handler handler) {
mHandler = handler;
@@ -86,7 +44,7 @@ public abstract class ContentObserver {
* {@hide}
*/
public IContentObserver getContentObserver() {
- synchronized(lock) {
+ synchronized (mLock) {
if (mTransport == null) {
mTransport = new Transport(this);
}
@@ -101,8 +59,8 @@ public abstract class ContentObserver {
* {@hide}
*/
public IContentObserver releaseContentObserver() {
- synchronized(lock) {
- Transport oldTransport = mTransport;
+ synchronized (mLock) {
+ final Transport oldTransport = mTransport;
if (oldTransport != null) {
oldTransport.releaseContentObserver();
mTransport = null;
@@ -112,27 +70,134 @@ public abstract class ContentObserver {
}
/**
- * Returns true if this observer is interested in notifications for changes
- * made through the cursor the observer is registered with.
+ * Returns true if this observer is interested receiving self-change notifications.
+ *
+ * Subclasses should override this method to indicate whether the observer
+ * is interested in receiving notifications for changes that it made to the
+ * content itself.
+ *
+ * @return True if self-change notifications should be delivered to the observer.
*/
public boolean deliverSelfNotifications() {
return false;
}
/**
- * This method is called when a change occurs to the cursor that
- * is being observed.
- *
- * @param selfChange true if the update was caused by a call to <code>commit</code> on the
- * cursor that is being observed.
+ * This method is called when a content change occurs.
+ * <p>
+ * Subclasses should override this method to handle content changes.
+ * </p>
+ *
+ * @param selfChange True if this is a self-change notification.
*/
- public void onChange(boolean selfChange) {}
+ public void onChange(boolean selfChange) {
+ // Do nothing. Subclass should override.
+ }
+ /**
+ * This method is called when a content change occurs.
+ * Includes the changed content Uri when available.
+ * <p>
+ * Subclasses should override this method to handle content changes.
+ * To ensure correct operation on older versions of the framework that
+ * did not provide a Uri argument, applications should also implement
+ * the {@link #onChange(boolean)} overload of this method whenever they
+ * implement the {@link #onChange(boolean, Uri)} overload.
+ * </p><p>
+ * Example implementation:
+ * <pre><code>
+ * // Implement the onChange(boolean) method to delegate the change notification to
+ * // the onChange(boolean, Uri) method to ensure correct operation on older versions
+ * // of the framework that did not have the onChange(boolean, Uri) method.
+ * {@literal @Override}
+ * public void onChange(boolean selfChange) {
+ * onChange(selfChange, null);
+ * }
+ *
+ * // Implement the onChange(boolean, Uri) method to take advantage of the new Uri argument.
+ * {@literal @Override}
+ * public void onChange(boolean selfChange, Uri uri) {
+ * // Handle change.
+ * }
+ * </code></pre>
+ * </p>
+ *
+ * @param selfChange True if this is a self-change notification.
+ * @param uri The Uri of the changed content, or null if unknown.
+ */
+ public void onChange(boolean selfChange, Uri uri) {
+ onChange(selfChange);
+ }
+
+ /**
+ * Dispatches a change notification to the observer.
+ * <p>
+ * If a {@link Handler} was supplied to the {@link ContentObserver} constructor,
+ * then a call to the {@link #onChange} method is posted to the handler's message queue.
+ * Otherwise, the {@link #onChange} method is invoked immediately on this thread.
+ * </p>
+ *
+ * @param selfChange True if this is a self-change notification.
+ *
+ * @deprecated Use {@link #dispatchChange(boolean, Uri)} instead.
+ */
+ @Deprecated
public final void dispatchChange(boolean selfChange) {
+ dispatchChange(selfChange, null);
+ }
+
+ /**
+ * Dispatches a change notification to the observer.
+ * Includes the changed content Uri when available.
+ * <p>
+ * If a {@link Handler} was supplied to the {@link ContentObserver} constructor,
+ * then a call to the {@link #onChange} method is posted to the handler's message queue.
+ * Otherwise, the {@link #onChange} method is invoked immediately on this thread.
+ * </p>
+ *
+ * @param selfChange True if this is a self-change notification.
+ * @param uri The Uri of the changed content, or null if unknown.
+ */
+ public final void dispatchChange(boolean selfChange, Uri uri) {
if (mHandler == null) {
- onChange(selfChange);
+ onChange(selfChange, uri);
} else {
- mHandler.post(new NotificationRunnable(selfChange));
+ mHandler.post(new NotificationRunnable(selfChange, uri));
+ }
+ }
+
+ private final class NotificationRunnable implements Runnable {
+ private final boolean mSelfChange;
+ private final Uri mUri;
+
+ public NotificationRunnable(boolean selfChange, Uri uri) {
+ mSelfChange = selfChange;
+ mUri = uri;
+ }
+
+ @Override
+ public void run() {
+ ContentObserver.this.onChange(mSelfChange, mUri);
+ }
+ }
+
+ private static final class Transport extends IContentObserver.Stub {
+ private ContentObserver mContentObserver;
+
+ public Transport(ContentObserver contentObserver) {
+ mContentObserver = contentObserver;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ ContentObserver contentObserver = mContentObserver;
+ if (contentObserver != null) {
+ contentObserver.dispatchChange(selfChange, uri);
+ }
+ }
+
+ public void releaseContentObserver() {
+ mContentObserver = null;
}
}
}
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index aa0f61e..167278a 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -16,6 +16,7 @@
package android.database;
+import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -78,9 +79,9 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
}
@Override
- public void onChange(boolean selfChange) {
+ public void onChange(boolean selfChange, Uri uri) {
try {
- mRemote.onChange(selfChange);
+ mRemote.onChange(selfChange, uri);
} catch (RemoteException ex) {
// Do nothing, the far side is dead
}
diff --git a/core/java/android/database/DataSetObservable.java b/core/java/android/database/DataSetObservable.java
index 51c72c1..ca77a13 100644
--- a/core/java/android/database/DataSetObservable.java
+++ b/core/java/android/database/DataSetObservable.java
@@ -17,13 +17,15 @@
package android.database;
/**
- * A specialization of Observable for DataSetObserver that provides methods for
- * invoking the various callback methods of DataSetObserver.
+ * A specialization of {@link Observable} for {@link DataSetObserver}
+ * that provides methods for sending notifications to a list of
+ * {@link DataSetObserver} objects.
*/
public class DataSetObservable extends Observable<DataSetObserver> {
/**
- * Invokes onChanged on each observer. Called when the data set being observed has
- * changed, and which when read contains the new state of the data.
+ * Invokes {@link DataSetObserver#onChanged} on each observer.
+ * Called when the contents of the data set have changed. The recipient
+ * will obtain the new contents the next time it queries the data set.
*/
public void notifyChanged() {
synchronized(mObservers) {
@@ -38,8 +40,9 @@ public class DataSetObservable extends Observable<DataSetObserver> {
}
/**
- * Invokes onInvalidated on each observer. Called when the data set being monitored
- * has changed such that it is no longer valid.
+ * Invokes {@link DataSetObserver#onInvalidated} on each observer.
+ * Called when the data set is no longer valid and cannot be queried again,
+ * such as when the data set has been closed.
*/
public void notifyInvalidated() {
synchronized (mObservers) {
diff --git a/core/java/android/database/IContentObserver.aidl b/core/java/android/database/IContentObserver.aidl
index ac2f975..13aff05 100755
--- a/core/java/android/database/IContentObserver.aidl
+++ b/core/java/android/database/IContentObserver.aidl
@@ -17,6 +17,8 @@
package android.database;
+import android.net.Uri;
+
/**
* @hide
*/
@@ -27,5 +29,5 @@ interface IContentObserver
* observed. selfUpdate is true if the update was caused by a call to
* commit on the cursor that is being observed.
*/
- oneway void onChange(boolean selfUpdate);
+ oneway void onChange(boolean selfUpdate, in Uri uri);
}
diff --git a/core/java/android/database/Observable.java b/core/java/android/database/Observable.java
index b6fecab..aff32db 100644
--- a/core/java/android/database/Observable.java
+++ b/core/java/android/database/Observable.java
@@ -19,7 +19,12 @@ package android.database;
import java.util.ArrayList;
/**
- * Provides methods for (un)registering arbitrary observers in an ArrayList.
+ * Provides methods for registering or unregistering arbitrary observers in an {@link ArrayList}.
+ *
+ * This abstract class is intended to be subclassed and specialized to maintain
+ * a registry of observers of specific types and dispatch notifications to them.
+ *
+ * @param T The observer type.
*/
public abstract class Observable<T> {
/**
@@ -66,13 +71,13 @@ public abstract class Observable<T> {
mObservers.remove(index);
}
}
-
+
/**
- * Remove all registered observer
+ * Remove all registered observers.
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
- }
+ }
}
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index e8f60b4..7a1ef66 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -102,6 +102,15 @@ public class NetworkStats implements Parcelable {
this.operations = operations;
}
+ public boolean isNegative() {
+ return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0;
+ }
+
+ public boolean isEmpty() {
+ return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0
+ && operations == 0;
+ }
+
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
@@ -343,6 +352,7 @@ public class NetworkStats implements Parcelable {
* on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
* since operation counts are at data layer.
*/
+ @Deprecated
public void spliceOperationsFrom(NetworkStats stats) {
for (int i = 0; i < size; i++) {
final int j = stats.findIndex(IFACE_ALL, uid[i], set[i], tag[i]);
@@ -397,7 +407,7 @@ public class NetworkStats implements Parcelable {
* Return total of all fields represented by this snapshot object.
*/
public Entry getTotal(Entry recycle) {
- return getTotal(recycle, null, UID_ALL);
+ return getTotal(recycle, null, UID_ALL, false);
}
/**
@@ -405,7 +415,7 @@ public class NetworkStats implements Parcelable {
* the requested {@link #uid}.
*/
public Entry getTotal(Entry recycle, int limitUid) {
- return getTotal(recycle, null, limitUid);
+ return getTotal(recycle, null, limitUid, false);
}
/**
@@ -413,7 +423,11 @@ public class NetworkStats implements Parcelable {
* the requested {@link #iface}.
*/
public Entry getTotal(Entry recycle, HashSet<String> limitIface) {
- return getTotal(recycle, limitIface, UID_ALL);
+ return getTotal(recycle, limitIface, UID_ALL, false);
+ }
+
+ public Entry getTotalIncludingTags(Entry recycle) {
+ return getTotal(recycle, null, UID_ALL, true);
}
/**
@@ -423,7 +437,8 @@ public class NetworkStats implements Parcelable {
* @param limitIface Set of {@link #iface} to include in total; or {@code
* null} to include all ifaces.
*/
- private Entry getTotal(Entry recycle, HashSet<String> limitIface, int limitUid) {
+ private Entry getTotal(
+ Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) {
final Entry entry = recycle != null ? recycle : new Entry();
entry.iface = IFACE_ALL;
@@ -442,7 +457,7 @@ public class NetworkStats implements Parcelable {
if (matchesUid && matchesIface) {
// skip specific tags, since already counted in TAG_NONE
- if (tag[i] != TAG_NONE) continue;
+ if (tag[i] != TAG_NONE && !includeTags) continue;
entry.rxBytes += rxBytes[i];
entry.rxPackets += rxPackets[i];
@@ -460,7 +475,7 @@ public class NetworkStats implements Parcelable {
* time, and that none of them have disappeared.
*/
public NetworkStats subtract(NetworkStats right) {
- return subtract(this, right, null);
+ return subtract(this, right, null, null);
}
/**
@@ -471,12 +486,12 @@ public class NetworkStats implements Parcelable {
* If counters have rolled backwards, they are clamped to {@code 0} and
* reported to the given {@link NonMonotonicObserver}.
*/
- public static NetworkStats subtract(
- NetworkStats left, NetworkStats right, NonMonotonicObserver observer) {
+ public static <C> NetworkStats subtract(
+ NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie) {
long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
if (deltaRealtime < 0) {
if (observer != null) {
- observer.foundNonMonotonic(left, -1, right, -1);
+ observer.foundNonMonotonic(left, -1, right, -1, cookie);
}
deltaRealtime = 0;
}
@@ -510,7 +525,7 @@ public class NetworkStats implements Parcelable {
if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
|| entry.txPackets < 0 || entry.operations < 0) {
if (observer != null) {
- observer.foundNonMonotonic(left, i, right, j);
+ observer.foundNonMonotonic(left, i, right, j, cookie);
}
entry.rxBytes = Math.max(entry.rxBytes, 0);
entry.rxPackets = Math.max(entry.rxPackets, 0);
@@ -663,8 +678,8 @@ public class NetworkStats implements Parcelable {
}
};
- public interface NonMonotonicObserver {
+ public interface NonMonotonicObserver<C> {
public void foundNonMonotonic(
- NetworkStats left, int leftIndex, NetworkStats right, int rightIndex);
+ NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
}
}
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 8c01331..faf8a3f 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -26,16 +26,18 @@ import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray;
import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
+import static com.android.internal.util.ArrayUtils.total;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.MathUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
import java.io.CharArrayWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
-import java.io.PrintWriter;
import java.net.ProtocolException;
import java.util.Arrays;
import java.util.Random;
@@ -74,6 +76,7 @@ public class NetworkStatsHistory implements Parcelable {
private long[] txBytes;
private long[] txPackets;
private long[] operations;
+ private long totalBytes;
public static class Entry {
public static final long UNKNOWN = -1;
@@ -106,6 +109,12 @@ public class NetworkStatsHistory implements Parcelable {
if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize];
if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize];
bucketCount = 0;
+ totalBytes = 0;
+ }
+
+ public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
+ this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
+ recordEntireHistory(existing);
}
public NetworkStatsHistory(Parcel in) {
@@ -118,6 +127,7 @@ public class NetworkStatsHistory implements Parcelable {
txPackets = readLongArray(in);
operations = readLongArray(in);
bucketCount = bucketStart.length;
+ totalBytes = in.readLong();
}
/** {@inheritDoc} */
@@ -130,6 +140,7 @@ public class NetworkStatsHistory implements Parcelable {
writeLongArray(out, txBytes, bucketCount);
writeLongArray(out, txPackets, bucketCount);
writeLongArray(out, operations, bucketCount);
+ out.writeLong(totalBytes);
}
public NetworkStatsHistory(DataInputStream in) throws IOException {
@@ -144,6 +155,7 @@ public class NetworkStatsHistory implements Parcelable {
txPackets = new long[bucketStart.length];
operations = new long[bucketStart.length];
bucketCount = bucketStart.length;
+ totalBytes = total(rxBytes) + total(txBytes);
break;
}
case VERSION_ADD_PACKETS:
@@ -158,6 +170,7 @@ public class NetworkStatsHistory implements Parcelable {
txPackets = readVarLongArray(in);
operations = readVarLongArray(in);
bucketCount = bucketStart.length;
+ totalBytes = total(rxBytes) + total(txBytes);
break;
}
default: {
@@ -208,6 +221,13 @@ public class NetworkStatsHistory implements Parcelable {
}
/**
+ * Return total bytes represented by this history.
+ */
+ public long getTotalBytes() {
+ return totalBytes;
+ }
+
+ /**
* Return index of bucket that contains or is immediately before the
* requested time.
*/
@@ -266,13 +286,16 @@ public class NetworkStatsHistory implements Parcelable {
* distribute across internal buckets, creating new buckets as needed.
*/
public void recordData(long start, long end, NetworkStats.Entry entry) {
- if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0
- || entry.operations < 0) {
+ long rxBytes = entry.rxBytes;
+ long rxPackets = entry.rxPackets;
+ long txBytes = entry.txBytes;
+ long txPackets = entry.txPackets;
+ long operations = entry.operations;
+
+ if (entry.isNegative()) {
throw new IllegalArgumentException("tried recording negative data");
}
- if (entry.rxBytes == 0 && entry.rxPackets == 0 && entry.txBytes == 0 && entry.txPackets == 0
- && entry.operations == 0) {
- // nothing to record; skip
+ if (entry.isEmpty()) {
return;
}
@@ -295,21 +318,23 @@ public class NetworkStatsHistory implements Parcelable {
if (overlap <= 0) continue;
// integer math each time is faster than floating point
- final long fracRxBytes = entry.rxBytes * overlap / duration;
- final long fracRxPackets = entry.rxPackets * overlap / duration;
- final long fracTxBytes = entry.txBytes * overlap / duration;
- final long fracTxPackets = entry.txPackets * overlap / duration;
- final long fracOperations = entry.operations * overlap / duration;
+ final long fracRxBytes = rxBytes * overlap / duration;
+ final long fracRxPackets = rxPackets * overlap / duration;
+ final long fracTxBytes = txBytes * overlap / duration;
+ final long fracTxPackets = txPackets * overlap / duration;
+ final long fracOperations = operations * overlap / duration;
addLong(activeTime, i, overlap);
- addLong(rxBytes, i, fracRxBytes); entry.rxBytes -= fracRxBytes;
- addLong(rxPackets, i, fracRxPackets); entry.rxPackets -= fracRxPackets;
- addLong(txBytes, i, fracTxBytes); entry.txBytes -= fracTxBytes;
- addLong(txPackets, i, fracTxPackets); entry.txPackets -= fracTxPackets;
- addLong(operations, i, fracOperations); entry.operations -= fracOperations;
+ addLong(this.rxBytes, i, fracRxBytes); rxBytes -= fracRxBytes;
+ addLong(this.rxPackets, i, fracRxPackets); rxPackets -= fracRxPackets;
+ addLong(this.txBytes, i, fracTxBytes); txBytes -= fracTxBytes;
+ addLong(this.txPackets, i, fracTxPackets); txPackets -= fracTxPackets;
+ addLong(this.operations, i, fracOperations); operations -= fracOperations;
duration -= overlap;
}
+
+ totalBytes += entry.rxBytes + entry.txBytes;
}
/**
@@ -394,6 +419,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Remove buckets older than requested cutoff.
*/
+ @Deprecated
public void removeBucketsBefore(long cutoff) {
int i;
for (i = 0; i < bucketCount; i++) {
@@ -415,6 +441,8 @@ public class NetworkStatsHistory implements Parcelable {
if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length);
if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
bucketCount -= i;
+
+ // TODO: subtract removed values from totalBytes
}
}
@@ -527,19 +555,17 @@ public class NetworkStatsHistory implements Parcelable {
return (long) (start + (r.nextFloat() * (end - start)));
}
- public void dump(String prefix, PrintWriter pw, boolean fullHistory) {
- pw.print(prefix);
+ public void dump(IndentingPrintWriter pw, boolean fullHistory) {
pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
+ pw.increaseIndent();
final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
if (start > 0) {
- pw.print(prefix);
- pw.print(" (omitting "); pw.print(start); pw.println(" buckets)");
+ pw.print("(omitting "); pw.print(start); pw.println(" buckets)");
}
for (int i = start; i < bucketCount; i++) {
- pw.print(prefix);
- pw.print(" bucketStart="); pw.print(bucketStart[i]);
+ pw.print("bucketStart="); pw.print(bucketStart[i]);
if (activeTime != null) { pw.print(" activeTime="); pw.print(activeTime[i]); }
if (rxBytes != null) { pw.print(" rxBytes="); pw.print(rxBytes[i]); }
if (rxPackets != null) { pw.print(" rxPackets="); pw.print(rxPackets[i]); }
@@ -548,12 +574,14 @@ public class NetworkStatsHistory implements Parcelable {
if (operations != null) { pw.print(" operations="); pw.print(operations[i]); }
pw.println();
}
+
+ pw.decreaseIndent();
}
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
- dump("", new PrintWriter(writer), false);
+ dump(new IndentingPrintWriter(writer, " "), false);
return writer.toString();
}
@@ -579,6 +607,10 @@ public class NetworkStatsHistory implements Parcelable {
if (array != null) array[i] += value;
}
+ public int estimateResizeBuckets(long newBucketDuration) {
+ return (int) (size() * getBucketDuration() / newBucketDuration);
+ }
+
/**
* Utility methods for interacting with {@link DataInputStream} and
* {@link DataOutputStream}, mostly dealing with writing partial arrays.
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 418b82f..8ebfd8d 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -18,6 +18,7 @@ package android.net;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkIdentity.scrubSubscriberId;
import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
@@ -231,10 +232,13 @@ public class NetworkTemplate implements Parcelable {
* Check if matches Wi-Fi network template.
*/
private boolean matchesWifi(NetworkIdentity ident) {
- if (ident.mType == TYPE_WIFI) {
- return true;
+ switch (ident.mType) {
+ case TYPE_WIFI:
+ case TYPE_WIFI_P2P:
+ return true;
+ default:
+ return false;
}
- return false;
}
/**
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 8bdb669..dfdea38 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -195,7 +195,7 @@ public class TrafficStats {
// subtract starting values and return delta
final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
final NetworkStats profilingDelta = NetworkStats.subtract(
- profilingStop, sActiveProfilingStart, null);
+ profilingStop, sActiveProfilingStart, null, null);
sActiveProfilingStart = null;
return profilingDelta;
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 9a53d76..270e9be 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -45,4 +45,5 @@ interface IPowerManager
// sets the brightness of the backlights (screen, keyboard, button) 0-255
void setBacklightBrightness(int brightness);
void setAttentionLight(boolean on, int color);
+ void setAutoBrightnessAdjustment(float adj);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0202c47..ef8cb16 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1401,6 +1401,12 @@ public final class Settings {
public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
/**
+ * Adjustment to auto-brightness to make it generally more (>0.0 <1.0)
+ * or less (<0.0 >-1.0) bright.
+ */
+ public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
+
+ /**
* SCREEN_BRIGHTNESS_MODE value for manual mode.
*/
public static final int SCREEN_BRIGHTNESS_MODE_MANUAL = 0;
@@ -1927,6 +1933,7 @@ public final class Settings {
SCREEN_OFF_TIMEOUT,
SCREEN_BRIGHTNESS,
SCREEN_BRIGHTNESS_MODE,
+ SCREEN_AUTO_BRIGHTNESS_ADJ,
VIBRATE_ON,
MODE_RINGER,
MODE_RINGER_STREAMS_AFFECTED,
@@ -4104,17 +4111,38 @@ public final class Settings {
/** {@hide} */
public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
/** {@hide} */
- public static final String NETSTATS_PERSIST_THRESHOLD = "netstats_persist_threshold";
+ public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
+ /** {@hide} */
+ public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
/** {@hide} */
- public static final String NETSTATS_NETWORK_BUCKET_DURATION = "netstats_network_bucket_duration";
+ public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
+
+ /** {@hide} */
+ public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
/** {@hide} */
- public static final String NETSTATS_NETWORK_MAX_HISTORY = "netstats_network_max_history";
+ public static final String NETSTATS_DEV_PERSIST_BYTES = "netstats_dev_persist_bytes";
+ /** {@hide} */
+ public static final String NETSTATS_DEV_ROTATE_AGE = "netstats_dev_rotate_age";
+ /** {@hide} */
+ public static final String NETSTATS_DEV_DELETE_AGE = "netstats_dev_delete_age";
+
/** {@hide} */
public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
/** {@hide} */
- public static final String NETSTATS_UID_MAX_HISTORY = "netstats_uid_max_history";
+ public static final String NETSTATS_UID_PERSIST_BYTES = "netstats_uid_persist_bytes";
+ /** {@hide} */
+ public static final String NETSTATS_UID_ROTATE_AGE = "netstats_uid_rotate_age";
+ /** {@hide} */
+ public static final String NETSTATS_UID_DELETE_AGE = "netstats_uid_delete_age";
+
+ /** {@hide} */
+ public static final String NETSTATS_UID_TAG_BUCKET_DURATION = "netstats_uid_tag_bucket_duration";
+ /** {@hide} */
+ public static final String NETSTATS_UID_TAG_PERSIST_BYTES = "netstats_uid_tag_persist_bytes";
+ /** {@hide} */
+ public static final String NETSTATS_UID_TAG_ROTATE_AGE = "netstats_uid_tag_rotate_age";
/** {@hide} */
- public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history";
+ public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
/** Preferred NTP server. {@hide} */
public static final String NTP_SERVER = "ntp_server";
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index c184c11..a52e2ba 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -109,6 +109,9 @@ class MeasuredText {
for (int i = 0; i < spans.length; i++) {
int startInPara = spanned.getSpanStart(spans[i]) - start;
int endInPara = spanned.getSpanEnd(spans[i]) - start;
+ // The span interval may be larger and must be restricted to [start, end[
+ if (startInPara < 0) startInPara = 0;
+ if (endInPara > len) endInPara = len;
for (int j = startInPara; j < endInPara; j++) {
mChars[j] = '\uFFFC';
}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index e06d661..c08a402 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -22,6 +22,7 @@ import android.graphics.ColorFilter;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Picture;
import android.graphics.PorterDuff;
@@ -546,6 +547,7 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nSetMatrix(int renderer, int matrix);
+ @SuppressWarnings("deprecation")
@Override
public void getMatrix(Matrix matrix) {
nGetMatrix(mRenderer, matrix.native_instance);
@@ -658,8 +660,17 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void setDrawFilter(DrawFilter filter) {
mFilter = filter;
+ if (filter == null) {
+ nResetPaintFilter(mRenderer);
+ } else if (filter instanceof PaintFlagsDrawFilter) {
+ PaintFlagsDrawFilter flagsFilter = (PaintFlagsDrawFilter) filter;
+ nSetupPaintFilter(mRenderer, flagsFilter.clearBits, flagsFilter.setBits);
+ }
}
+ private static native void nResetPaintFilter(int renderer);
+ private static native void nSetupPaintFilter(int renderer, int clearBits, int setBits);
+
@Override
public DrawFilter getDrawFilter() {
return mFilter;
@@ -968,6 +979,7 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nDrawPoints(int renderer, float[] points,
int offset, int count, int paint);
+ @SuppressWarnings("deprecation")
@Override
public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
if (index < 0 || index + count > text.length || count * 2 > pos.length) {
@@ -985,6 +997,7 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nDrawPosText(int renderer, char[] text, int index, int count,
float[] pos, int paint);
+ @SuppressWarnings("deprecation")
@Override
public void drawPosText(String text, float[] pos, Paint paint) {
if (text.length() * 2 > pos.length) {
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index c53fc6b..7fd3389 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -185,7 +185,8 @@ public final class ViewTreeObserver {
mTouchableInsets = TOUCHABLE_INSETS_FRAME;
}
- @Override public boolean equals(Object o) {
+ @Override
+ public boolean equals(Object o) {
try {
if (o == null) {
return false;
@@ -357,10 +358,26 @@ public final class ViewTreeObserver {
* @param victim The callback to remove
*
* @throws IllegalStateException If {@link #isAlive()} returns false
+ *
+ * @deprecated Use #removeOnGlobalLayoutListener instead
*
* @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
*/
+ @Deprecated
public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
+ removeOnGlobalLayoutListener(victim);
+ }
+
+ /**
+ * Remove a previously installed global layout callback
+ *
+ * @param victim The callback to remove
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ *
+ * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
+ */
+ public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
return;
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 5ec1ec3..bd02d62 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -193,10 +193,12 @@ public class BaseInputConnection implements InputConnection {
/**
* The default implementation performs the deletion around the current
* selection position of the editable text.
+ * @param beforeLength
+ * @param afterLength
*/
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength
- + " / " + rightLength);
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
+ + " / " + afterLength);
final Editable content = getEditable();
if (content == null) return false;
@@ -226,17 +228,17 @@ public class BaseInputConnection implements InputConnection {
int deleted = 0;
- if (leftLength > 0) {
- int start = a - leftLength;
+ if (beforeLength > 0) {
+ int start = a - beforeLength;
if (start < 0) start = 0;
content.delete(start, a);
deleted = a - start;
}
- if (rightLength > 0) {
+ if (afterLength > 0) {
b = b - deleted;
- int end = b + rightLength;
+ int end = b + afterLength;
if (end > content.length()) end = content.length();
content.delete(b, end);
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index a6639d1..bc2a270 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -138,19 +138,20 @@ public interface InputConnection {
int flags);
/**
- * Delete <var>leftLength</var> characters of text before the current cursor
- * position, and delete <var>rightLength</var> characters of text after the
+ * Delete <var>beforeLength</var> characters of text before the current cursor
+ * position, and delete <var>afterLength</var> characters of text after the
* current cursor position, excluding composing text.
*
- * @param leftLength The number of characters to be deleted before the
+ *
+ * @param beforeLength The number of characters to be deleted before the
* current cursor position.
- * @param rightLength The number of characters to be deleted after the
+ * @param afterLength The number of characters to be deleted after the
* current cursor position.
- *
+ *
* @return Returns true on success, false if the input connection is no longer
* valid.
*/
- public boolean deleteSurroundingText(int leftLength, int rightLength);
+ public boolean deleteSurroundingText(int beforeLength, int afterLength);
/**
* Set composing text around the current cursor position with the given text,
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 690ea85..a48473e 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -62,8 +62,8 @@ public class InputConnectionWrapper implements InputConnection {
return mTarget.getExtractedText(request, flags);
}
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
- return mTarget.deleteSurroundingText(leftLength, rightLength);
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ return mTarget.deleteSurroundingText(beforeLength, afterLength);
}
public boolean setComposingText(CharSequence text, int newCursorPosition) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 148be5c..3697635 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -384,17 +384,17 @@ public class WebView extends AbsoluteLayout
}
@Override
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// Look for one-character delete and send it as a key press.
- if (leftLength == 1 && rightLength == 0) {
+ if (beforeLength == 1 && afterLength == 0) {
sendKeyPress(KeyEvent.KEYCODE_DEL);
- } else if (leftLength == 0 && rightLength == 1){
+ } else if (beforeLength == 0 && afterLength == 1){
sendKeyPress(KeyEvent.KEYCODE_FORWARD_DEL);
} else if (mWebViewCore != null) {
mWebViewCore.sendMessage(EventHub.DELETE_SURROUNDING_TEXT,
- leftLength, rightLength);
+ beforeLength, afterLength);
}
- return super.deleteSurroundingText(leftLength, rightLength);
+ return super.deleteSurroundingText(beforeLength, afterLength);
}
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index baeb0ed..fe5c04c 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1742,6 +1742,7 @@ public final class WebViewCore {
Rect rect = (Rect) msg.obj;
nativeScrollLayer(mNativeClass, nativeLayer,
rect);
+ break;
case DELETE_TEXT: {
int[] handles = (int[]) msg.obj;
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index 60b24bc..be6b4e2 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -33,8 +33,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.widget.ActivityChooserModel.ActivityChooserModelClient;
/**
@@ -366,7 +364,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
getListPopupWindow().dismiss();
ViewTreeObserver viewTreeObserver = getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
- viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
+ viewTreeObserver.removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
}
}
return true;
@@ -400,7 +398,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
}
ViewTreeObserver viewTreeObserver = getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
- viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
+ viewTreeObserver.removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
}
mIsAttachedToWindow = false;
}
@@ -547,6 +545,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
position = mAdapter.getShowDefaultActivity() ? position : position + 1;
Intent launchIntent = mAdapter.getDataModel().chooseActivity(position);
if (launchIntent != null) {
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
mContext.startActivity(launchIntent);
}
}
@@ -564,6 +563,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity);
Intent launchIntent = mAdapter.getDataModel().chooseActivity(index);
if (launchIntent != null) {
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
mContext.startActivity(launchIntent);
}
} else if (view == mExpandActivityOverflowButton) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index e226d37..bb00049 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -558,7 +558,9 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
mCurrentWindowEnd = newWindowEnd;
mCurrentWindowStartUnbounded = newWindowStartUnbounded;
if (mRemoteViewsAdapter != null) {
- mRemoteViewsAdapter.setVisibleRangeHint(mCurrentWindowStart, mCurrentWindowEnd);
+ int adapterStart = modulo(mCurrentWindowStart, adapterCount);
+ int adapterEnd = modulo(mCurrentWindowEnd, adapterCount);
+ mRemoteViewsAdapter.setVisibleRangeHint(adapterStart, adapterEnd);
}
}
requestLayout();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index a210f0b..182df7a 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -71,6 +71,11 @@ import com.android.internal.R;
public class NumberPicker extends LinearLayout {
/**
+ * The number of items show in the selector wheel.
+ */
+ public static final int SELECTOR_WHEEL_ITEM_COUNT = 5;
+
+ /**
* The default update interval during long press.
*/
private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300;
@@ -1137,14 +1142,17 @@ public class NumberPicker extends LinearLayout {
* items shown on the selector wheel) the selector wheel wrapping is
* enabled.
* </p>
- *
+ * <p>
+ * <strong>Note:</strong> If the number of items, i.e. the range
+ * ({@link #getMaxValue()} - {@link #getMinValue()}) is less than
+ * {@link #SELECTOR_WHEEL_ITEM_COUNT}, the selector wheel will not
+ * wrap. Hence, in such a case calling this method is a NOP.
+ * </p>
* @param wrapSelectorWheel Whether to wrap.
*/
public void setWrapSelectorWheel(boolean wrapSelectorWheel) {
- if (wrapSelectorWheel && (mMaxValue - mMinValue) < mSelectorIndices.length) {
- throw new IllegalStateException("Range less than selector items count.");
- }
- if (wrapSelectorWheel != mWrapSelectorWheel) {
+ final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mSelectorIndices.length;
+ if ((!wrapSelectorWheel || wrappingAllowed) && wrapSelectorWheel != mWrapSelectorWheel) {
mWrapSelectorWheel = wrapSelectorWheel;
updateIncrementAndDecrementButtonsVisibilityState();
}
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index bb27b73..22e9ef1 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -279,6 +279,7 @@ public class ShareActionProvider extends ActionProvider {
final int itemId = item.getItemId();
Intent launchIntent = dataModel.chooseActivity(itemId);
if (launchIntent != null) {
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
mContext.startActivity(launchIntent);
}
return true;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f78c247..9fb26ae 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -342,6 +342,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mTextEditSuggestionItemLayout;
private SuggestionsPopupWindow mSuggestionsPopupWindow;
private SuggestionRangeSpan mSuggestionRangeSpan;
+ private Runnable mShowSuggestionRunnable;
private int mCursorDrawableRes;
private final Drawable[] mCursorDrawable = new Drawable[2];
@@ -4513,6 +4514,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mSelectionModifierCursorController.onDetached();
}
+ if (mShowSuggestionRunnable != null) {
+ removeCallbacks(mShowSuggestionRunnable);
+ }
+
hideControllers();
resetResolvedDrawables();
@@ -8330,6 +8335,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
getSelectionController().onTouchEvent(event);
}
+ if (mShowSuggestionRunnable != null) {
+ removeCallbacks(mShowSuggestionRunnable);
+ }
+
if (action == MotionEvent.ACTION_DOWN) {
mLastDownPositionX = event.getX();
mLastDownPositionY = event.getY();
@@ -8370,17 +8379,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
getSelectionEnd(), ClickableSpan.class);
- if (links.length != 0) {
+ if (links.length > 0) {
links[0].onClick(this);
handled = true;
}
}
if (touchIsFinished && (isTextEditable() || mTextIsSelectable)) {
- // Move cursor
- final int offset = getOffsetForPosition(event.getX(), event.getY());
- Selection.setSelection((Spannable) mText, offset);
-
// Show the IME, except when selecting in read-only text.
final InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
@@ -8397,8 +8402,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (!extractedTextModeWillBeStarted()) {
if (isCursorInsideEasyCorrectionSpan()) {
- showSuggestions();
+ if (mShowSuggestionRunnable == null) {
+ mShowSuggestionRunnable = new Runnable() {
+ public void run() {
+ showSuggestions();
+ }
+ };
+ }
+ postDelayed(mShowSuggestionRunnable,
+ ViewConfiguration.getDoubleTapTimeout());
} else if (hasInsertionController()) {
+ // Move cursor
+ final int offset = getOffsetForPosition(event.getX(), event.getY());
+ Selection.setSelection((Spannable) mText, offset);
getInsertionController().show();
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9c45dc6..6a99a2b 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -243,7 +243,7 @@ public class ZygoteInit {
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
- InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream(
+ InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
PRELOADED_CLASSES);
if (is == null) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index edeb2a8..d1aa1ce 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -142,6 +142,14 @@ public class ArrayUtils
return false;
}
+ public static long total(long[] array) {
+ long total = 0;
+ for (long value : array) {
+ total += value;
+ }
+ return total;
+ }
+
/**
* Appends an element to a copy of the array and returns the copy.
* @param array The original array, or null to represent an empty array.
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 3ce95e7..8a8f315 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -17,9 +17,9 @@
package com.android.internal.util;
import android.os.FileUtils;
+import android.util.Slog;
-import com.android.internal.util.FileRotator.Reader;
-import com.android.internal.util.FileRotator.Writer;
+import com.android.internal.util.FileRotator.Rewriter;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -41,12 +41,15 @@ import libcore.io.IoUtils;
* Instead of manipulating files directly, users implement interfaces that
* perform operations on {@link InputStream} and {@link OutputStream}. This
* enables atomic rewriting of file contents in
- * {@link #combineActive(Reader, Writer, long)}.
+ * {@link #rewriteActive(Rewriter, long)}.
* <p>
* Users must periodically call {@link #maybeRotate(long)} to perform actual
* rotation. Not inherently thread safe.
*/
public class FileRotator {
+ private static final String TAG = "FileRotator";
+ private static final boolean LOGD = true;
+
private final File mBasePath;
private final String mPrefix;
private final long mRotateAgeMillis;
@@ -73,6 +76,15 @@ public class FileRotator {
}
/**
+ * External class that reads existing data from given {@link InputStream},
+ * then writes any modified data to {@link OutputStream}.
+ */
+ public interface Rewriter extends Reader, Writer {
+ public void reset();
+ public boolean shouldWrite();
+ }
+
+ /**
* Create a file rotator.
*
* @param basePath Directory under which all files will be placed.
@@ -96,6 +108,8 @@ public class FileRotator {
if (!name.startsWith(mPrefix)) continue;
if (name.endsWith(SUFFIX_BACKUP)) {
+ if (LOGD) Slog.d(TAG, "recovering " + name);
+
final File backupFile = new File(mBasePath, name);
final File file = new File(
mBasePath, name.substring(0, name.length() - SUFFIX_BACKUP.length()));
@@ -104,6 +118,8 @@ public class FileRotator {
backupFile.renameTo(file);
} else if (name.endsWith(SUFFIX_NO_BACKUP)) {
+ if (LOGD) Slog.d(TAG, "recovering " + name);
+
final File noBackupFile = new File(mBasePath, name);
final File file = new File(
mBasePath, name.substring(0, name.length() - SUFFIX_NO_BACKUP.length()));
@@ -116,26 +132,95 @@ public class FileRotator {
}
/**
- * Atomically combine data with existing data in currently active file.
- * Maintains a backup during write, which is restored if the write fails.
+ * Delete all files managed by this rotator.
*/
- public void combineActive(Reader reader, Writer writer, long currentTimeMillis)
+ public void deleteAll() {
+ final FileInfo info = new FileInfo(mPrefix);
+ for (String name : mBasePath.list()) {
+ if (!info.parse(name)) continue;
+
+ // delete each file that matches parser
+ new File(mBasePath, name).delete();
+ }
+ }
+
+ /**
+ * Process currently active file, first reading any existing data, then
+ * writing modified data. Maintains a backup during write, which is restored
+ * if the write fails.
+ */
+ public void rewriteActive(Rewriter rewriter, long currentTimeMillis)
throws IOException {
final String activeName = getActiveName(currentTimeMillis);
+ rewriteSingle(rewriter, activeName);
+ }
- final File file = new File(mBasePath, activeName);
+ @Deprecated
+ public void combineActive(final Reader reader, final Writer writer, long currentTimeMillis)
+ throws IOException {
+ rewriteActive(new Rewriter() {
+ /** {@inheritDoc} */
+ public void reset() {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void read(InputStream in) throws IOException {
+ reader.read(in);
+ }
+
+ /** {@inheritDoc} */
+ public boolean shouldWrite() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void write(OutputStream out) throws IOException {
+ writer.write(out);
+ }
+ }, currentTimeMillis);
+ }
+
+ /**
+ * Process all files managed by this rotator, usually to rewrite historical
+ * data. Each file is processed atomically.
+ */
+ public void rewriteAll(Rewriter rewriter) throws IOException {
+ final FileInfo info = new FileInfo(mPrefix);
+ for (String name : mBasePath.list()) {
+ if (!info.parse(name)) continue;
+
+ // process each file that matches parser
+ rewriteSingle(rewriter, name);
+ }
+ }
+
+ /**
+ * Process a single file atomically, first reading any existing data, then
+ * writing modified data. Maintains a backup during write, which is restored
+ * if the write fails.
+ */
+ private void rewriteSingle(Rewriter rewriter, String name) throws IOException {
+ if (LOGD) Slog.d(TAG, "rewriting " + name);
+
+ final File file = new File(mBasePath, name);
final File backupFile;
+ rewriter.reset();
+
if (file.exists()) {
// read existing data
- readFile(file, reader);
+ readFile(file, rewriter);
+
+ // skip when rewriter has nothing to write
+ if (!rewriter.shouldWrite()) return;
// backup existing data during write
- backupFile = new File(mBasePath, activeName + SUFFIX_BACKUP);
+ backupFile = new File(mBasePath, name + SUFFIX_BACKUP);
file.renameTo(backupFile);
try {
- writeFile(file, writer);
+ writeFile(file, rewriter);
// write success, delete backup
backupFile.delete();
@@ -148,11 +233,11 @@ public class FileRotator {
} else {
// create empty backup during write
- backupFile = new File(mBasePath, activeName + SUFFIX_NO_BACKUP);
+ backupFile = new File(mBasePath, name + SUFFIX_NO_BACKUP);
backupFile.createNewFile();
try {
- writeFile(file, writer);
+ writeFile(file, rewriter);
// write success, delete empty backup
backupFile.delete();
@@ -176,6 +261,8 @@ public class FileRotator {
// read file when it overlaps
if (info.startMillis <= matchEndMillis && matchStartMillis <= info.endMillis) {
+ if (LOGD) Slog.d(TAG, "reading matching " + name);
+
final File file = new File(mBasePath, name);
readFile(file, reader);
}
@@ -224,16 +311,20 @@ public class FileRotator {
if (!info.parse(name)) continue;
if (info.isActive()) {
- // found active file; rotate if old enough
- if (info.startMillis < rotateBefore) {
+ if (info.startMillis <= rotateBefore) {
+ // found active file; rotate if old enough
+ if (LOGD) Slog.d(TAG, "rotating " + name);
+
info.endMillis = currentTimeMillis;
final File file = new File(mBasePath, name);
final File destFile = new File(mBasePath, info.build());
file.renameTo(destFile);
}
- } else if (info.endMillis < deleteBefore) {
+ } else if (info.endMillis <= deleteBefore) {
// found rotated file; delete if old enough
+ if (LOGD) Slog.d(TAG, "deleting " + name);
+
final File file = new File(mBasePath, name);
file.delete();
}
diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java
new file mode 100644
index 0000000..3dd2284
--- /dev/null
+++ b/core/java/com/android/internal/util/IndentingPrintWriter.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 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.util;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Lightweight wrapper around {@link PrintWriter} that automatically indents
+ * newlines based on internal state. Delays writing indent until first actual
+ * write on a newline, enabling indent modification after newline.
+ */
+public class IndentingPrintWriter extends PrintWriter {
+ private final String mIndent;
+
+ private StringBuilder mBuilder = new StringBuilder();
+ private String mCurrent = new String();
+ private boolean mEmptyLine = true;
+
+ public IndentingPrintWriter(Writer writer, String indent) {
+ super(writer);
+ mIndent = indent;
+ }
+
+ public void increaseIndent() {
+ mBuilder.append(mIndent);
+ mCurrent = mBuilder.toString();
+ }
+
+ public void decreaseIndent() {
+ mBuilder.delete(0, mIndent.length());
+ mCurrent = mBuilder.toString();
+ }
+
+ @Override
+ public void println() {
+ super.println();
+ mEmptyLine = true;
+ }
+
+ @Override
+ public void write(char[] buf, int offset, int count) {
+ if (mEmptyLine) {
+ mEmptyLine = false;
+ super.print(mCurrent);
+ }
+ super.write(buf, offset, count);
+ }
+}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index a235d9a..9024d8d 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -387,9 +387,9 @@ public class InputConnectionWrapper implements InputConnection {
}
}
- public boolean deleteSurroundingText(int leftLength, int rightLength) {
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
try {
- mIInputContext.deleteSurroundingText(leftLength, rightLength);
+ mIInputContext.deleteSurroundingText(beforeLength, afterLength);
return true;
} catch (RemoteException e) {
return false;
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 3e9ab86..c8b725a 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -776,9 +776,6 @@ public:
static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
- // TODO: need to suppress this code after the GL renderer is modified for not
- // copying the paint
-
// Beware: this needs Glyph encoding (already done on the Paint constructor)
canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint);
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 9f3238a..9bcfa5f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -466,7 +466,8 @@ public:
jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL);
TextLayoutCacheValue value(contextCount);
- TextLayoutEngine::getInstance().computeValues(&value, paint, text, start, count, contextCount, flags);
+ TextLayoutEngine::getInstance().computeValues(&value, paint, text, start, count,
+ contextCount, flags);
const jchar* shapedGlyphs = value.getGlyphs();
size_t glyphsCount = value.getGlyphsCount();
memcpy(glyphsArray, shapedGlyphs, sizeof(jchar) * glyphsCount);
@@ -673,13 +674,25 @@ public:
}
}
- static int breakText(JNIEnv* env, const SkPaint& paint, const jchar text[],
+ static int breakText(JNIEnv* env, SkPaint& paint, const jchar text[],
int count, float maxWidth, jfloatArray jmeasured,
SkPaint::TextBufferDirection tbd) {
- SkASSERT(paint.getTextEncoding() == SkPaint::kUTF16_TextEncoding);
+ sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+ value = TextLayoutCache::getInstance().getValue(&paint, text, 0, count,
+ count, paint.getFlags());
+ if (value == NULL) {
+ ALOGE("Cannot get TextLayoutCache value for text = '%s'",
+ String8(text, count).string());
+ }
+#else
+ value = new TextLayoutCacheValue(count);
+ TextLayoutEngine::getInstance().computeValues(value.get(), &paint,
+ reinterpret_cast<const UChar*>(text), 0, count, count, paint.getFlags());
+#endif
SkScalar measured;
- size_t bytes = paint.breakText(text, count << 1,
+ size_t bytes = paint.breakText(value->getGlyphs(), value->getGlyphsCount() << 1,
SkFloatToScalar(maxWidth), &measured, tbd);
SkASSERT((bytes & 1) == 0);
@@ -743,7 +756,20 @@ public:
SkRect r;
SkIRect ir;
- paint.measureText(text, count << 1, &r);
+ sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+ value = TextLayoutCache::getInstance().getValue(&paint, text, 0, count,
+ count, paint.getFlags());
+ if (value == NULL) {
+ ALOGE("Cannot get TextLayoutCache value for text = '%s'",
+ String8(text, count).string());
+ }
+#else
+ value = new TextLayoutCacheValue(count);
+ TextLayoutEngine::getInstance().computeValues(value.get(), &paint,
+ reinterpret_cast<const UChar*>(text), 0, count, count, paint.getFlags());
+#endif
+ paint.measureText(value->getGlyphs(), value->getGlyphsCount() << 1, &r);
r.roundOut(&ir);
GraphicsJNI::irect_to_jrect(ir, env, bounds);
}
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index d26f563..71c283a 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -93,7 +93,7 @@ void TextLayoutCache::clear() {
/*
* Caching
*/
-sp<TextLayoutCacheValue> TextLayoutCache::getValue(SkPaint* paint,
+sp<TextLayoutCacheValue> TextLayoutCache::getValue(const SkPaint* paint,
const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
AutoMutex _l(mLock);
nsecs_t startTime = 0;
@@ -360,7 +360,7 @@ TextLayoutEngine::~TextLayoutEngine() {
// we don't bother at the moment
}
-void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
+void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags) {
computeValues(paint, chars, start, count, contextCount, dirFlags,
@@ -371,7 +371,7 @@ void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint
#endif
}
-void TextLayoutEngine::computeValues(SkPaint* paint, const UChar* chars,
+void TextLayoutEngine::computeValues(const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs) {
@@ -513,7 +513,7 @@ static void logGlyphs(HB_ShaperItem shaperItem) {
}
}
-void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars,
+void TextLayoutEngine::computeRunValues(const SkPaint* paint, const UChar* chars,
size_t count, bool isRTL,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs) {
@@ -719,7 +719,7 @@ void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars,
}
-size_t TextLayoutEngine::shapeFontRun(SkPaint* paint, bool isRTL) {
+size_t TextLayoutEngine::shapeFontRun(const SkPaint* paint, bool isRTL) {
// Reset kerning
mShaperItem.kerning_applied = false;
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 510aa18..956e8ca 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -179,7 +179,7 @@ public:
*/
void operator()(TextLayoutCacheKey& text, sp<TextLayoutCacheValue>& desc);
- sp<TextLayoutCacheValue> getValue(SkPaint* paint, const jchar* text, jint start, jint count,
+ sp<TextLayoutCacheValue> getValue(const SkPaint* paint, const jchar* text, jint start, jint count,
jint contextCount, jint dirFlags);
/**
@@ -224,7 +224,7 @@ public:
TextLayoutEngine();
virtual ~TextLayoutEngine();
- void computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
+ void computeValues(TextLayoutCacheValue* value, const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags);
private:
@@ -273,14 +273,14 @@ private:
*/
UnicodeString mBuffer;
- size_t shapeFontRun(SkPaint* paint, bool isRTL);
+ size_t shapeFontRun(const SkPaint* paint, bool isRTL);
- void computeValues(SkPaint* paint, const UChar* chars,
+ void computeValues(const SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs);
- void computeRunValues(SkPaint* paint, const UChar* chars,
+ void computeRunValues(const SkPaint* paint, const UChar* chars,
size_t count, bool isRTL,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 064dcac..5811ddd 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -483,6 +483,20 @@ static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject clazz,
}
// ----------------------------------------------------------------------------
+// Draw filters
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_setupPaintFilter(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer, jint clearBits, jint setBits) {
+ renderer->setupPaintFilter(clearBits, setBits);
+}
+
+static void android_view_GLES20Canvas_resetPaintFilter(JNIEnv* env, jobject clazz,
+ OpenGLRenderer* renderer) {
+ renderer->resetPaintFilter();
+}
+
+// ----------------------------------------------------------------------------
// Text
// ----------------------------------------------------------------------------
@@ -870,6 +884,9 @@ static JNINativeMethod gMethods[] = {
{ "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
{ "nSetupShadow", "(IFFFI)V", (void*) android_view_GLES20Canvas_setupShadow },
+ { "nSetupPaintFilter", "(III)V", (void*) android_view_GLES20Canvas_setupPaintFilter },
+ { "nResetPaintFilter", "(I)V", (void*) android_view_GLES20Canvas_resetPaintFilter },
+
{ "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
{ "nDrawText", "(ILjava/lang/String;IIFFII)V",
(void*) android_view_GLES20Canvas_drawText },
diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml
index 95cc562..470416b 100644
--- a/core/res/res/anim/screen_rotate_180_enter.xml
+++ b/core/res/res/anim/screen_rotate_180_enter.xml
@@ -19,16 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale=".9" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:fillEnabled="true" android:fillBefore="true"
+ <rotate android:fromDegrees="180" android:toDegrees="0"
+ android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="160"
- android:duration="300" />
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="160"
- android:duration="300"/>
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set> \ No newline at end of file
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index d3dd4c0..58a1868 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -19,12 +19,10 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <scale android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale="0.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@interpolator/accelerate_cubic"
- android:duration="160" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@interpolator/decelerate_cubic"
- android:duration="160"/>
+ <rotate android:fromDegrees="0" android:toDegrees="-180"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set> \ No newline at end of file
diff --git a/core/res/res/anim/screen_rotate_finish_enter.xml b/core/res/res/anim/screen_rotate_finish_enter.xml
new file mode 100644
index 0000000..849aa66
--- /dev/null
+++ b/core/res/res/anim/screen_rotate_finish_enter.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <scale android:fromXScale="1.0" android:toXScale="1.25"
+ android:fromYScale="1.0" android:toYScale="1.25"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="100%p" android:toXScale="100%"
+ android:fromYScale="100%p" android:toYScale="100%"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/anim/screen_rotate_finish_exit.xml b/core/res/res/anim/screen_rotate_finish_exit.xml
new file mode 100644
index 0000000..7f70dbc
--- /dev/null
+++ b/core/res/res/anim/screen_rotate_finish_exit.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <scale android:fromXScale="1.0" android:toXScale="1.25"
+ android:fromYScale="1.0" android:toYScale="1.25"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <!--
+ <scale android:fromXScale="100%" android:toXScale="100%p"
+ android:fromYScale="100%" android:toYScale="100%p"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_enter.xml b/core/res/res/anim/screen_rotate_minus_90_enter.xml
index 61aa72a..d2aebc9 100644
--- a/core/res/res/anim/screen_rotate_minus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml
@@ -19,8 +19,44 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
+ <!--
+ <scale android:fromXScale="1.0" android:toXScale="0.565"
+ android:fromYScale="1.0" android:toYScale="0.565"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="1.0" android:toXScale="1.777777777"
+ android:fromYScale="1.0" android:toYScale="1.777777777"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="@android:integer/config_longAnimTime"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="100%p" android:toXScale="100%"
+ android:fromYScale="100%p" android:toYScale="100%"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:startOffset="@android:integer/config_longAnimTime"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
+ <!--
+ <scale android:fromXScale="100%p" android:toXScale="100%"
+ android:fromYScale="100%p" android:toYScale="100%"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
<rotate android:fromDegrees="-90" android:toDegrees="0"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_exit.xml b/core/res/res/anim/screen_rotate_minus_90_exit.xml
index 65294f6..c7c38cd 100644
--- a/core/res/res/anim/screen_rotate_minus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml
@@ -19,16 +19,51 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
+ <!--
+ <scale android:fromXScale="1.0" android:toXScale="0.565"
+ android:fromYScale="1.0" android:toYScale="0.565"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="1.0" android:toXScale="1.777777777"
+ android:fromYScale="1.0" android:toYScale="1.777777777"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="@android:integer/config_longAnimTime"
+ android:duration="@android:integer/config_mediumAnimTime"/>
<scale android:fromXScale="100%" android:toXScale="100%p"
android:fromYScale="100%" android:toYScale="100%p"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
+ android:startOffset="@android:integer/config_longAnimTime"
android:duration="@android:integer/config_mediumAnimTime" />
- <rotate android:fromDegrees="0" android:toDegrees="90"
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="@android:integer/config_longAnimTime"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
+ <!--
+ <scale android:fromXScale="100%" android:toXScale="100%p"
+ android:fromYScale="100%" android:toYScale="100%p"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
android:duration="@android:integer/config_mediumAnimTime" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
+ <rotate android:fromDegrees="0" android:toDegrees="90"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_enter.xml b/core/res/res/anim/screen_rotate_plus_90_enter.xml
index 53b0ccd..63d7043 100644
--- a/core/res/res/anim/screen_rotate_plus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml
@@ -19,8 +19,44 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
+ <!--
+ <scale android:fromXScale="1.0" android:toXScale="0.565"
+ android:fromYScale="1.0" android:toYScale="0.565"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="1.0" android:toXScale="1.777777777"
+ android:fromYScale="1.0" android:toYScale="1.777777777"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="75"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="100%p" android:toXScale="100%"
+ android:fromYScale="100%p" android:toYScale="100%"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:startOffset="75"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
+ <!--
+ <scale android:fromXScale="100%p" android:toXScale="100%"
+ android:fromYScale="100%p" android:toYScale="100%"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
<rotate android:fromDegrees="90" android:toDegrees="0"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_exit.xml b/core/res/res/anim/screen_rotate_plus_90_exit.xml
index 63c0b09..ea48c81 100644
--- a/core/res/res/anim/screen_rotate_plus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml
@@ -19,16 +19,51 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
+ <!--
+ <scale android:fromXScale="1.0" android:toXScale="0.565"
+ android:fromYScale="1.0" android:toYScale="0.565"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="1.0" android:toXScale="1.777777777"
+ android:fromYScale="1.0" android:toYScale="1.777777777"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="75"
+ android:duration="@android:integer/config_mediumAnimTime"/>
<scale android:fromXScale="100%" android:toXScale="100%p"
android:fromYScale="100%" android:toYScale="100%p"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
+ android:startOffset="75"
android:duration="@android:integer/config_mediumAnimTime" />
- <rotate android:fromDegrees="0" android:toDegrees="-90"
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="75"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
+ <!--
+ <scale android:fromXScale="100%" android:toXScale="100%p"
+ android:fromYScale="100%" android:toYScale="100%p"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
android:duration="@android:integer/config_mediumAnimTime" />
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ -->
+ <rotate android:fromDegrees="0" android:toDegrees="-90"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_start_enter.xml b/core/res/res/anim/screen_rotate_start_enter.xml
new file mode 100644
index 0000000..e3f48e4
--- /dev/null
+++ b/core/res/res/anim/screen_rotate_start_enter.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <scale android:fromXScale="1.0" android:toXScale="0.8"
+ android:fromYScale="1.0" android:toYScale="0.8"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/screen_rotate_start_exit.xml b/core/res/res/anim/screen_rotate_start_exit.xml
new file mode 100644
index 0000000..e3f48e4
--- /dev/null
+++ b/core/res/res/anim/screen_rotate_start_exit.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <scale android:fromXScale="1.0" android:toXScale="0.8"
+ android:fromYScale="1.0" android:toYScale="0.8"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index c4d08ed..e46d07c 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -154,6 +154,14 @@ above.</p>
<td><code>0489</code></td>
</tr>
<tr>
+ <td>Fujitsu</td>
+ <td><code>04C5</code></td>
+ </tr>
+ <tr>
+ <td>Fujitsu Toshiba</td>
+ <td><code>04C5</code></td>
+ </tr>
+ <tr>
<td>Garmin-Asus</td>
<td><code>091E</code></td>
</tr>
diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
index e3ff215..39ccbf4 100644
--- a/docs/html/guide/topics/graphics/hardware-accel.jd
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -300,6 +300,16 @@ changed.</li>
<li>{@link android.graphics.Paint#setRasterizer setRasterizer()}</li>
</ul>
</li>
+
+ <li>
+ <strong>Xfermodes</strong>
+
+ <ul>
+ <li>{@link android.graphics.AvoidXfermode AvoidXfermode}</li>
+
+ <li>{@link android.graphics.PixelXorXfermode PixelXorXfermode}</li>
+ </ul>
+ </li>
</ul>
<p>In addition, some operations behave differently with hardware acceleration enabled:</p>
@@ -315,9 +325,6 @@ changed.</li>
<li>{@link android.graphics.Canvas#drawBitmapMesh drawBitmapMesh()}: colors array is
ignored</li>
-
- <li>{@link android.graphics.Canvas#setDrawFilter setDrawFilter()}: can be set, but is
- ignored</li>
</ul>
</li>
@@ -336,6 +343,24 @@ changed.</li>
</li>
<li>
+ <strong>PorterDuffXfermode</strong>
+
+ <ul>
+ <li>{@link android.graphics.PorterDuff.Mode#DARKEN PorterDuff.Mode.DARKEN} will
+ be equivalent to {@link android.graphics.PorterDuff.Mode#SRC_OVER} when blending
+ against the framebuffer.</li>
+
+ <li>{@link android.graphics.PorterDuff.Mode#LIGHTEN PorterDuff.Mode.LIGHTEN} will
+ be equivalent to {@link android.graphics.PorterDuff.Mode#SRC_OVER} when blending
+ against the framebuffer.</li>
+
+ <li>{@link android.graphics.PorterDuff.Mode#OVERLAY PorterDuff.Mode.OVERLAY} will
+ be equivalent to {@link android.graphics.PorterDuff.Mode#SRC_OVER} when blending
+ against the framebuffer.</li>
+ </ul>
+ </li>
+
+ <li>
<strong>ComposeShader</strong>
<ul>
diff --git a/graphics/java/android/graphics/AvoidXfermode.java b/graphics/java/android/graphics/AvoidXfermode.java
index 7e2722d..5a59e36 100644
--- a/graphics/java/android/graphics/AvoidXfermode.java
+++ b/graphics/java/android/graphics/AvoidXfermode.java
@@ -20,6 +20,7 @@ package android.graphics;
* AvoidXfermode xfermode will draw the src everywhere except on top of the
* opColor or, depending on the Mode, draw only on top of the opColor.
*/
+@Deprecated
public class AvoidXfermode extends Xfermode {
// these need to match the enum in SkAvoidXfermode.h on the native side
diff --git a/graphics/java/android/graphics/PaintFlagsDrawFilter.java b/graphics/java/android/graphics/PaintFlagsDrawFilter.java
index 0f0d03d..c833a12 100644
--- a/graphics/java/android/graphics/PaintFlagsDrawFilter.java
+++ b/graphics/java/android/graphics/PaintFlagsDrawFilter.java
@@ -17,6 +17,10 @@
package android.graphics;
public class PaintFlagsDrawFilter extends DrawFilter {
+ /** @hide **/
+ public final int clearBits;
+ /** @hide **/
+ public final int setBits;
/**
* Subclass of DrawFilter that affects every paint by first clearing
@@ -27,6 +31,8 @@ public class PaintFlagsDrawFilter extends DrawFilter {
* @param setBits These bits will be set in the paint's flags
*/
public PaintFlagsDrawFilter(int clearBits, int setBits) {
+ this.clearBits = clearBits;
+ this.setBits = setBits;
// our native constructor can return 0, if the specified bits
// are effectively a no-op
mNativeInt = nativeConstructor(clearBits, setBits);
diff --git a/graphics/java/android/graphics/PixelXorXfermode.java b/graphics/java/android/graphics/PixelXorXfermode.java
index 18d15cf..6075ec3 100644
--- a/graphics/java/android/graphics/PixelXorXfermode.java
+++ b/graphics/java/android/graphics/PixelXorXfermode.java
@@ -22,6 +22,7 @@ package android.graphics;
* this mode *always* returns an opaque color (alpha == 255). Thus it is
* not really usefull for operating on blended colors.
*/
+@Deprecated
public class PixelXorXfermode extends Xfermode {
public PixelXorXfermode(int opColor) {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 824ab4f..ee935e4 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -67,6 +67,8 @@ const char* DisplayList::OP_NAMES[] = {
"SetupColorFilter",
"ResetShadow",
"SetupShadow",
+ "ResetPaintFilter",
+ "SetupPaintFilter",
"DrawGLFunction"
};
@@ -249,7 +251,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float f2 = getFloat();
float f3 = getFloat();
float f4 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
int flags = getInt();
ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent,
OP_NAMES[op], f1, f2, f3, f4, paint, flags);
@@ -322,7 +324,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
Layer* layer = (Layer*) getInt();
float x = getFloat();
float y = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
layer, x, y, paint);
}
@@ -331,7 +333,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
SkBitmap* bitmap = getBitmap();
float x = getFloat();
float y = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
bitmap, x, y, paint);
}
@@ -339,7 +341,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
case DrawBitmapMatrix: {
SkBitmap* bitmap = getBitmap();
SkMatrix* matrix = getMatrix();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op],
bitmap, matrix, paint);
}
@@ -354,7 +356,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float f6 = getFloat();
float f7 = getFloat();
float f8 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint);
}
@@ -368,7 +370,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float* vertices = getFloats(verticesCount);
bool hasColors = getInt();
int* colors = hasColors ? getInts(colorsCount) : NULL;
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
}
break;
@@ -387,7 +389,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float top = getFloat();
float right = getFloat();
float bottom = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %.2f, %.2f, %.2f, %.2f", (char*) indent, OP_NAMES[op],
left, top, right, bottom);
}
@@ -403,7 +405,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float f2 = getFloat();
float f3 = getFloat();
float f4 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
f1, f2, f3, f4, paint);
}
@@ -415,7 +417,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float f4 = getFloat();
float f5 = getFloat();
float f6 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint);
}
@@ -424,7 +426,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float f1 = getFloat();
float f2 = getFloat();
float f3 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, paint);
}
@@ -434,7 +436,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float f2 = getFloat();
float f3 = getFloat();
float f4 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint);
}
@@ -447,28 +449,28 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float f5 = getFloat();
float f6 = getFloat();
int i1 = getInt();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint);
}
break;
case DrawPath: {
SkPath* path = getPath();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint);
}
break;
case DrawLines: {
int count = 0;
float* points = getFloats(count);
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
}
break;
case DrawPoints: {
int count = 0;
float* points = getFloats(count);
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
}
break;
@@ -477,7 +479,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
int count = getInt();
float x = getFloat();
float y = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
float length = getFloat();
ALOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent, OP_NAMES[op],
text.text(), text.length(), count, x, y, paint, length);
@@ -488,7 +490,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
int count = getInt();
int positionsCount = 0;
float* positions = getFloats(positionsCount);
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
text.text(), text.length(), count, paint);
}
@@ -523,6 +525,16 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
radius, dx, dy, color);
}
break;
+ case ResetPaintFilter: {
+ ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ }
+ break;
+ case SetupPaintFilter: {
+ int clearBits = getInt();
+ int setBits = getInt();
+ ALOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op], clearBits, setBits);
+ }
+ break;
default:
ALOGD("Display List error: op not handled: %s%s",
(char*) indent, OP_NAMES[op]);
@@ -588,7 +600,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float f2 = getFloat();
float f3 = getFloat();
float f4 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
int flags = getInt();
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent,
OP_NAMES[op], f1, f2, f3, f4, paint, flags);
@@ -671,7 +683,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
Layer* layer = (Layer*) getInt();
float x = getFloat();
float y = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
layer, x, y, paint);
renderer.drawLayer(layer, x, y, paint);
@@ -681,7 +693,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
SkBitmap* bitmap = getBitmap();
float x = getFloat();
float y = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
bitmap, x, y, paint);
renderer.drawBitmap(bitmap, x, y, paint);
@@ -690,7 +702,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
case DrawBitmapMatrix: {
SkBitmap* bitmap = getBitmap();
SkMatrix* matrix = getMatrix();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op],
bitmap, matrix, paint);
renderer.drawBitmap(bitmap, matrix, paint);
@@ -706,7 +718,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float f6 = getFloat();
float f7 = getFloat();
float f8 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint);
renderer.drawBitmap(bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint);
@@ -722,7 +734,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float* vertices = getFloats(verticesCount);
bool hasColors = getInt();
int* colors = hasColors ? getInts(colorsCount) : NULL;
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, paint);
@@ -746,7 +758,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float top = getFloat();
float right = getFloat();
float bottom = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount,
@@ -765,7 +777,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float f2 = getFloat();
float f3 = getFloat();
float f4 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
f1, f2, f3, f4, paint);
renderer.drawRect(f1, f2, f3, f4, paint);
@@ -778,7 +790,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float f4 = getFloat();
float f5 = getFloat();
float f6 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint);
renderer.drawRoundRect(f1, f2, f3, f4, f5, f6, paint);
@@ -788,7 +800,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float f1 = getFloat();
float f2 = getFloat();
float f3 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, paint);
renderer.drawCircle(f1, f2, f3, paint);
@@ -799,7 +811,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float f2 = getFloat();
float f3 = getFloat();
float f4 = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint);
renderer.drawOval(f1, f2, f3, f4, paint);
@@ -813,7 +825,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float f5 = getFloat();
float f6 = getFloat();
int i1 = getInt();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p",
(char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint);
renderer.drawArc(f1, f2, f3, f4, f5, f6, i1 == 1, paint);
@@ -821,7 +833,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
break;
case DrawPath: {
SkPath* path = getPath();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint);
renderer.drawPath(path, paint);
}
@@ -829,7 +841,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
case DrawLines: {
int count = 0;
float* points = getFloats(count);
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
renderer.drawLines(points, count, paint);
}
@@ -837,7 +849,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
case DrawPoints: {
int count = 0;
float* points = getFloats(count);
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
renderer.drawPoints(points, count, paint);
}
@@ -847,7 +859,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
int count = getInt();
float x = getFloat();
float y = getFloat();
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
float length = getFloat();
DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent,
OP_NAMES[op], text.text(), text.length(), count, x, y, paint, length);
@@ -859,7 +871,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
int count = getInt();
int positionsCount = 0;
float* positions = getFloats(positionsCount);
- SkPaint* paint = getPaint();
+ SkPaint* paint = getPaint(renderer);
DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent,
OP_NAMES[op], text.text(), text.length(), count, paint);
renderer.drawPosText(text.text(), text.length(), count, positions, paint);
@@ -902,6 +914,19 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
renderer.setupShadow(radius, dx, dy, color);
}
break;
+ case ResetPaintFilter: {
+ DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+ renderer.resetPaintFilter();
+ }
+ break;
+ case SetupPaintFilter: {
+ int clearBits = getInt();
+ int setBits = getInt();
+ DISPLAY_LIST_LOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op],
+ clearBits, setBits);
+ renderer.setupPaintFilter(clearBits, setBits);
+ }
+ break;
default:
DISPLAY_LIST_LOGD("Display List error: op not handled: %s%s",
(char*) indent, OP_NAMES[op]);
@@ -1277,5 +1302,15 @@ void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int colo
addInt(color);
}
+void DisplayListRenderer::resetPaintFilter() {
+ addOp(DisplayList::ResetPaintFilter);
+}
+
+void DisplayListRenderer::setupPaintFilter(int clearBits, int setBits) {
+ addOp(DisplayList::SetupPaintFilter);
+ addInt(clearBits);
+ addInt(setBits);
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 422184e..6fe4205 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -103,6 +103,8 @@ public:
SetupColorFilter,
ResetShadow,
SetupShadow,
+ ResetPaintFilter,
+ SetupPaintFilter,
DrawGLFunction,
};
@@ -177,8 +179,8 @@ private:
return (SkPath*) getInt();
}
- SkPaint* getPaint() {
- return (SkPaint*) getInt();
+ SkPaint* getPaint(OpenGLRenderer& renderer) {
+ return renderer.filterPaint((SkPaint*) getInt());
}
DisplayList* getDisplayList() {
@@ -304,6 +306,9 @@ public:
virtual void resetShadow();
virtual void setupShadow(float radius, float dx, float dy, int color);
+ virtual void resetPaintFilter();
+ virtual void setupPaintFilter(int clearBits, int setBits);
+
ANDROID_API void reset();
const SkWriter32& writeStream() const {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 786a4f1..cc0e05e 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -111,6 +111,7 @@ OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
mShader = NULL;
mColorFilter = NULL;
mHasShadow = false;
+ mHasDrawFilter = false;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
@@ -2399,6 +2400,31 @@ void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
}
///////////////////////////////////////////////////////////////////////////////
+// Draw filters
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetPaintFilter() {
+ mHasDrawFilter = false;
+}
+
+void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) {
+ mHasDrawFilter = true;
+ mPaintFilterClearBits = clearBits & SkPaint::kAllFlags;
+ mPaintFilterSetBits = setBits & SkPaint::kAllFlags;
+}
+
+SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
+ if (!mHasDrawFilter || !paint) return paint;
+
+ uint32_t flags = paint->getFlags();
+
+ mFilteredPaint = *paint;
+ mFilteredPaint.setFlags((flags & ~mPaintFilterClearBits) | mPaintFilterSetBits);
+
+ return &mFilteredPaint;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index a9cda47..ae355bb 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -136,6 +136,11 @@ public:
virtual void resetShadow();
virtual void setupShadow(float radius, float dx, float dy, int color);
+ virtual void resetPaintFilter();
+ virtual void setupPaintFilter(int clearBits, int setBits);
+
+ SkPaint* filterPaint(SkPaint* paint);
+
protected:
/**
* Compose the layer defined in the current snapshot with the layer
@@ -581,6 +586,12 @@ private:
float mShadowDy;
int mShadowColor;
+ // Draw filters
+ bool mHasDrawFilter;
+ int mPaintFilterClearBits;
+ int mPaintFilterSetBits;
+ SkPaint mFilteredPaint;
+
// Various caches
Caches& mCaches;
diff --git a/media/libstagefright/codecs/aacenc/src/adj_thr.c b/media/libstagefright/codecs/aacenc/src/adj_thr.c
index c656f65..a8ab809 100644
--- a/media/libstagefright/codecs/aacenc/src/adj_thr.c
+++ b/media/libstagefright/codecs/aacenc/src/adj_thr.c
@@ -1039,7 +1039,7 @@ void AdjThrInit(ADJ_THR_STATE *hAdjThr,
/* minSnr adaptation */
/* maximum reduction of minSnr goes down to minSnr^maxRed */
- msaParam->maxRed = 0x20000000; /* *0.25f /
+ msaParam->maxRed = 0x20000000; /* *0.25f */
/* start adaptation of minSnr for avgEn/sfbEn > startRatio */
msaParam->startRatio = 0x0ccccccd; /* 10 */
/* maximum minSnr reduction to minSnr^maxRed is reached for
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 69a247d..d33ed3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -23,6 +23,8 @@ import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Message;
import android.os.ServiceManager;
import android.util.AttributeSet;
import android.util.Slog;
@@ -66,6 +68,35 @@ public class NavigationBarView extends LinearLayout {
int mDisabledFlags = 0;
int mNavigationIconHints = 0;
+ // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
+ final static boolean WORKAROUND_INVALID_LAYOUT = true;
+ final static int MSG_CHECK_INVALID_LAYOUT = 8686;
+
+ private class H extends Handler {
+ public void handleMessage(Message m) {
+ switch (m.what) {
+ case MSG_CHECK_INVALID_LAYOUT:
+ final String how = "" + m.obj;
+ final int w = getWidth();
+ final int h = getHeight();
+ final int vw = mCurrentView.getWidth();
+ final int vh = mCurrentView.getHeight();
+
+ if (h != vh || w != vw) {
+ Slog.w(TAG, String.format(
+ "*** Invalid layout in navigation bar (%s this=%dx%d cur=%dx%d)",
+ how, w, h, vw, vh));
+ if (WORKAROUND_INVALID_LAYOUT) {
+ requestLayout();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private H mHandler = new H();
+
public View getRecentsButton() {
return mCurrentView.findViewById(R.id.recent_apps);
}
@@ -275,6 +306,36 @@ public class NavigationBarView extends LinearLayout {
}
}
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ if (DEBUG) Slog.d(TAG, String.format(
+ "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
+ postCheckForInvalidLayout("sizeChanged");
+ super.onSizeChanged(w, h, oldw, oldh);
+ }
+
+ /*
+ @Override
+ protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+ if (DEBUG) Slog.d(TAG, String.format(
+ "onLayout: %s (%d,%d,%d,%d)",
+ changed?"changed":"notchanged", left, top, right, bottom));
+ super.onLayout(changed, left, top, right, bottom);
+ }
+
+ // uncomment this for extra defensiveness in WORKAROUND_INVALID_LAYOUT situations: if all else
+ // fails, any touch on the display will fix the layout.
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (DEBUG) Slog.d(TAG, "onInterceptTouchEvent: " + ev.toString());
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ postCheckForInvalidLayout("touch");
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+ */
+
+
private String getResourceName(int resId) {
if (resId != 0) {
final android.content.res.Resources res = mContext.getResources();
@@ -288,6 +349,10 @@ public class NavigationBarView extends LinearLayout {
}
}
+ private void postCheckForInvalidLayout(final String how) {
+ mHandler.obtainMessage(MSG_CHECK_INVALID_LAYOUT, 0, 0, how).sendToTarget();
+ }
+
private static String visibilityToString(int vis) {
switch (vis) {
case View.INVISIBLE:
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 8e062b7..46c0f83 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -5,7 +5,7 @@
<application android:label="VpnDialogs"
android:allowBackup="false" >
<activity android:name=".ConfirmDialog"
- android:theme="@style/transparent">
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -13,7 +13,7 @@
</activity>
<activity android:name=".ManageDialog"
- android:theme="@style/transparent"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/packages/VpnDialogs/res/values/styles.xml b/packages/VpnDialogs/res/values/styles.xml
index cf10596..e3469ec 100644
--- a/packages/VpnDialogs/res/values/styles.xml
+++ b/packages/VpnDialogs/res/values/styles.xml
@@ -15,13 +15,6 @@
-->
<resources>
-
- <style name="transparent">
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:windowIsFloating">true</item>
- </style>
-
<style name="label">
<item name="android:gravity">center_vertical|right</item>
<item name="android:paddingRight">2mm</item>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index c7b4a5f..13d8019 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -16,8 +16,6 @@
package com.android.vpndialogs;
-import android.app.Activity;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -32,15 +30,16 @@ import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
-public class ConfirmDialog extends Activity implements CompoundButton.OnCheckedChangeListener,
- DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+import com.android.internal.app.AlertActivity;
+
+public class ConfirmDialog extends AlertActivity implements
+ CompoundButton.OnCheckedChangeListener, DialogInterface.OnClickListener {
private static final String TAG = "VpnConfirm";
private String mPackage;
private IConnectivityManager mService;
- private AlertDialog mDialog;
private Button mButton;
@Override
@@ -67,18 +66,17 @@ public class ConfirmDialog extends Activity implements CompoundButton.OnCheckedC
getString(R.string.prompt, app.loadLabel(pm)));
((CompoundButton) view.findViewById(R.id.check)).setOnCheckedChangeListener(this);
- mDialog = new AlertDialog.Builder(this)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setTitle(android.R.string.dialog_alert_title)
- .setView(view)
- .setPositiveButton(android.R.string.ok, this)
- .setNegativeButton(android.R.string.cancel, this)
- .setCancelable(false)
- .create();
- mDialog.setOnDismissListener(this);
- mDialog.show();
-
- mButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ mAlertParams.mIconId = android.R.drawable.ic_dialog_alert;
+ mAlertParams.mTitle = getText(android.R.string.dialog_alert_title);
+ mAlertParams.mPositiveButtonText = getText(android.R.string.ok);
+ mAlertParams.mPositiveButtonListener = this;
+ mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
+ mAlertParams.mNegativeButtonListener = this;
+ mAlertParams.mView = view;
+ setupAlert();
+
+ getWindow().setCloseOnTouchOutside(false);
+ mButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mButton.setEnabled(false);
} catch (Exception e) {
Log.e(TAG, "onResume", e);
@@ -87,12 +85,7 @@ public class ConfirmDialog extends Activity implements CompoundButton.OnCheckedC
}
@Override
- protected void onPause() {
- super.onPause();
- if (mDialog != null) {
- mDialog.setOnDismissListener(null);
- mDialog.dismiss();
- }
+ public void onBackPressed() {
}
@Override
@@ -103,16 +96,11 @@ public class ConfirmDialog extends Activity implements CompoundButton.OnCheckedC
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (which == AlertDialog.BUTTON_POSITIVE && mService.prepareVpn(null, mPackage)) {
+ if (which == DialogInterface.BUTTON_POSITIVE && mService.prepareVpn(null, mPackage)) {
setResult(RESULT_OK);
}
} catch (Exception e) {
Log.e(TAG, "onClick", e);
}
}
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- finish();
- }
}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 7fb1417..2de0251 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,8 +16,6 @@
package com.android.vpndialogs;
-import android.app.Activity;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -35,20 +33,20 @@ import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.app.AlertActivity;
import com.android.internal.net.VpnConfig;
import java.io.DataInputStream;
import java.io.FileInputStream;
-public class ManageDialog extends Activity implements Handler.Callback,
- DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+public class ManageDialog extends AlertActivity implements
+ DialogInterface.OnClickListener, Handler.Callback {
private static final String TAG = "VpnManage";
private VpnConfig mConfig;
private IConnectivityManager mService;
- private AlertDialog mDialog;
private TextView mDuration;
private TextView mDataTransmitted;
private TextView mDataReceived;
@@ -80,31 +78,24 @@ public class ManageDialog extends Activity implements Handler.Callback,
mDataReceived = (TextView) view.findViewById(R.id.data_received);
if (mConfig.user.equals(VpnConfig.LEGACY_VPN)) {
- mDialog = new AlertDialog.Builder(this)
- .setIcon(android.R.drawable.ic_dialog_info)
- .setTitle(R.string.legacy_title)
- .setView(view)
- .setNeutralButton(R.string.disconnect, this)
- .setNegativeButton(android.R.string.cancel, this)
- .create();
+ mAlertParams.mIconId = android.R.drawable.ic_dialog_info;
+ mAlertParams.mTitle = getText(R.string.legacy_title);
} else {
PackageManager pm = getPackageManager();
ApplicationInfo app = pm.getApplicationInfo(mConfig.user, 0);
- mDialog = new AlertDialog.Builder(this)
- .setIcon(app.loadIcon(pm))
- .setTitle(app.loadLabel(pm))
- .setView(view)
- .setNeutralButton(R.string.disconnect, this)
- .setNegativeButton(android.R.string.cancel, this)
- .create();
+ mAlertParams.mIcon = app.loadIcon(pm);
+ mAlertParams.mTitle = app.loadLabel(pm);
}
-
if (mConfig.configureIntent != null) {
- mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
- getText(R.string.configure), this);
+ mAlertParams.mPositiveButtonText = getText(R.string.configure);
+ mAlertParams.mPositiveButtonListener = this;
}
- mDialog.setOnDismissListener(this);
- mDialog.show();
+ mAlertParams.mNeutralButtonText = getText(R.string.disconnect);
+ mAlertParams.mNeutralButtonListener = this;
+ mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
+ mAlertParams.mNegativeButtonListener = this;
+ mAlertParams.mView = view;
+ setupAlert();
if (mHandler == null) {
mHandler = new Handler(this);
@@ -119,18 +110,17 @@ public class ManageDialog extends Activity implements Handler.Callback,
@Override
protected void onPause() {
super.onPause();
- if (mDialog != null) {
- mDialog.setOnDismissListener(null);
- mDialog.dismiss();
+ if (!isFinishing()) {
+ finish();
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (which == AlertDialog.BUTTON_POSITIVE) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
mConfig.configureIntent.send();
- } else if (which == AlertDialog.BUTTON_NEUTRAL) {
+ } else if (which == DialogInterface.BUTTON_NEUTRAL) {
mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN);
}
} catch (Exception e) {
@@ -140,15 +130,10 @@ public class ManageDialog extends Activity implements Handler.Callback,
}
@Override
- public void onDismiss(DialogInterface dialog) {
- finish();
- }
-
- @Override
public boolean handleMessage(Message message) {
mHandler.removeMessages(0);
- if (mDialog.isShowing()) {
+ if (!isFinishing()) {
if (mConfig.startTime != 0) {
long seconds = (SystemClock.elapsedRealtime() - mConfig.startTime) / 1000;
mDuration.setText(String.format("%02d:%02d:%02d",
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 43a34d0..2d6e4f8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2044,6 +2044,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mTmpNavigationFrame.offset(mNavigationBarWidth, 0);
}
}
+ // Make sure the content and current rectangles are updated to
+ // account for the restrictions from the navigation bar.
+ mContentTop = mCurTop = mDockTop;
+ mContentBottom = mCurBottom = mDockBottom;
+ mContentLeft = mCurLeft = mDockLeft;
+ mContentRight = mCurRight = mDockRight;
// And compute the final frame.
mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
mTmpNavigationFrame, mTmpNavigationFrame);
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 4dad209..0bcec2e 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -142,5 +142,5 @@ option java_package com.android.server
# ---------------------------
# NetworkStatsService.java
# ---------------------------
-51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3),(dev_history_start|2|3)
-51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3),(dev_history_start|2|3)
+51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
+51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3)
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 774495a..bb0ac3e 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -51,7 +51,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.WorkSource;
-import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
@@ -60,6 +59,7 @@ import android.view.WindowManagerPolicy;
import static android.provider.Settings.System.DIM_SCREEN;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE;
+import static android.provider.Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ;
import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import static android.provider.Settings.System.STAY_ON_WHILE_PLUGGED_IN;
@@ -106,6 +106,14 @@ public class PowerManagerService extends IPowerManager.Stub
// light sensor events rate in microseconds
private static final int LIGHT_SENSOR_RATE = 1000000;
+ // Expansion of range of light values when applying scale from light
+ // sensor brightness setting, in the [0..255] brightness range.
+ private static final int LIGHT_SENSOR_RANGE_EXPANSION = 20;
+
+ // Scaling factor of the light sensor brightness setting when applying
+ // it to the final brightness.
+ private static final int LIGHT_SENSOR_OFFSET_SCALE = 8;
+
// For debouncing the proximity sensor in milliseconds
private static final int PROXIMITY_SENSOR_DELAY = 1000;
@@ -118,6 +126,9 @@ public class PowerManagerService extends IPowerManager.Stub
// Default timeout for screen off, if not found in settings database = 15 seconds.
private static final int DEFAULT_SCREEN_OFF_TIMEOUT = 15000;
+ // Screen brightness should always have a value, but just in case...
+ private static final int DEFAULT_SCREEN_BRIGHTNESS = 192;
+
// flags for setPowerState
private static final int SCREEN_ON_BIT = 0x00000001;
private static final int SCREEN_BRIGHT_BIT = 0x00000002;
@@ -150,6 +161,8 @@ public class PowerManagerService extends IPowerManager.Stub
static final int ANIM_STEPS = 60/4;
// Slower animation for autobrightness changes
static final int AUTOBRIGHTNESS_ANIM_STEPS = 60;
+ // Number of steps when performing a more immediate brightness change.
+ static final int IMMEDIATE_ANIM_STEPS = 4;
// These magic numbers are the initial state of the LEDs at boot. Ideally
// we should read them from the driver, but our current hardware returns 0
@@ -227,6 +240,7 @@ public class PowerManagerService extends IPowerManager.Stub
private boolean mLightSensorPendingDecrease = false;
private boolean mLightSensorPendingIncrease = false;
private float mLightSensorPendingValue = -1;
+ private float mLightSensorAdjustSetting = 0;
private int mLightSensorScreenBrightness = -1;
private int mLightSensorButtonBrightness = -1;
private int mLightSensorKeyboardBrightness = -1;
@@ -240,6 +254,7 @@ public class PowerManagerService extends IPowerManager.Stub
// mLastScreenOnTime is the time the screen was last turned on
private long mLastScreenOnTime;
private boolean mPreventScreenOn;
+ private int mScreenBrightnessSetting = DEFAULT_SCREEN_BRIGHTNESS;
private int mScreenBrightnessOverride = -1;
private int mButtonBrightnessOverride = -1;
private int mScreenBrightnessDim;
@@ -460,6 +475,9 @@ public class PowerManagerService extends IPowerManager.Stub
// DIM_SCREEN
//mDimScreen = getInt(DIM_SCREEN) != 0;
+ mScreenBrightnessSetting = getInt(SCREEN_BRIGHTNESS, DEFAULT_SCREEN_BRIGHTNESS);
+ mLightSensorAdjustSetting = getFloat(SCREEN_AUTO_BRIGHTNESS_ADJ, 0);
+
// SCREEN_BRIGHTNESS_MODE, default to manual
setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL));
@@ -624,9 +642,12 @@ public class PowerManagerService extends IPowerManager.Stub
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?) or ("
+ + Settings.System.NAME + "=?) or ("
+ Settings.System.NAME + "=?)",
- new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN,
- SCREEN_BRIGHTNESS_MODE, WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
+ new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN, SCREEN_BRIGHTNESS,
+ SCREEN_BRIGHTNESS_MODE, SCREEN_AUTO_BRIGHTNESS_ADJ,
+ WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE},
null);
mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler);
SettingsObserver settingsObserver = new SettingsObserver();
@@ -1163,7 +1184,8 @@ public class PowerManagerService extends IPowerManager.Stub
pw.println(" mProximitySensorActive=" + mProximitySensorActive);
pw.println(" mProximityPendingValue=" + mProximityPendingValue);
pw.println(" mLastProximityEventTime=" + mLastProximityEventTime);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled
+ + " mLightSensorAdjustSetting=" + mLightSensorAdjustSetting);
pw.println(" mLightSensorValue=" + mLightSensorValue
+ " mLightSensorPendingValue=" + mLightSensorPendingValue);
pw.println(" mLightSensorPendingDecrease=" + mLightSensorPendingDecrease
@@ -2230,20 +2252,15 @@ public class PowerManagerService extends IPowerManager.Stub
}
private int getPreferredBrightness() {
- try {
- if (mScreenBrightnessOverride >= 0) {
- return mScreenBrightnessOverride;
- } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness
- && mAutoBrightessEnabled) {
- return mLightSensorScreenBrightness;
- }
- final int brightness = Settings.System.getInt(mContext.getContentResolver(),
- SCREEN_BRIGHTNESS);
- // Don't let applications turn the screen all the way off
- return Math.max(brightness, mScreenBrightnessDim);
- } catch (SettingNotFoundException snfe) {
- return Power.BRIGHTNESS_ON;
+ if (mScreenBrightnessOverride >= 0) {
+ return mScreenBrightnessOverride;
+ } else if (mLightSensorScreenBrightness >= 0 && mUseSoftwareAutoBrightness
+ && mAutoBrightessEnabled) {
+ return mLightSensorScreenBrightness;
}
+ final int brightness = mScreenBrightnessSetting;
+ // Don't let applications turn the screen all the way off
+ return Math.max(brightness, mScreenBrightnessDim);
}
private int applyButtonState(int state) {
@@ -2439,7 +2456,34 @@ public class PowerManagerService extends IPowerManager.Stub
break;
}
}
- return values[i];
+ // This is the range of brightness values that we can use.
+ final int minval = values[0];
+ final int maxval = values[mAutoBrightnessLevels.length];
+ // This is the range we will be scaling. We put some padding
+ // at the low and high end to give the adjustment a little better
+ // impact on the actual observed value.
+ final int range = (maxval-minval) + LIGHT_SENSOR_RANGE_EXPANSION;
+ // This is the desired brightness value from 0.0 to 1.0.
+ float valf = ((values[i]-minval+(LIGHT_SENSOR_RANGE_EXPANSION/2))/(float)range);
+ // Apply a scaling to the value based on the adjustment.
+ if (mLightSensorAdjustSetting > 0 && mLightSensorAdjustSetting <= 1) {
+ float adj = (float)Math.sqrt(1.0f-mLightSensorAdjustSetting);
+ if (adj <= .00001) {
+ valf = 1;
+ } else {
+ valf /= adj;
+ }
+ } else if (mLightSensorAdjustSetting < 0 && mLightSensorAdjustSetting >= -1) {
+ float adj = (float)Math.sqrt(1.0f+mLightSensorAdjustSetting);
+ valf *= adj;
+ }
+ // Apply an additional offset to the value based on the adjustment.
+ valf += mLightSensorAdjustSetting/LIGHT_SENSOR_OFFSET_SCALE;
+ // Convert the 0.0-1.0 value back to a brightness integer.
+ int val = (int)((valf*range)+minval) - (LIGHT_SENSOR_RANGE_EXPANSION/2);
+ if (val < minval) val = minval;
+ else if (val > maxval) val = maxval;
+ return val;
} catch (Exception e) {
// guard against null pointer or index out of bounds errors
Slog.e(TAG, "getAutoBrightnessValue", e);
@@ -2468,7 +2512,7 @@ public class PowerManagerService extends IPowerManager.Stub
int value = (int)mLightSensorPendingValue;
mLightSensorPendingDecrease = false;
mLightSensorPendingIncrease = false;
- lightSensorChangedLocked(value);
+ lightSensorChangedLocked(value, false);
}
}
}
@@ -2485,12 +2529,12 @@ public class PowerManagerService extends IPowerManager.Stub
// force lights recalculation
int value = (int)mLightSensorValue;
mLightSensorValue = -1;
- lightSensorChangedLocked(value);
+ lightSensorChangedLocked(value, false);
}
}
}
- private void lightSensorChangedLocked(int value) {
+ private void lightSensorChangedLocked(int value, boolean immediate) {
if (mDebugLightSensor) {
Slog.d(TAG, "lightSensorChangedLocked " + value);
}
@@ -2536,7 +2580,8 @@ public class PowerManagerService extends IPowerManager.Stub
if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) {
if (!mSkippedScreenOn) {
- mScreenBrightness.setTargetLocked(lcdValue, AUTOBRIGHTNESS_ANIM_STEPS,
+ mScreenBrightness.setTargetLocked(lcdValue,
+ immediate ? IMMEDIATE_ANIM_STEPS : AUTOBRIGHTNESS_ANIM_STEPS,
INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue);
}
}
@@ -2684,7 +2729,7 @@ public class PowerManagerService extends IPowerManager.Stub
if (mLightSensorValue >= 0) {
int value = (int)mLightSensorValue;
mLightSensorValue = -1;
- lightSensorChangedLocked(value);
+ lightSensorChangedLocked(value, false);
}
}
userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true);
@@ -2944,10 +2989,28 @@ public class PowerManagerService extends IPowerManager.Stub
Binder.restoreCallingIdentity(identity);
}
- // update our animation state
- synchronized (mLocks) {
- mScreenBrightness.targetValue = brightness;
- mScreenBrightness.jumpToTargetLocked();
+ mScreenBrightness.targetValue = brightness;
+ mScreenBrightness.jumpToTargetLocked();
+ }
+ }
+
+ public void setAutoBrightnessAdjustment(float adj) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ synchronized (mLocks) {
+ mLightSensorAdjustSetting = adj;
+ if (mSensorManager != null && mLightSensorEnabled) {
+ // clear calling identity so sensor manager battery stats are accurate
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // force recompute of backlight values
+ if (mLightSensorValue >= 0) {
+ int value = (int)mLightSensorValue;
+ mLightSensorValue = -1;
+ handleLightSensorValue(value, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
}
@@ -3062,7 +3125,7 @@ public class PowerManagerService extends IPowerManager.Stub
if (mLightSensorValue >= 0) {
int value = (int)mLightSensorValue;
mLightSensorValue = -1;
- handleLightSensorValue(value);
+ handleLightSensorValue(value, true);
}
mSensorManager.registerListener(mLightListener, mLightSensor,
LIGHT_SENSOR_RATE);
@@ -3122,7 +3185,7 @@ public class PowerManagerService extends IPowerManager.Stub
}
};
- private void handleLightSensorValue(int value) {
+ private void handleLightSensorValue(int value, boolean immediate) {
long milliseconds = SystemClock.elapsedRealtime();
if (mLightSensorValue == -1 ||
milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
@@ -3130,7 +3193,7 @@ public class PowerManagerService extends IPowerManager.Stub
mHandler.removeCallbacks(mAutoBrightnessTask);
mLightSensorPendingDecrease = false;
mLightSensorPendingIncrease = false;
- lightSensorChangedLocked(value);
+ lightSensorChangedLocked(value, immediate);
} else {
if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
(value < mLightSensorValue && mLightSensorPendingIncrease) ||
@@ -3160,7 +3223,7 @@ public class PowerManagerService extends IPowerManager.Stub
if (isScreenTurningOffLocked()) {
return;
}
- handleLightSensorValue((int)event.values[0]);
+ handleLightSensorValue((int)event.values[0], false);
}
}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 51adebe..a71ccb5 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1573,6 +1573,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private long getTotalBytes(NetworkTemplate template, long start, long end) {
try {
return mNetworkStats.getSummaryForNetwork(template, start, end).getTotalBytes();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "problem reading network stats: " + e);
+ return 0;
} catch (RemoteException e) {
// ignored; service lives in system_server
return 0;
diff --git a/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java
new file mode 100644
index 0000000..70038d9
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkStatsCollection.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2012 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.net;
+
+import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.SET_ALL;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.TrafficStats.UID_REMOVED;
+
+import android.net.NetworkIdentity;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.text.format.DateUtils;
+
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FileRotator;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Objects;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import libcore.io.IoUtils;
+
+/**
+ * Collection of {@link NetworkStatsHistory}, stored based on combined key of
+ * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
+ */
+public class NetworkStatsCollection implements FileRotator.Reader {
+ private static final String TAG = "NetworkStatsCollection";
+
+ /** File header magic number: "ANET" */
+ private static final int FILE_MAGIC = 0x414E4554;
+
+ private static final int VERSION_NETWORK_INIT = 1;
+
+ private static final int VERSION_UID_INIT = 1;
+ private static final int VERSION_UID_WITH_IDENT = 2;
+ private static final int VERSION_UID_WITH_TAG = 3;
+ private static final int VERSION_UID_WITH_SET = 4;
+
+ private static final int VERSION_UNIFIED_INIT = 16;
+
+ private HashMap<Key, NetworkStatsHistory> mStats = Maps.newHashMap();
+
+ private long mBucketDuration;
+
+ private long mStartMillis;
+ private long mEndMillis;
+ private long mTotalBytes;
+ private boolean mDirty;
+
+ public NetworkStatsCollection(long bucketDuration) {
+ mBucketDuration = bucketDuration;
+ reset();
+ }
+
+ public void reset() {
+ mStats.clear();
+ mStartMillis = Long.MAX_VALUE;
+ mEndMillis = Long.MIN_VALUE;
+ mTotalBytes = 0;
+ mDirty = false;
+ }
+
+ public long getStartMillis() {
+ return mStartMillis;
+ }
+
+ public long getEndMillis() {
+ return mEndMillis;
+ }
+
+ public long getTotalBytes() {
+ return mTotalBytes;
+ }
+
+ public boolean isDirty() {
+ return mDirty;
+ }
+
+ public void clearDirty() {
+ mDirty = false;
+ }
+
+ public boolean isEmpty() {
+ return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
+ }
+
+ /**
+ * Combine all {@link NetworkStatsHistory} in this collection which match
+ * the requested parameters.
+ */
+ public NetworkStatsHistory getHistory(
+ NetworkTemplate template, int uid, int set, int tag, int fields) {
+ final NetworkStatsHistory combined = new NetworkStatsHistory(
+ mBucketDuration, estimateBuckets(), fields);
+ for (Map.Entry<Key, NetworkStatsHistory> entry : mStats.entrySet()) {
+ final Key key = entry.getKey();
+ final boolean setMatches = set == SET_ALL || key.set == set;
+ if (key.uid == uid && setMatches && key.tag == tag
+ && templateMatches(template, key.ident)) {
+ combined.recordEntireHistory(entry.getValue());
+ }
+ }
+ return combined;
+ }
+
+ /**
+ * Summarize all {@link NetworkStatsHistory} in this collection which match
+ * the requested parameters.
+ */
+ public NetworkStats getSummary(NetworkTemplate template, long start, long end) {
+ final long now = System.currentTimeMillis();
+
+ final NetworkStats stats = new NetworkStats(end - start, 24);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ NetworkStatsHistory.Entry historyEntry = null;
+
+ for (Map.Entry<Key, NetworkStatsHistory> mapEntry : mStats.entrySet()) {
+ final Key key = mapEntry.getKey();
+ if (templateMatches(template, key.ident)) {
+ final NetworkStatsHistory history = mapEntry.getValue();
+ historyEntry = history.getValues(start, end, now, historyEntry);
+
+ entry.iface = IFACE_ALL;
+ entry.uid = key.uid;
+ entry.set = key.set;
+ entry.tag = key.tag;
+ entry.rxBytes = historyEntry.rxBytes;
+ entry.rxPackets = historyEntry.rxPackets;
+ entry.txBytes = historyEntry.txBytes;
+ entry.txPackets = historyEntry.txPackets;
+ entry.operations = historyEntry.operations;
+
+ if (!entry.isEmpty()) {
+ stats.combineValues(entry);
+ }
+ }
+ }
+
+ return stats;
+ }
+
+ /**
+ * Record given {@link NetworkStats.Entry} into this collection.
+ */
+ public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
+ long end, NetworkStats.Entry entry) {
+ noteRecordedHistory(start, end, entry.rxBytes + entry.txBytes);
+ findOrCreateHistory(ident, uid, set, tag).recordData(start, end, entry);
+ }
+
+ /**
+ * Record given {@link NetworkStatsHistory} into this collection.
+ */
+ private void recordHistory(Key key, NetworkStatsHistory history) {
+ if (history.size() == 0) return;
+ noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
+
+ final NetworkStatsHistory existing = mStats.get(key);
+ if (existing != null) {
+ existing.recordEntireHistory(history);
+ } else {
+ mStats.put(key, history);
+ }
+ }
+
+ /**
+ * Record all {@link NetworkStatsHistory} contained in the given collection
+ * into this collection.
+ */
+ public void recordCollection(NetworkStatsCollection another) {
+ for (Map.Entry<Key, NetworkStatsHistory> entry : another.mStats.entrySet()) {
+ recordHistory(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private NetworkStatsHistory findOrCreateHistory(
+ NetworkIdentitySet ident, int uid, int set, int tag) {
+ final Key key = new Key(ident, uid, set, tag);
+ final NetworkStatsHistory existing = mStats.get(key);
+
+ // update when no existing, or when bucket duration changed
+ NetworkStatsHistory updated = null;
+ if (existing == null) {
+ updated = new NetworkStatsHistory(mBucketDuration, 10);
+ } else if (existing.getBucketDuration() != mBucketDuration) {
+ updated = new NetworkStatsHistory(existing, mBucketDuration);
+ }
+
+ if (updated != null) {
+ mStats.put(key, updated);
+ return updated;
+ } else {
+ return existing;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void read(InputStream in) throws IOException {
+ read(new DataInputStream(in));
+ }
+
+ public void read(DataInputStream in) throws IOException {
+ // verify file magic header intact
+ final int magic = in.readInt();
+ if (magic != FILE_MAGIC) {
+ throw new ProtocolException("unexpected magic: " + magic);
+ }
+
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_UNIFIED_INIT: {
+ // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
+ final int identSize = in.readInt();
+ for (int i = 0; i < identSize; i++) {
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
+
+ final int size = in.readInt();
+ for (int j = 0; j < size; j++) {
+ final int uid = in.readInt();
+ final int set = in.readInt();
+ final int tag = in.readInt();
+
+ final Key key = new Key(ident, uid, set, tag);
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+ recordHistory(key, history);
+ }
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ }
+
+ public void write(DataOutputStream out) throws IOException {
+ // cluster key lists grouped by ident
+ final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
+ for (Key key : mStats.keySet()) {
+ ArrayList<Key> keys = keysByIdent.get(key.ident);
+ if (keys == null) {
+ keys = Lists.newArrayList();
+ keysByIdent.put(key.ident, keys);
+ }
+ keys.add(key);
+ }
+
+ out.writeInt(FILE_MAGIC);
+ out.writeInt(VERSION_UNIFIED_INIT);
+
+ out.writeInt(keysByIdent.size());
+ for (NetworkIdentitySet ident : keysByIdent.keySet()) {
+ final ArrayList<Key> keys = keysByIdent.get(ident);
+ ident.writeToStream(out);
+
+ out.writeInt(keys.size());
+ for (Key key : keys) {
+ final NetworkStatsHistory history = mStats.get(key);
+ out.writeInt(key.uid);
+ out.writeInt(key.set);
+ out.writeInt(key.tag);
+ history.writeToStream(out);
+ }
+ }
+
+ out.flush();
+ }
+
+ @Deprecated
+ public void readLegacyNetwork(File file) throws IOException {
+ final AtomicFile inputFile = new AtomicFile(file);
+
+ DataInputStream in = null;
+ try {
+ in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
+
+ // verify file magic header intact
+ final int magic = in.readInt();
+ if (magic != FILE_MAGIC) {
+ throw new ProtocolException("unexpected magic: " + magic);
+ }
+
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_NETWORK_INIT: {
+ // network := size *(NetworkIdentitySet NetworkStatsHistory)
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+
+ final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
+ recordHistory(key, history);
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // missing stats is okay, probably first boot
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ @Deprecated
+ public void readLegacyUid(File file, boolean onlyTags) throws IOException {
+ final AtomicFile inputFile = new AtomicFile(file);
+
+ DataInputStream in = null;
+ try {
+ in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
+
+ // verify file magic header intact
+ final int magic = in.readInt();
+ if (magic != FILE_MAGIC) {
+ throw new ProtocolException("unexpected magic: " + magic);
+ }
+
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_UID_INIT: {
+ // uid := size *(UID NetworkStatsHistory)
+
+ // drop this data version, since we don't have a good
+ // mapping into NetworkIdentitySet.
+ break;
+ }
+ case VERSION_UID_WITH_IDENT: {
+ // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
+
+ // drop this data version, since this version only existed
+ // for a short time.
+ break;
+ }
+ case VERSION_UID_WITH_TAG:
+ case VERSION_UID_WITH_SET: {
+ // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
+ final int identSize = in.readInt();
+ for (int i = 0; i < identSize; i++) {
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
+
+ final int size = in.readInt();
+ for (int j = 0; j < size; j++) {
+ final int uid = in.readInt();
+ final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
+ : SET_DEFAULT;
+ final int tag = in.readInt();
+
+ final Key key = new Key(ident, uid, set, tag);
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+
+ if ((tag == TAG_NONE) != onlyTags) {
+ recordHistory(key, history);
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // missing stats is okay, probably first boot
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ /**
+ * Remove any {@link NetworkStatsHistory} attributed to the requested UID,
+ * moving any {@link NetworkStats#TAG_NONE} series to
+ * {@link TrafficStats#UID_REMOVED}.
+ */
+ public void removeUid(int uid) {
+ final ArrayList<Key> knownKeys = Lists.newArrayList();
+ knownKeys.addAll(mStats.keySet());
+
+ // migrate all UID stats into special "removed" bucket
+ for (Key key : knownKeys) {
+ if (key.uid == uid) {
+ // only migrate combined TAG_NONE history
+ if (key.tag == TAG_NONE) {
+ final NetworkStatsHistory uidHistory = mStats.get(key);
+ final NetworkStatsHistory removedHistory = findOrCreateHistory(
+ key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
+ removedHistory.recordEntireHistory(uidHistory);
+ }
+ mStats.remove(key);
+ mDirty = true;
+ }
+ }
+ }
+
+ private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
+ if (startMillis < mStartMillis) mStartMillis = startMillis;
+ if (endMillis > mEndMillis) mEndMillis = endMillis;
+ mTotalBytes += totalBytes;
+ mDirty = true;
+ }
+
+ private int estimateBuckets() {
+ return (int) (Math.min(mEndMillis - mStartMillis, DateUtils.WEEK_IN_MILLIS * 5)
+ / mBucketDuration);
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ final ArrayList<Key> keys = Lists.newArrayList();
+ keys.addAll(mStats.keySet());
+ Collections.sort(keys);
+
+ for (Key key : keys) {
+ pw.print("ident="); pw.print(key.ident.toString());
+ pw.print(" uid="); pw.print(key.uid);
+ pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
+ pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
+
+ final NetworkStatsHistory history = mStats.get(key);
+ pw.increaseIndent();
+ history.dump(pw, true);
+ pw.decreaseIndent();
+ }
+ }
+
+ /**
+ * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
+ * in the given {@link NetworkIdentitySet}.
+ */
+ private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
+ for (NetworkIdentity ident : identSet) {
+ if (template.matches(ident)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static class Key implements Comparable<Key> {
+ public final NetworkIdentitySet ident;
+ public final int uid;
+ public final int set;
+ public final int tag;
+
+ private final int hashCode;
+
+ public Key(NetworkIdentitySet ident, int uid, int set, int tag) {
+ this.ident = ident;
+ this.uid = uid;
+ this.set = set;
+ this.tag = tag;
+ hashCode = Objects.hashCode(ident, uid, set, tag);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Key) {
+ final Key key = (Key) obj;
+ return uid == key.uid && set == key.set && tag == key.tag
+ && Objects.equal(ident, key.ident);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Key another) {
+ return Integer.compare(uid, another.uid);
+ }
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java
new file mode 100644
index 0000000..e7ba358
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkStatsRecorder.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2012 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.net;
+
+import static android.net.NetworkStats.TAG_NONE;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.net.NetworkStats;
+import android.net.NetworkStats.NonMonotonicObserver;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.FileRotator;
+import com.android.internal.util.IndentingPrintWriter;
+import com.google.android.collect.Sets;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Logic to record deltas between periodic {@link NetworkStats} snapshots into
+ * {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}.
+ * Keeps pending changes in memory until they pass a specific threshold, in
+ * bytes. Uses {@link FileRotator} for persistence logic.
+ * <p>
+ * Not inherently thread safe.
+ */
+public class NetworkStatsRecorder {
+ private static final String TAG = "NetworkStatsRecorder";
+ private static final boolean LOGD = true;
+
+ private final FileRotator mRotator;
+ private final NonMonotonicObserver<String> mObserver;
+ private final String mCookie;
+
+ private final long mBucketDuration;
+ private final long mPersistThresholdBytes;
+ private final boolean mOnlyTags;
+
+ private NetworkStats mLastSnapshot;
+
+ private final NetworkStatsCollection mPending;
+ private final NetworkStatsCollection mSinceBoot;
+
+ private final CombiningRewriter mPendingRewriter;
+
+ private WeakReference<NetworkStatsCollection> mComplete;
+
+ public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
+ String cookie, long bucketDuration, long persistThresholdBytes, boolean onlyTags) {
+ mRotator = checkNotNull(rotator, "missing FileRotator");
+ mObserver = checkNotNull(observer, "missing NonMonotonicObserver");
+ mCookie = cookie;
+
+ mBucketDuration = bucketDuration;
+ mPersistThresholdBytes = persistThresholdBytes;
+ mOnlyTags = onlyTags;
+
+ mPending = new NetworkStatsCollection(bucketDuration);
+ mSinceBoot = new NetworkStatsCollection(bucketDuration);
+
+ mPendingRewriter = new CombiningRewriter(mPending);
+ }
+
+ public void resetLocked() {
+ mLastSnapshot = null;
+ mPending.reset();
+ mSinceBoot.reset();
+ mComplete.clear();
+ }
+
+ public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) {
+ return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
+ }
+
+ /**
+ * Load complete history represented by {@link FileRotator}. Caches
+ * internally as a {@link WeakReference}, and updated with future
+ * {@link #recordSnapshotLocked(NetworkStats, Map, long)} snapshots as long
+ * as reference is valid.
+ */
+ public NetworkStatsCollection getOrLoadCompleteLocked() {
+ NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
+ if (complete == null) {
+ if (LOGD) Slog.d(TAG, "getOrLoadCompleteLocked() reading from disk for " + mCookie);
+ try {
+ complete = new NetworkStatsCollection(mBucketDuration);
+ mRotator.readMatching(complete, Long.MIN_VALUE, Long.MAX_VALUE);
+ complete.recordCollection(mPending);
+ mComplete = new WeakReference<NetworkStatsCollection>(complete);
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem completely reading network stats", e);
+ }
+ }
+ return complete;
+ }
+
+ /**
+ * Record any delta that occurred since last {@link NetworkStats} snapshot,
+ * using the given {@link Map} to identify network interfaces. First
+ * snapshot is considered bootstrap, and is not counted as delta.
+ */
+ public void recordSnapshotLocked(NetworkStats snapshot,
+ Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
+ final HashSet<String> unknownIfaces = Sets.newHashSet();
+
+ // assume first snapshot is bootstrap and don't record
+ if (mLastSnapshot == null) {
+ mLastSnapshot = snapshot;
+ return;
+ }
+
+ final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
+
+ final NetworkStats delta = NetworkStats.subtract(
+ snapshot, mLastSnapshot, mObserver, mCookie);
+ final long end = currentTimeMillis;
+ final long start = end - delta.getElapsedRealtime();
+
+ NetworkStats.Entry entry = null;
+ for (int i = 0; i < delta.size(); i++) {
+ entry = delta.getValues(i, entry);
+ final NetworkIdentitySet ident = ifaceIdent.get(entry.iface);
+ if (ident == null) {
+ unknownIfaces.add(entry.iface);
+ continue;
+ }
+
+ // skip when no delta occured
+ if (entry.isEmpty()) continue;
+
+ // only record tag data when requested
+ if ((entry.tag == TAG_NONE) != mOnlyTags) {
+ mPending.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
+
+ // also record against boot stats when present
+ if (mSinceBoot != null) {
+ mSinceBoot.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
+ }
+
+ // also record against complete dataset when present
+ if (complete != null) {
+ complete.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
+ }
+ }
+ }
+
+ mLastSnapshot = snapshot;
+
+ if (LOGD && unknownIfaces.size() > 0) {
+ Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
+ }
+ }
+
+ /**
+ * Consider persisting any pending deltas, if they are beyond
+ * {@link #mPersistThresholdBytes}.
+ */
+ public void maybePersistLocked(long currentTimeMillis) {
+ final long pendingBytes = mPending.getTotalBytes();
+ if (pendingBytes >= mPersistThresholdBytes) {
+ forcePersistLocked(currentTimeMillis);
+ } else {
+ mRotator.maybeRotate(currentTimeMillis);
+ }
+ }
+
+ /**
+ * Force persisting any pending deltas.
+ */
+ public void forcePersistLocked(long currentTimeMillis) {
+ if (mPending.isDirty()) {
+ if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
+ try {
+ mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
+ mRotator.maybeRotate(currentTimeMillis);
+ mPending.reset();
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem persisting pending stats", e);
+ }
+ }
+ }
+
+ /**
+ * Remove the given UID from all {@link FileRotator} history, migrating it
+ * to {@link TrafficStats#UID_REMOVED}.
+ */
+ public void removeUidLocked(int uid) {
+ try {
+ // process all existing data to migrate uid
+ mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uid));
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem removing UID " + uid, e);
+ }
+
+ // clear UID from current stats snapshot
+ if (mLastSnapshot != null) {
+ mLastSnapshot = mLastSnapshot.withoutUid(uid);
+ }
+ }
+
+ /**
+ * Rewriter that will combine current {@link NetworkStatsCollection} values
+ * with anything read from disk, and write combined set to disk. Clears the
+ * original {@link NetworkStatsCollection} when finished writing.
+ */
+ private static class CombiningRewriter implements FileRotator.Rewriter {
+ private final NetworkStatsCollection mCollection;
+
+ public CombiningRewriter(NetworkStatsCollection collection) {
+ mCollection = checkNotNull(collection, "missing NetworkStatsCollection");
+ }
+
+ /** {@inheritDoc} */
+ public void reset() {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void read(InputStream in) throws IOException {
+ mCollection.read(in);
+ }
+
+ /** {@inheritDoc} */
+ public boolean shouldWrite() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public void write(OutputStream out) throws IOException {
+ mCollection.write(new DataOutputStream(out));
+ mCollection.reset();
+ }
+ }
+
+ /**
+ * Rewriter that will remove any {@link NetworkStatsHistory} attributed to
+ * the requested UID, only writing data back when modified.
+ */
+ public static class RemoveUidRewriter implements FileRotator.Rewriter {
+ private final NetworkStatsCollection mTemp;
+ private final int mUid;
+
+ public RemoveUidRewriter(long bucketDuration, int uid) {
+ mTemp = new NetworkStatsCollection(bucketDuration);
+ mUid = uid;
+ }
+
+ /** {@inheritDoc} */
+ public void reset() {
+ mTemp.reset();
+ }
+
+ /** {@inheritDoc} */
+ public void read(InputStream in) throws IOException {
+ mTemp.read(in);
+ mTemp.clearDirty();
+ mTemp.removeUid(mUid);
+ }
+
+ /** {@inheritDoc} */
+ public boolean shouldWrite() {
+ return mTemp.isDirty();
+ }
+
+ /** {@inheritDoc} */
+ public void write(OutputStream out) throws IOException {
+ mTemp.write(new DataOutputStream(out));
+ }
+ }
+
+ public void importLegacyNetworkLocked(File file) throws IOException {
+ // legacy file still exists; start empty to avoid double importing
+ mRotator.deleteAll();
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
+ collection.readLegacyNetwork(file);
+
+ final long startMillis = collection.getStartMillis();
+ final long endMillis = collection.getEndMillis();
+
+ if (!collection.isEmpty()) {
+ // process legacy data, creating active file at starting time, then
+ // using end time to possibly trigger rotation.
+ mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
+ mRotator.maybeRotate(endMillis);
+ }
+ }
+
+ public void importLegacyUidLocked(File file) throws IOException {
+ // legacy file still exists; start empty to avoid double importing
+ mRotator.deleteAll();
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
+ collection.readLegacyUid(file, mOnlyTags);
+
+ final long startMillis = collection.getStartMillis();
+ final long endMillis = collection.getEndMillis();
+
+ if (!collection.isEmpty()) {
+ // process legacy data, creating active file at starting time, then
+ // using end time to possibly trigger rotation.
+ mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
+ mRotator.maybeRotate(endMillis);
+ }
+ }
+
+ public void dumpLocked(IndentingPrintWriter pw, boolean fullHistory) {
+ pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
+ if (fullHistory) {
+ pw.println("Complete history:");
+ getOrLoadCompleteLocked().dump(pw);
+ } else {
+ pw.println("History since boot:");
+ mSinceBoot.dump(pw);
+ }
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index eeb7fec..c9b79e8 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -34,14 +34,18 @@ import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
-import static android.net.TrafficStats.UID_REMOVED;
-import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
-import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
+import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_DEV_PERSIST_BYTES;
+import static android.provider.Settings.Secure.NETSTATS_DEV_ROTATE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_GLOBAL_ALERT_BYTES;
import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
-import static android.provider.Settings.Secure.NETSTATS_TAG_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_SAMPLE_ENABLED;
+import static android.provider.Settings.Secure.NETSTATS_TIME_CACHE_MAX_AGE;
import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_UID_DELETE_AGE;
+import static android.provider.Settings.Secure.NETSTATS_UID_PERSIST_BYTES;
+import static android.provider.Settings.Secure.NETSTATS_UID_ROTATE_AGE;
import static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -61,12 +65,10 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
+import android.net.LinkProperties;
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkState;
@@ -74,6 +76,7 @@ import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.TrafficStats;
import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
@@ -94,32 +97,18 @@ import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TrustedTime;
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.Objects;
+import com.android.internal.util.FileRotator;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.connectivity.Tethering;
-import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.net.ProtocolException;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Random;
-
-import libcore.io.IoUtils;
/**
* Collect and persist detailed network statistics, and provide this data to
@@ -127,16 +116,8 @@ import libcore.io.IoUtils;
*/
public class NetworkStatsService extends INetworkStatsService.Stub {
private static final String TAG = "NetworkStats";
- private static final boolean LOGD = false;
- private static final boolean LOGV = false;
-
- /** File header magic number: "ANET" */
- private static final int FILE_MAGIC = 0x414E4554;
- private static final int VERSION_NETWORK_INIT = 1;
- private static final int VERSION_UID_INIT = 1;
- private static final int VERSION_UID_WITH_IDENT = 2;
- private static final int VERSION_UID_WITH_TAG = 3;
- private static final int VERSION_UID_WITH_SET = 4;
+ private static final boolean LOGD = true;
+ private static final boolean LOGV = true;
private static final int MSG_PERFORM_POLL = 1;
private static final int MSG_UPDATE_IFACES = 2;
@@ -147,9 +128,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
private static final int FLAG_PERSIST_FORCE = 0x100;
- /** Sample recent usage after each poll event. */
- private static final boolean ENABLE_SAMPLE_AFTER_POLL = true;
-
private static final String TAG_NETSTATS_ERROR = "netstats_error";
private final Context mContext;
@@ -159,10 +137,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private final TelephonyManager mTeleManager;
private final NetworkStatsSettings mSettings;
+ private final File mSystemDir;
+ private final File mBaseDir;
+
private final PowerManager.WakeLock mWakeLock;
private IConnectivityManager mConnManager;
- private DropBoxManager mDropBox;
// @VisibleForTesting
public static final String ACTION_NETWORK_STATS_POLL =
@@ -172,71 +152,76 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private PendingIntent mPollIntent;
- // TODO: trim empty history objects entirely
-
private static final long KB_IN_BYTES = 1024;
private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
+ private static final String PREFIX_DEV = "dev";
+ private static final String PREFIX_UID = "uid";
+ private static final String PREFIX_UID_TAG = "uid_tag";
+
/**
* Settings that can be changed externally.
*/
public interface NetworkStatsSettings {
public long getPollInterval();
- public long getPersistThreshold();
- public long getNetworkBucketDuration();
- public long getNetworkMaxHistory();
- public long getUidBucketDuration();
- public long getUidMaxHistory();
- public long getTagMaxHistory();
public long getTimeCacheMaxAge();
+ public long getGlobalAlertBytes();
+ public boolean getSampleEnabled();
+
+ public static class Config {
+ public final long bucketDuration;
+ public final long persistBytes;
+ public final long rotateAgeMillis;
+ public final long deleteAgeMillis;
+
+ public Config(long bucketDuration, long persistBytes, long rotateAgeMillis,
+ long deleteAgeMillis) {
+ this.bucketDuration = bucketDuration;
+ this.persistBytes = persistBytes;
+ this.rotateAgeMillis = rotateAgeMillis;
+ this.deleteAgeMillis = deleteAgeMillis;
+ }
+ }
+
+ public Config getDevConfig();
+ public Config getUidConfig();
+ public Config getUidTagConfig();
}
private final Object mStatsLock = new Object();
/** Set of currently active ifaces. */
private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
- /** Set of historical {@code dev} stats for known networks. */
- private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkDevStats = Maps.newHashMap();
- /** Set of historical {@code xtables} stats for known networks. */
- private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkXtStats = Maps.newHashMap();
- /** Set of historical {@code xtables} stats for known UIDs. */
- private HashMap<UidStatsKey, NetworkStatsHistory> mUidStats = Maps.newHashMap();
-
- /** Flag if {@link #mNetworkDevStats} have been loaded from disk. */
- private boolean mNetworkStatsLoaded = false;
- /** Flag if {@link #mUidStats} have been loaded from disk. */
- private boolean mUidStatsLoaded = false;
-
- private NetworkStats mLastPollNetworkDevSnapshot;
- private NetworkStats mLastPollNetworkXtSnapshot;
- private NetworkStats mLastPollUidSnapshot;
- private NetworkStats mLastPollOperationsSnapshot;
-
- private NetworkStats mLastPersistNetworkDevSnapshot;
- private NetworkStats mLastPersistNetworkXtSnapshot;
- private NetworkStats mLastPersistUidSnapshot;
+ /** Current default active iface. */
+ private String mActiveIface;
+
+ private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
+ new DropBoxNonMonotonicObserver();
+
+ private NetworkStatsRecorder mDevRecorder;
+ private NetworkStatsRecorder mUidRecorder;
+ private NetworkStatsRecorder mUidTagRecorder;
+
+ /** Cached {@link #mDevRecorder} stats. */
+ private NetworkStatsCollection mDevStatsCached;
/** Current counter sets for each UID. */
private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
/** Data layer operation counters for splicing into other structures. */
- private NetworkStats mOperations = new NetworkStats(0L, 10);
+ private NetworkStats mUidOperations = new NetworkStats(0L, 10);
private final HandlerThread mHandlerThread;
private final Handler mHandler;
- private final AtomicFile mNetworkDevFile;
- private final AtomicFile mNetworkXtFile;
- private final AtomicFile mUidFile;
-
public NetworkStatsService(
Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
this(context, networkManager, alarmManager, NtpTrustedTime.getInstance(context),
- getSystemDir(), new DefaultNetworkStatsSettings(context));
+ getDefaultSystemDir(), new DefaultNetworkStatsSettings(context));
}
- private static File getSystemDir() {
+ private static File getDefaultSystemDir() {
return new File(Environment.getDataDirectory(), "system");
}
@@ -258,9 +243,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
- mNetworkDevFile = new AtomicFile(new File(systemDir, "netstats.bin"));
- mNetworkXtFile = new AtomicFile(new File(systemDir, "netstats_xt.bin"));
- mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin"));
+ mSystemDir = checkNotNull(systemDir);
+ mBaseDir = new File(systemDir, "netstats");
+ mBaseDir.mkdirs();
}
public void bindConnectivityManager(IConnectivityManager connManager) {
@@ -273,17 +258,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return;
}
+ // create data recorders along with historical rotators
+ mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false);
+ mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false);
+ mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true);
+
synchronized (mStatsLock) {
+ // upgrade any legacy stats, migrating them to rotated files
+ maybeUpgradeLegacyStatsLocked();
+
// read historical network stats from disk, since policy service
- // might need them right away. we delay loading detailed UID stats
- // until actually needed.
- readNetworkDevStatsLocked();
- readNetworkXtStatsLocked();
- mNetworkStatsLoaded = true;
- }
+ // might need them right away.
+ mDevStatsCached = mDevRecorder.getOrLoadCompleteLocked();
- // bootstrap initial stats to prevent double-counting later
- bootstrapStats();
+ // bootstrap initial stats to prevent double-counting later
+ bootstrapStatsLocked();
+ }
// watch for network interfaces to be claimed
final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE);
@@ -317,8 +307,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
registerPollAlarmLocked();
registerGlobalAlert();
+ }
- mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
+ private NetworkStatsRecorder buildRecorder(
+ String prefix, NetworkStatsSettings.Config config, boolean includeTags) {
+ return new NetworkStatsRecorder(
+ new FileRotator(mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
+ mNonMonotonicObserver, prefix, config.bucketDuration, config.persistBytes,
+ includeTags);
}
private void shutdownLocked() {
@@ -330,18 +326,44 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mTeleManager.listen(mPhoneListener, LISTEN_NONE);
- if (mNetworkStatsLoaded) {
- writeNetworkDevStatsLocked();
- writeNetworkXtStatsLocked();
- }
- if (mUidStatsLoaded) {
- writeUidStatsLocked();
+ final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
+
+ // persist any pending stats
+ mDevRecorder.forcePersistLocked(currentTime);
+ mUidRecorder.forcePersistLocked(currentTime);
+ mUidTagRecorder.forcePersistLocked(currentTime);
+
+ mDevRecorder = null;
+ mUidRecorder = null;
+ mUidTagRecorder = null;
+
+ mDevStatsCached = null;
+ }
+
+ private void maybeUpgradeLegacyStatsLocked() {
+ File file;
+ try {
+ file = new File(mSystemDir, "netstats.bin");
+ if (file.exists()) {
+ mDevRecorder.importLegacyNetworkLocked(file);
+ file.delete();
+ }
+
+ file = new File(mSystemDir, "netstats_xt.bin");
+ if (file.exists()) {
+ file.delete();
+ }
+
+ file = new File(mSystemDir, "netstats_uid.bin");
+ if (file.exists()) {
+ mUidRecorder.importLegacyUidLocked(file);
+ mUidTagRecorder.importLegacyUidLocked(file);
+ file.delete();
+ }
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem during legacy upgrade", e);
}
- mNetworkDevStats.clear();
- mNetworkXtStats.clear();
- mUidStats.clear();
- mNetworkStatsLoaded = false;
- mUidStatsLoaded = false;
}
/**
@@ -372,7 +394,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
*/
private void registerGlobalAlert() {
try {
- final long alertBytes = mSettings.getPersistThreshold();
+ final long alertBytes = mSettings.getGlobalAlertBytes();
mNetworkManager.setGlobalAlert(alertBytes);
} catch (IllegalStateException e) {
Slog.w(TAG, "problem registering for global alert: " + e);
@@ -383,161 +405,39 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- return getHistoryForNetworkDev(template, fields);
- }
-
- private NetworkStatsHistory getHistoryForNetworkDev(NetworkTemplate template, int fields) {
- return getHistoryForNetwork(template, fields, mNetworkDevStats);
- }
-
- private NetworkStatsHistory getHistoryForNetworkXt(NetworkTemplate template, int fields) {
- return getHistoryForNetwork(template, fields, mNetworkXtStats);
+ return mDevStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields);
}
- private NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields,
- HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
- synchronized (mStatsLock) {
- // combine all interfaces that match template
- final NetworkStatsHistory combined = new NetworkStatsHistory(
- mSettings.getNetworkBucketDuration(), estimateNetworkBuckets(), fields);
- for (NetworkIdentitySet ident : source.keySet()) {
- if (templateMatches(template, ident)) {
- final NetworkStatsHistory history = source.get(ident);
- if (history != null) {
- combined.recordEntireHistory(history);
- }
- }
- }
- return combined;
- }
+ @Override
+ public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
+ return mDevStatsCached.getSummary(template, start, end);
}
@Override
public NetworkStatsHistory getHistoryForUid(
NetworkTemplate template, int uid, int set, int tag, int fields) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
-
- synchronized (mStatsLock) {
- ensureUidStatsLoadedLocked();
-
- // combine all interfaces that match template
- final NetworkStatsHistory combined = new NetworkStatsHistory(
- mSettings.getUidBucketDuration(), estimateUidBuckets(), fields);
- for (UidStatsKey key : mUidStats.keySet()) {
- final boolean setMatches = set == SET_ALL || key.set == set;
- if (templateMatches(template, key.ident) && key.uid == uid && setMatches
- && key.tag == tag) {
- final NetworkStatsHistory history = mUidStats.get(key);
- combined.recordEntireHistory(history);
- }
- }
-
- return combined;
- }
- }
-
- @Override
- public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- return getSummaryForNetworkDev(template, start, end);
- }
-
- private NetworkStats getSummaryForNetworkDev(NetworkTemplate template, long start, long end) {
- return getSummaryForNetwork(template, start, end, mNetworkDevStats);
- }
-
- private NetworkStats getSummaryForNetworkXt(NetworkTemplate template, long start, long end) {
- return getSummaryForNetwork(template, start, end, mNetworkXtStats);
- }
-
- private NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end,
- HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
- synchronized (mStatsLock) {
- // use system clock to be externally consistent
- final long now = System.currentTimeMillis();
-
- final NetworkStats stats = new NetworkStats(end - start, 1);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- NetworkStatsHistory.Entry historyEntry = null;
-
- // combine total from all interfaces that match template
- for (NetworkIdentitySet ident : source.keySet()) {
- if (templateMatches(template, ident)) {
- final NetworkStatsHistory history = source.get(ident);
- historyEntry = history.getValues(start, end, now, historyEntry);
-
- entry.iface = IFACE_ALL;
- entry.uid = UID_ALL;
- entry.tag = TAG_NONE;
- entry.rxBytes = historyEntry.rxBytes;
- entry.rxPackets = historyEntry.rxPackets;
- entry.txBytes = historyEntry.txBytes;
- entry.txPackets = historyEntry.txPackets;
-
- stats.combineValues(entry);
- }
- }
-
- return stats;
- }
- }
-
- private long getHistoryStartLocked(
- NetworkTemplate template, HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
- long start = Long.MAX_VALUE;
- for (NetworkIdentitySet ident : source.keySet()) {
- if (templateMatches(template, ident)) {
- final NetworkStatsHistory history = source.get(ident);
- start = Math.min(start, history.getStart());
- }
+ // TODO: transition to stats sessions to avoid WeakReferences
+ if (tag == TAG_NONE) {
+ return mUidRecorder.getOrLoadCompleteLocked().getHistory(
+ template, uid, set, tag, fields);
+ } else {
+ return mUidTagRecorder.getOrLoadCompleteLocked().getHistory(
+ template, uid, set, tag, fields);
}
- return start;
}
@Override
public NetworkStats getSummaryForAllUid(
NetworkTemplate template, long start, long end, boolean includeTags) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
-
- synchronized (mStatsLock) {
- ensureUidStatsLoadedLocked();
-
- // use system clock to be externally consistent
- final long now = System.currentTimeMillis();
-
- final NetworkStats stats = new NetworkStats(end - start, 24);
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- NetworkStatsHistory.Entry historyEntry = null;
-
- for (UidStatsKey key : mUidStats.keySet()) {
- if (templateMatches(template, key.ident)) {
- // always include summary under TAG_NONE, and include
- // other tags when requested.
- if (key.tag == TAG_NONE || includeTags) {
- final NetworkStatsHistory history = mUidStats.get(key);
- historyEntry = history.getValues(start, end, now, historyEntry);
-
- entry.iface = IFACE_ALL;
- entry.uid = key.uid;
- entry.set = key.set;
- entry.tag = key.tag;
- entry.rxBytes = historyEntry.rxBytes;
- entry.rxPackets = historyEntry.rxPackets;
- entry.txBytes = historyEntry.txBytes;
- entry.txPackets = historyEntry.txPackets;
- entry.operations = historyEntry.operations;
-
- if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
- || entry.txPackets > 0 || entry.operations > 0) {
- stats.combineValues(entry);
- }
- }
- }
- }
-
- return stats;
- }
+ // TODO: transition to stats sessions to avoid WeakReferences
+ final NetworkStats stats = mUidRecorder.getOrLoadCompleteLocked().getSummary(
+ template, start, end);
+ if (includeTags) {
+ final NetworkStats tagStats = mUidTagRecorder.getOrLoadCompleteLocked().getSummary(
+ template, start, end);
+ stats.combineAllValues(tagStats);
+ }
+ return stats;
}
@Override
@@ -567,7 +467,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
// splice in operation counts
- dataLayer.spliceOperationsFrom(mOperations);
+ dataLayer.spliceOperationsFrom(mUidOperations);
return dataLayer;
}
@@ -586,8 +486,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
synchronized (mStatsLock) {
final int set = mActiveUidCounterSet.get(uid, SET_DEFAULT);
- mOperations.combineValues(IFACE_ALL, uid, set, tag, 0L, 0L, 0L, 0L, operationCount);
- mOperations.combineValues(IFACE_ALL, uid, set, TAG_NONE, 0L, 0L, 0L, 0L, operationCount);
+ mUidOperations.combineValues(
+ mActiveIface, uid, set, tag, 0L, 0L, 0L, 0L, operationCount);
+ mUidOperations.combineValues(
+ mActiveIface, uid, set, TAG_NONE, 0L, 0L, 0L, 0L, operationCount);
}
}
@@ -755,13 +657,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
performPollLocked(FLAG_PERSIST_NETWORK);
final NetworkState[] states;
+ final LinkProperties activeLink;
try {
states = mConnManager.getAllNetworkState();
+ activeLink = mConnManager.getActiveLinkProperties();
} catch (RemoteException e) {
// ignored; service lives in system_server
return;
}
+ mActiveIface = activeLink != null ? activeLink.getInterfaceName() : null;
+
// rebuild active interfaces based on connected networks
mActiveIfaces.clear();
@@ -785,12 +691,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* Bootstrap initial stats snapshot, usually during {@link #systemReady()}
* so we have baseline values without double-counting.
*/
- private void bootstrapStats() {
+ private void bootstrapStatsLocked() {
+ final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
+
try {
- mLastPollUidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
- mLastPollNetworkDevSnapshot = mNetworkManager.getNetworkStatsSummary();
- mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
- mLastPollOperationsSnapshot = new NetworkStats(0L, 0);
+ // snapshot and record current counters; read UID stats first to
+ // avoid overcounting dev stats.
+ final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
+ final NetworkStats devSnapshot = getNetworkStatsSummary();
+
+ mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
+ mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
+ mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
+
} catch (IllegalStateException e) {
Slog.w(TAG, "problem reading network stats: " + e);
} catch (RemoteException e) {
@@ -830,27 +744,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: consider marking "untrusted" times in historical stats
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- final long threshold = mSettings.getPersistThreshold();
- final NetworkStats uidSnapshot;
- final NetworkStats networkXtSnapshot;
- final NetworkStats networkDevSnapshot;
try {
- // collect any tethering stats
- final NetworkStats tetherSnapshot = getNetworkStatsTethering();
-
- // record uid stats, folding in tethering stats
- uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
- uidSnapshot.combineAllValues(tetherSnapshot);
- performUidPollLocked(uidSnapshot, currentTime);
+ // snapshot and record current counters; read UID stats first to
+ // avoid overcounting dev stats.
+ final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
+ final NetworkStats devSnapshot = getNetworkStatsSummary();
- // record dev network stats
- networkDevSnapshot = mNetworkManager.getNetworkStatsSummary();
- performNetworkDevPollLocked(networkDevSnapshot, currentTime);
-
- // record xt network stats
- networkXtSnapshot = computeNetworkXtSnapshotFromUid(uidSnapshot);
- performNetworkXtPollLocked(networkXtSnapshot, currentTime);
+ mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
+ mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
+ mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveIfaces, currentTime);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
@@ -860,26 +763,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return;
}
- // persist when enough network data has occurred
- final long persistNetworkDevDelta = computeStatsDelta(
- mLastPersistNetworkDevSnapshot, networkDevSnapshot, true, "devp").getTotalBytes();
- final long persistNetworkXtDelta = computeStatsDelta(
- mLastPersistNetworkXtSnapshot, networkXtSnapshot, true, "xtp").getTotalBytes();
- final boolean networkOverThreshold = persistNetworkDevDelta > threshold
- || persistNetworkXtDelta > threshold;
- if (persistForce || (persistNetwork && networkOverThreshold)) {
- writeNetworkDevStatsLocked();
- writeNetworkXtStatsLocked();
- mLastPersistNetworkDevSnapshot = networkDevSnapshot;
- mLastPersistNetworkXtSnapshot = networkXtSnapshot;
- }
-
- // persist when enough uid data has occurred
- final long persistUidDelta = computeStatsDelta(
- mLastPersistUidSnapshot, uidSnapshot, true, "uidp").getTotalBytes();
- if (persistForce || (persistUid && persistUidDelta > threshold)) {
- writeUidStatsLocked();
- mLastPersistUidSnapshot = uidSnapshot;
+ // persist any pending data depending on requested flags
+ if (persistForce) {
+ mDevRecorder.forcePersistLocked(currentTime);
+ mUidRecorder.forcePersistLocked(currentTime);
+ mUidTagRecorder.forcePersistLocked(currentTime);
+ } else {
+ if (persistNetwork) {
+ mDevRecorder.maybePersistLocked(currentTime);
+ }
+ if (persistUid) {
+ mUidRecorder.maybePersistLocked(currentTime);
+ mUidTagRecorder.maybePersistLocked(currentTime);
+ }
}
if (LOGV) {
@@ -887,9 +783,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
Slog.v(TAG, "performPollLocked() took " + duration + "ms");
}
- if (ENABLE_SAMPLE_AFTER_POLL) {
+ if (mSettings.getSampleEnabled()) {
// sample stats after each full poll
- performSample();
+ performSampleLocked();
}
// finally, dispatch updated event to any listeners
@@ -899,511 +795,58 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
/**
- * Update {@link #mNetworkDevStats} historical usage.
- */
- private void performNetworkDevPollLocked(NetworkStats networkDevSnapshot, long currentTime) {
- final HashSet<String> unknownIface = Sets.newHashSet();
-
- final NetworkStats delta = computeStatsDelta(
- mLastPollNetworkDevSnapshot, networkDevSnapshot, false, "dev");
- final long timeStart = currentTime - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
- final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
- if (ident == null) {
- unknownIface.add(entry.iface);
- continue;
- }
-
- final NetworkStatsHistory history = findOrCreateNetworkDevStatsLocked(ident);
- history.recordData(timeStart, currentTime, entry);
- }
-
- mLastPollNetworkDevSnapshot = networkDevSnapshot;
-
- if (LOGD && unknownIface.size() > 0) {
- Slog.w(TAG, "unknown dev interfaces " + unknownIface + ", ignoring those stats");
- }
- }
-
- /**
- * Update {@link #mNetworkXtStats} historical usage.
- */
- private void performNetworkXtPollLocked(NetworkStats networkXtSnapshot, long currentTime) {
- final HashSet<String> unknownIface = Sets.newHashSet();
-
- final NetworkStats delta = computeStatsDelta(
- mLastPollNetworkXtSnapshot, networkXtSnapshot, false, "xt");
- final long timeStart = currentTime - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
- final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
- if (ident == null) {
- unknownIface.add(entry.iface);
- continue;
- }
-
- final NetworkStatsHistory history = findOrCreateNetworkXtStatsLocked(ident);
- history.recordData(timeStart, currentTime, entry);
- }
-
- mLastPollNetworkXtSnapshot = networkXtSnapshot;
-
- if (LOGD && unknownIface.size() > 0) {
- Slog.w(TAG, "unknown xt interfaces " + unknownIface + ", ignoring those stats");
- }
- }
-
- /**
- * Update {@link #mUidStats} historical usage.
- */
- private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
- ensureUidStatsLoadedLocked();
-
- final NetworkStats delta = computeStatsDelta(
- mLastPollUidSnapshot, uidSnapshot, false, "uid");
- final NetworkStats operationsDelta = computeStatsDelta(
- mLastPollOperationsSnapshot, mOperations, false, "uidop");
- final long timeStart = currentTime - delta.getElapsedRealtime();
-
- NetworkStats.Entry entry = null;
- NetworkStats.Entry operationsEntry = null;
- for (int i = 0; i < delta.size(); i++) {
- entry = delta.getValues(i, entry);
- final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
- if (ident == null) {
- if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
- || entry.txPackets > 0) {
- Log.w(TAG, "dropping UID delta from unknown iface: " + entry);
- }
- continue;
- }
-
- // splice in operation counts since last poll
- final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.set, entry.tag);
- if (j != -1) {
- operationsEntry = operationsDelta.getValues(j, operationsEntry);
- entry.operations = operationsEntry.operations;
- }
-
- final NetworkStatsHistory history = findOrCreateUidStatsLocked(
- ident, entry.uid, entry.set, entry.tag);
- history.recordData(timeStart, currentTime, entry);
- }
-
- mLastPollUidSnapshot = uidSnapshot;
- mLastPollOperationsSnapshot = mOperations.clone();
- }
-
- /**
* Sample recent statistics summary into {@link EventLog}.
*/
- private void performSample() {
- final long largestBucketSize = Math.max(
- mSettings.getNetworkBucketDuration(), mSettings.getUidBucketDuration());
-
- // take sample as atomic buckets
- final long now = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
- final long end = now - (now % largestBucketSize) + largestBucketSize;
- final long start = end - largestBucketSize;
-
+ private void performSampleLocked() {
+ // TODO: migrate trustedtime fixes to separate binary log events
final long trustedTime = mTime.hasCache() ? mTime.currentTimeMillis() : -1;
- long devHistoryStart = Long.MAX_VALUE;
- NetworkTemplate template = null;
- NetworkStats.Entry devTotal = null;
- NetworkStats.Entry xtTotal = null;
- NetworkStats.Entry uidTotal = null;
+ NetworkTemplate template;
+ NetworkStats.Entry devTotal;
+ NetworkStats.Entry xtTotal;
+ NetworkStats.Entry uidTotal;
// collect mobile sample
template = buildTemplateMobileAll(getActiveSubscriberId(mContext));
- devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal);
- devHistoryStart = getHistoryStartLocked(template, mNetworkDevStats);
- xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal);
- uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
+ devTotal = mDevRecorder.getTotalSinceBootLocked(template);
+ xtTotal = new NetworkStats.Entry();
+ uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
EventLogTags.writeNetstatsMobileSample(
devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
- trustedTime, devHistoryStart);
+ trustedTime);
// collect wifi sample
template = buildTemplateWifi();
- devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal);
- devHistoryStart = getHistoryStartLocked(template, mNetworkDevStats);
- xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal);
- uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
+ devTotal = mDevRecorder.getTotalSinceBootLocked(template);
+ xtTotal = new NetworkStats.Entry();
+ uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
+
EventLogTags.writeNetstatsWifiSample(
devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
- trustedTime, devHistoryStart);
+ trustedTime);
}
/**
- * Clean up {@link #mUidStats} after UID is removed.
+ * Clean up {@link #mUidRecorder} after UID is removed.
*/
private void removeUidLocked(int uid) {
- ensureUidStatsLoadedLocked();
-
// perform one last poll before removing
performPollLocked(FLAG_PERSIST_ALL);
- final ArrayList<UidStatsKey> knownKeys = Lists.newArrayList();
- knownKeys.addAll(mUidStats.keySet());
-
- // migrate all UID stats into special "removed" bucket
- for (UidStatsKey key : knownKeys) {
- if (key.uid == uid) {
- // only migrate combined TAG_NONE history
- if (key.tag == TAG_NONE) {
- final NetworkStatsHistory uidHistory = mUidStats.get(key);
- final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
- key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
- removedHistory.recordEntireHistory(uidHistory);
- }
- mUidStats.remove(key);
- }
- }
-
- // clear UID from current stats snapshot
- if (mLastPollUidSnapshot != null) {
- mLastPollUidSnapshot = mLastPollUidSnapshot.withoutUid(uid);
- mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
- }
+ mUidRecorder.removeUidLocked(uid);
+ mUidTagRecorder.removeUidLocked(uid);
// clear kernel stats associated with UID
resetKernelUidStats(uid);
-
- // since this was radical rewrite, push to disk
- writeUidStatsLocked();
- }
-
- private NetworkStatsHistory findOrCreateNetworkXtStatsLocked(NetworkIdentitySet ident) {
- return findOrCreateNetworkStatsLocked(ident, mNetworkXtStats);
- }
-
- private NetworkStatsHistory findOrCreateNetworkDevStatsLocked(NetworkIdentitySet ident) {
- return findOrCreateNetworkStatsLocked(ident, mNetworkDevStats);
- }
-
- private NetworkStatsHistory findOrCreateNetworkStatsLocked(
- NetworkIdentitySet ident, HashMap<NetworkIdentitySet, NetworkStatsHistory> source) {
- final NetworkStatsHistory existing = source.get(ident);
-
- // update when no existing, or when bucket duration changed
- final long bucketDuration = mSettings.getNetworkBucketDuration();
- NetworkStatsHistory updated = null;
- if (existing == null) {
- updated = new NetworkStatsHistory(bucketDuration, 10);
- } else if (existing.getBucketDuration() != bucketDuration) {
- updated = new NetworkStatsHistory(
- bucketDuration, estimateResizeBuckets(existing, bucketDuration));
- updated.recordEntireHistory(existing);
- }
-
- if (updated != null) {
- source.put(ident, updated);
- return updated;
- } else {
- return existing;
- }
- }
-
- private NetworkStatsHistory findOrCreateUidStatsLocked(
- NetworkIdentitySet ident, int uid, int set, int tag) {
- ensureUidStatsLoadedLocked();
-
- final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
- final NetworkStatsHistory existing = mUidStats.get(key);
-
- // update when no existing, or when bucket duration changed
- final long bucketDuration = mSettings.getUidBucketDuration();
- NetworkStatsHistory updated = null;
- if (existing == null) {
- updated = new NetworkStatsHistory(bucketDuration, 10);
- } else if (existing.getBucketDuration() != bucketDuration) {
- updated = new NetworkStatsHistory(
- bucketDuration, estimateResizeBuckets(existing, bucketDuration));
- updated.recordEntireHistory(existing);
- }
-
- if (updated != null) {
- mUidStats.put(key, updated);
- return updated;
- } else {
- return existing;
- }
- }
-
- private void readNetworkDevStatsLocked() {
- if (LOGV) Slog.v(TAG, "readNetworkDevStatsLocked()");
- readNetworkStats(mNetworkDevFile, mNetworkDevStats);
- }
-
- private void readNetworkXtStatsLocked() {
- if (LOGV) Slog.v(TAG, "readNetworkXtStatsLocked()");
- readNetworkStats(mNetworkXtFile, mNetworkXtStats);
- }
-
- private static void readNetworkStats(
- AtomicFile inputFile, HashMap<NetworkIdentitySet, NetworkStatsHistory> output) {
- // clear any existing stats and read from disk
- output.clear();
-
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
-
- // verify file magic header intact
- final int magic = in.readInt();
- if (magic != FILE_MAGIC) {
- throw new ProtocolException("unexpected magic: " + magic);
- }
-
- final int version = in.readInt();
- switch (version) {
- case VERSION_NETWORK_INIT: {
- // network := size *(NetworkIdentitySet NetworkStatsHistory)
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- final NetworkIdentitySet ident = new NetworkIdentitySet(in);
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
- output.put(ident, history);
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- } catch (FileNotFoundException e) {
- // missing stats is okay, probably first boot
- } catch (IOException e) {
- Log.wtf(TAG, "problem reading network stats", e);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- private void ensureUidStatsLoadedLocked() {
- if (!mUidStatsLoaded) {
- readUidStatsLocked();
- mUidStatsLoaded = true;
- }
- }
-
- private void readUidStatsLocked() {
- if (LOGV) Slog.v(TAG, "readUidStatsLocked()");
-
- // clear any existing stats and read from disk
- mUidStats.clear();
-
- DataInputStream in = null;
- try {
- in = new DataInputStream(new BufferedInputStream(mUidFile.openRead()));
-
- // verify file magic header intact
- final int magic = in.readInt();
- if (magic != FILE_MAGIC) {
- throw new ProtocolException("unexpected magic: " + magic);
- }
-
- final int version = in.readInt();
- switch (version) {
- case VERSION_UID_INIT: {
- // uid := size *(UID NetworkStatsHistory)
-
- // drop this data version, since we don't have a good
- // mapping into NetworkIdentitySet.
- break;
- }
- case VERSION_UID_WITH_IDENT: {
- // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
-
- // drop this data version, since this version only existed
- // for a short time.
- break;
- }
- case VERSION_UID_WITH_TAG:
- case VERSION_UID_WITH_SET: {
- // uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
- final int identSize = in.readInt();
- for (int i = 0; i < identSize; i++) {
- final NetworkIdentitySet ident = new NetworkIdentitySet(in);
-
- final int size = in.readInt();
- for (int j = 0; j < size; j++) {
- final int uid = in.readInt();
- final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
- : SET_DEFAULT;
- final int tag = in.readInt();
-
- final UidStatsKey key = new UidStatsKey(ident, uid, set, tag);
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
- mUidStats.put(key, history);
- }
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- } catch (FileNotFoundException e) {
- // missing stats is okay, probably first boot
- } catch (IOException e) {
- Log.wtf(TAG, "problem reading uid stats", e);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- private void writeNetworkDevStatsLocked() {
- if (LOGV) Slog.v(TAG, "writeNetworkDevStatsLocked()");
- writeNetworkStats(mNetworkDevStats, mNetworkDevFile);
- }
-
- private void writeNetworkXtStatsLocked() {
- if (LOGV) Slog.v(TAG, "writeNetworkXtStatsLocked()");
- writeNetworkStats(mNetworkXtStats, mNetworkXtFile);
- }
-
- private void writeNetworkStats(
- HashMap<NetworkIdentitySet, NetworkStatsHistory> input, AtomicFile outputFile) {
- // TODO: consider duplicating stats and releasing lock while writing
-
- // trim any history beyond max
- if (mTime.hasCache()) {
- final long systemCurrentTime = System.currentTimeMillis();
- final long trustedCurrentTime = mTime.currentTimeMillis();
-
- final long currentTime = Math.min(systemCurrentTime, trustedCurrentTime);
- final long maxHistory = mSettings.getNetworkMaxHistory();
-
- for (NetworkStatsHistory history : input.values()) {
- final int beforeSize = history.size();
- history.removeBucketsBefore(currentTime - maxHistory);
- final int afterSize = history.size();
-
- if (beforeSize > 24 && afterSize < beforeSize / 2) {
- // yikes, dropping more than half of significant history
- final StringBuilder builder = new StringBuilder();
- builder.append("yikes, dropping more than half of history").append('\n');
- builder.append("systemCurrentTime=").append(systemCurrentTime).append('\n');
- builder.append("trustedCurrentTime=").append(trustedCurrentTime).append('\n');
- builder.append("maxHistory=").append(maxHistory).append('\n');
- builder.append("beforeSize=").append(beforeSize).append('\n');
- builder.append("afterSize=").append(afterSize).append('\n');
- mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString());
- }
- }
- }
-
- FileOutputStream fos = null;
- try {
- fos = outputFile.startWrite();
- final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
-
- out.writeInt(FILE_MAGIC);
- out.writeInt(VERSION_NETWORK_INIT);
-
- out.writeInt(input.size());
- for (NetworkIdentitySet ident : input.keySet()) {
- final NetworkStatsHistory history = input.get(ident);
- ident.writeToStream(out);
- history.writeToStream(out);
- }
-
- out.flush();
- outputFile.finishWrite(fos);
- } catch (IOException e) {
- Log.wtf(TAG, "problem writing stats", e);
- if (fos != null) {
- outputFile.failWrite(fos);
- }
- }
- }
-
- private void writeUidStatsLocked() {
- if (LOGV) Slog.v(TAG, "writeUidStatsLocked()");
-
- if (!mUidStatsLoaded) {
- Slog.w(TAG, "asked to write UID stats when not loaded; skipping");
- return;
- }
-
- // TODO: consider duplicating stats and releasing lock while writing
-
- // trim any history beyond max
- if (mTime.hasCache()) {
- final long currentTime = Math.min(
- System.currentTimeMillis(), mTime.currentTimeMillis());
- final long maxUidHistory = mSettings.getUidMaxHistory();
- final long maxTagHistory = mSettings.getTagMaxHistory();
- for (UidStatsKey key : mUidStats.keySet()) {
- final NetworkStatsHistory history = mUidStats.get(key);
-
- // detailed tags are trimmed sooner than summary in TAG_NONE
- if (key.tag == TAG_NONE) {
- history.removeBucketsBefore(currentTime - maxUidHistory);
- } else {
- history.removeBucketsBefore(currentTime - maxTagHistory);
- }
- }
- }
-
- // build UidStatsKey lists grouped by ident
- final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
- for (UidStatsKey key : mUidStats.keySet()) {
- ArrayList<UidStatsKey> keys = keysByIdent.get(key.ident);
- if (keys == null) {
- keys = Lists.newArrayList();
- keysByIdent.put(key.ident, keys);
- }
- keys.add(key);
- }
-
- FileOutputStream fos = null;
- try {
- fos = mUidFile.startWrite();
- final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
-
- out.writeInt(FILE_MAGIC);
- out.writeInt(VERSION_UID_WITH_SET);
-
- out.writeInt(keysByIdent.size());
- for (NetworkIdentitySet ident : keysByIdent.keySet()) {
- final ArrayList<UidStatsKey> keys = keysByIdent.get(ident);
- ident.writeToStream(out);
-
- out.writeInt(keys.size());
- for (UidStatsKey key : keys) {
- final NetworkStatsHistory history = mUidStats.get(key);
- out.writeInt(key.uid);
- out.writeInt(key.set);
- out.writeInt(key.tag);
- history.writeToStream(out);
- }
- }
-
- out.flush();
- mUidFile.finishWrite(fos);
- } catch (IOException e) {
- Log.wtf(TAG, "problem writing stats", e);
- if (fos != null) {
- mUidFile.failWrite(fos);
- }
- }
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
final HashSet<String> argSet = new HashSet<String>();
@@ -1411,187 +854,68 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
argSet.add(arg);
}
- final boolean fullHistory = argSet.contains("full");
+ // usage: dumpsys netstats --full --uid --tag
+ final boolean poll = argSet.contains("--poll") || argSet.contains("poll");
+ final boolean fullHistory = argSet.contains("--full") || argSet.contains("full");
+ final boolean includeUid = argSet.contains("--uid") || argSet.contains("detail");
+ final boolean includeTag = argSet.contains("--tag") || argSet.contains("detail");
- synchronized (mStatsLock) {
- // TODO: remove this testing code, since it corrupts stats
- if (argSet.contains("generate")) {
- generateRandomLocked(args);
- pw.println("Generated stub stats");
- return;
- }
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- if (argSet.contains("poll")) {
+ synchronized (mStatsLock) {
+ if (poll) {
performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
pw.println("Forced poll");
return;
}
pw.println("Active interfaces:");
+ pw.increaseIndent();
for (String iface : mActiveIfaces.keySet()) {
final NetworkIdentitySet ident = mActiveIfaces.get(iface);
- pw.print(" iface="); pw.print(iface);
+ pw.print("iface="); pw.print(iface);
pw.print(" ident="); pw.println(ident.toString());
}
-
- pw.println("Known historical dev stats:");
- for (NetworkIdentitySet ident : mNetworkDevStats.keySet()) {
- final NetworkStatsHistory history = mNetworkDevStats.get(ident);
- pw.print(" ident="); pw.println(ident.toString());
- history.dump(" ", pw, fullHistory);
- }
-
- pw.println("Known historical xt stats:");
- for (NetworkIdentitySet ident : mNetworkXtStats.keySet()) {
- final NetworkStatsHistory history = mNetworkXtStats.get(ident);
- pw.print(" ident="); pw.println(ident.toString());
- history.dump(" ", pw, fullHistory);
+ pw.decreaseIndent();
+
+ pw.println("Dev stats:");
+ pw.increaseIndent();
+ mDevRecorder.dumpLocked(pw, fullHistory);
+ pw.decreaseIndent();
+
+ if (includeUid) {
+ pw.println("UID stats:");
+ pw.increaseIndent();
+ mUidRecorder.dumpLocked(pw, fullHistory);
+ pw.decreaseIndent();
}
- if (argSet.contains("detail")) {
- // since explicitly requested with argument, we're okay to load
- // from disk if not already in memory.
- ensureUidStatsLoadedLocked();
-
- final ArrayList<UidStatsKey> keys = Lists.newArrayList();
- keys.addAll(mUidStats.keySet());
- Collections.sort(keys);
-
- pw.println("Detailed UID stats:");
- for (UidStatsKey key : keys) {
- pw.print(" ident="); pw.print(key.ident.toString());
- pw.print(" uid="); pw.print(key.uid);
- pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
- pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
-
- final NetworkStatsHistory history = mUidStats.get(key);
- history.dump(" ", pw, fullHistory);
- }
+ if (includeTag) {
+ pw.println("UID tag stats:");
+ pw.increaseIndent();
+ mUidTagRecorder.dumpLocked(pw, fullHistory);
+ pw.decreaseIndent();
}
}
}
- /**
- * @deprecated only for temporary testing
- */
- @Deprecated
- private void generateRandomLocked(String[] args) {
- final long totalBytes = Long.parseLong(args[1]);
- final long totalTime = Long.parseLong(args[2]);
-
- final PackageManager pm = mContext.getPackageManager();
- final ArrayList<Integer> specialUidList = Lists.newArrayList();
- for (int i = 3; i < args.length; i++) {
- try {
- specialUidList.add(pm.getApplicationInfo(args[i], 0).uid);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- final HashSet<Integer> otherUidSet = Sets.newHashSet();
- for (ApplicationInfo info : pm.getInstalledApplications(0)) {
- if (pm.checkPermission(android.Manifest.permission.INTERNET, info.packageName)
- == PackageManager.PERMISSION_GRANTED && !specialUidList.contains(info.uid)) {
- otherUidSet.add(info.uid);
- }
- }
-
- final ArrayList<Integer> otherUidList = new ArrayList<Integer>(otherUidSet);
-
- final long end = System.currentTimeMillis();
- final long start = end - totalTime;
-
- mNetworkDevStats.clear();
- mNetworkXtStats.clear();
- mUidStats.clear();
-
- final Random r = new Random();
- for (NetworkIdentitySet ident : mActiveIfaces.values()) {
- final NetworkStatsHistory devHistory = findOrCreateNetworkDevStatsLocked(ident);
- final NetworkStatsHistory xtHistory = findOrCreateNetworkXtStatsLocked(ident);
-
- final ArrayList<Integer> uidList = new ArrayList<Integer>();
- uidList.addAll(specialUidList);
-
- if (uidList.size() == 0) {
- Collections.shuffle(otherUidList);
- uidList.addAll(otherUidList);
- }
-
- boolean first = true;
- long remainingBytes = totalBytes;
- for (int uid : uidList) {
- final NetworkStatsHistory defaultHistory = findOrCreateUidStatsLocked(
- ident, uid, SET_DEFAULT, TAG_NONE);
- final NetworkStatsHistory foregroundHistory = findOrCreateUidStatsLocked(
- ident, uid, SET_FOREGROUND, TAG_NONE);
-
- final long uidBytes = totalBytes / uidList.size();
-
- final float fractionDefault = r.nextFloat();
- final long defaultBytes = (long) (uidBytes * fractionDefault);
- final long foregroundBytes = (long) (uidBytes * (1 - fractionDefault));
-
- defaultHistory.generateRandom(start, end, defaultBytes);
- foregroundHistory.generateRandom(start, end, foregroundBytes);
-
- if (first) {
- final long bumpTime = (start + end) / 2;
- defaultHistory.recordData(
- bumpTime, bumpTime + DAY_IN_MILLIS, 200 * MB_IN_BYTES, 0);
- first = false;
- }
-
- devHistory.recordEntireHistory(defaultHistory);
- devHistory.recordEntireHistory(foregroundHistory);
- xtHistory.recordEntireHistory(defaultHistory);
- xtHistory.recordEntireHistory(foregroundHistory);
- }
- }
- }
-
- private StatsObserver mStatsObserver = new StatsObserver();
-
- private class StatsObserver implements NonMonotonicObserver {
- private String mCurrentType;
-
- public void setCurrentType(String type) {
- mCurrentType = type;
- }
-
- /** {@inheritDoc} */
- public void foundNonMonotonic(
- NetworkStats left, int leftIndex, NetworkStats right, int rightIndex) {
- Log.w(TAG, "found non-monotonic values; saving to dropbox");
-
- // record error for debugging
- final StringBuilder builder = new StringBuilder();
- builder.append("found non-monotonic " + mCurrentType + " values at left[" + leftIndex
- + "] - right[" + rightIndex + "]\n");
- builder.append("left=").append(left).append('\n');
- builder.append("right=").append(right).append('\n');
- mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString());
- }
+ private NetworkStats getNetworkStatsSummary() throws RemoteException {
+ return mNetworkManager.getNetworkStatsSummary();
}
/**
- * Return the delta between two {@link NetworkStats} snapshots, where {@code
- * before} can be {@code null}.
+ * Return snapshot of current UID statistics, including any
+ * {@link TrafficStats#UID_TETHERING} and {@link #mUidOperations} values.
*/
- private NetworkStats computeStatsDelta(
- NetworkStats before, NetworkStats current, boolean collectStale, String type) {
- if (before != null) {
- mStatsObserver.setCurrentType(type);
- return NetworkStats.subtract(current, before, mStatsObserver);
- } else if (collectStale) {
- // caller is okay collecting stale stats for first call.
- return current;
- } else {
- // this is first snapshot; to prevent from double-counting we only
- // observe traffic occuring between known snapshots.
- return new NetworkStats(0L, 10);
- }
+ private NetworkStats getNetworkStatsUidDetail() throws RemoteException {
+ final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+
+ // fold tethering stats and operations into uid snapshot
+ final NetworkStats tetherSnapshot = getNetworkStatsTethering();
+ uidSnapshot.combineAllValues(tetherSnapshot);
+ uidSnapshot.combineAllValues(mUidOperations);
+
+ return uidSnapshot;
}
/**
@@ -1608,35 +932,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
- private static NetworkStats computeNetworkXtSnapshotFromUid(NetworkStats uidSnapshot) {
- return uidSnapshot.groupedByIface();
- }
-
- private int estimateNetworkBuckets() {
- return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
- }
-
- private int estimateUidBuckets() {
- return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration());
- }
-
- private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) {
- return (int) (existing.size() * existing.getBucketDuration() / newBucketDuration);
- }
-
- /**
- * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
- * in the given {@link NetworkIdentitySet}.
- */
- private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
- for (NetworkIdentity ident : identSet) {
- if (template.matches(ident)) {
- return true;
- }
- }
- return false;
- }
-
private Handler.Callback mHandlerCallback = new Handler.Callback() {
/** {@inheritDoc} */
public boolean handleMessage(Message msg) {
@@ -1672,40 +967,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
- /**
- * Key uniquely identifying a {@link NetworkStatsHistory} for a UID.
- */
- private static class UidStatsKey implements Comparable<UidStatsKey> {
- public final NetworkIdentitySet ident;
- public final int uid;
- public final int set;
- public final int tag;
-
- public UidStatsKey(NetworkIdentitySet ident, int uid, int set, int tag) {
- this.ident = ident;
- this.uid = uid;
- this.set = set;
- this.tag = tag;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(ident, uid, set, tag);
- }
+ private class DropBoxNonMonotonicObserver implements NonMonotonicObserver<String> {
+ /** {@inheritDoc} */
+ public void foundNonMonotonic(NetworkStats left, int leftIndex, NetworkStats right,
+ int rightIndex, String cookie) {
+ Log.w(TAG, "found non-monotonic values; saving to dropbox");
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof UidStatsKey) {
- final UidStatsKey key = (UidStatsKey) obj;
- return Objects.equal(ident, key.ident) && uid == key.uid && set == key.set
- && tag == key.tag;
- }
- return false;
- }
+ // record error for debugging
+ final StringBuilder builder = new StringBuilder();
+ builder.append("found non-monotonic " + cookie + " values at left[" + leftIndex
+ + "] - right[" + rightIndex + "]\n");
+ builder.append("left=").append(left).append('\n');
+ builder.append("right=").append(right).append('\n');
- /** {@inheritDoc} */
- public int compareTo(UidStatsKey another) {
- return Integer.compare(uid, another.uid);
+ final DropBoxManager dropBox = (DropBoxManager) mContext.getSystemService(
+ Context.DROPBOX_SERVICE);
+ dropBox.addText(TAG_NETSTATS_ERROR, builder.toString());
}
}
@@ -1731,26 +1008,35 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public long getPollInterval() {
return getSecureLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
}
- public long getPersistThreshold() {
- return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 2 * MB_IN_BYTES);
- }
- public long getNetworkBucketDuration() {
- return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS);
+ public long getTimeCacheMaxAge() {
+ return getSecureLong(NETSTATS_TIME_CACHE_MAX_AGE, DAY_IN_MILLIS);
}
- public long getNetworkMaxHistory() {
- return getSecureLong(NETSTATS_NETWORK_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+ public long getGlobalAlertBytes() {
+ return getSecureLong(NETSTATS_GLOBAL_ALERT_BYTES, 2 * MB_IN_BYTES);
}
- public long getUidBucketDuration() {
- return getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS);
+ public boolean getSampleEnabled() {
+ return getSecureBoolean(NETSTATS_SAMPLE_ENABLED, true);
}
- public long getUidMaxHistory() {
- return getSecureLong(NETSTATS_UID_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+
+ public Config getDevConfig() {
+ return new Config(getSecureLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
+ getSecureLong(NETSTATS_DEV_PERSIST_BYTES, 2 * MB_IN_BYTES),
+ getSecureLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
+ getSecureLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
}
- public long getTagMaxHistory() {
- return getSecureLong(NETSTATS_TAG_MAX_HISTORY, 30 * DAY_IN_MILLIS);
+
+ public Config getUidConfig() {
+ return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES),
+ getSecureLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
}
- public long getTimeCacheMaxAge() {
- return DAY_IN_MILLIS;
+
+ public Config getUidTagConfig() {
+ return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES),
+ getSecureLong(NETSTATS_UID_ROTATE_AGE, 5 * DAY_IN_MILLIS),
+ getSecureLong(NETSTATS_UID_DELETE_AGE, 15 * DAY_IN_MILLIS));
}
}
}
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 8fc9a70..55fb038 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -17,13 +17,8 @@
package com.android.server.wm;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.Slog;
import android.view.Surface;
@@ -34,7 +29,8 @@ import android.view.animation.Transformation;
class ScreenRotationAnimation {
static final String TAG = "ScreenRotationAnimation";
- static final boolean DEBUG = false;
+ static final boolean DEBUG_STATE = false;
+ static final boolean DEBUG_TRANSFORMS = false;
static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
@@ -49,11 +45,51 @@ class ScreenRotationAnimation {
int mOriginalWidth, mOriginalHeight;
int mCurRotation;
- Animation mExitAnimation;
+ // For all animations, "exit" is for the UI elements that are going
+ // away (that is the snapshot of the old screen), and "enter" is for
+ // the new UI elements that are appearing (that is the active windows
+ // in their final orientation).
+
+ // The starting animation for the exiting and entering elements. This
+ // animation applies a transformation while the rotation is in progress.
+ // It is started immediately, before the new entering UI is ready.
+ Animation mStartExitAnimation;
+ final Transformation mStartExitTransformation = new Transformation();
+ Animation mStartEnterAnimation;
+ final Transformation mStartEnterTransformation = new Transformation();
+
+ // The finishing animation for the exiting and entering elements. This
+ // animation needs to undo the transformation of the starting animation.
+ // It starts running once the new rotation UI elements are ready to be
+ // displayed.
+ Animation mFinishExitAnimation;
+ final Transformation mFinishExitTransformation = new Transformation();
+ Animation mFinishEnterAnimation;
+ final Transformation mFinishEnterTransformation = new Transformation();
+
+ // The current active animation to move from the old to the new rotated
+ // state. Which animation is run here will depend on the old and new
+ // rotations.
+ Animation mRotateExitAnimation;
+ final Transformation mRotateExitTransformation = new Transformation();
+ Animation mRotateEnterAnimation;
+ final Transformation mRotateEnterTransformation = new Transformation();
+
+ // A previously running rotate animation. This will be used if we need
+ // to switch to a new rotation before finishing the previous one.
+ Animation mLastRotateExitAnimation;
+ final Transformation mLastRotateExitTransformation = new Transformation();
+ Animation mLastRotateEnterAnimation;
+ final Transformation mLastRotateEnterTransformation = new Transformation();
+
+ // Complete transformations being applied.
final Transformation mExitTransformation = new Transformation();
- Animation mEnterAnimation;
final Transformation mEnterTransformation = new Transformation();
+
boolean mStarted;
+ boolean mAnimRunning;
+ boolean mFinishAnimReady;
+ long mFinishAnimStartTime;
final Matrix mSnapshotInitialMatrix = new Matrix();
final Matrix mSnapshotFinalMatrix = new Matrix();
@@ -133,7 +169,7 @@ class ScreenRotationAnimation {
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
mSurface.setAlpha(alpha);
- if (DEBUG) {
+ if (DEBUG_TRANSFORMS) {
float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
float[] dstPnts = new float[4];
matrix.mapPoints(dstPnts, srcPnts);
@@ -167,7 +203,7 @@ class ScreenRotationAnimation {
}
// Must be called while in a transaction.
- public void setRotation(int rotation) {
+ private void setRotation(int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
@@ -176,46 +212,78 @@ class ScreenRotationAnimation {
int delta = deltaRotation(rotation, mSnapshotRotation);
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
- if (DEBUG) Slog.v(TAG, "**** ROTATION: " + delta);
+ if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
}
+ // Must be called while in a transaction.
+ public boolean setRotation(int rotation, SurfaceSession session,
+ long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
+ setRotation(rotation);
+ return startAnimation(session, maxAnimationDuration, animationScale,
+ finalWidth, finalHeight, false);
+ }
+
/**
* Returns true if animating.
*/
- public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
- float animationScale, int finalWidth, int finalHeight) {
+ private boolean startAnimation(SurfaceSession session, long maxAnimationDuration,
+ float animationScale, int finalWidth, int finalHeight, boolean dismissing) {
if (mSurface == null) {
// Can't do animation.
return false;
}
+ if (mStarted) {
+ return true;
+ }
+
+ mStarted = true;
+
+ boolean firstStart = false;
// Figure out how the screen has moved from the original rotation.
int delta = deltaRotation(mCurRotation, mOriginalRotation);
+ if (mFinishExitAnimation == null && (!dismissing || delta != Surface.ROTATION_0)) {
+ if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
+ firstStart = true;
+ mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_start_exit);
+ mStartEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_start_enter);
+ mFinishExitAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_finish_exit);
+ mFinishEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_finish_enter);
+ }
+
+ if (DEBUG_STATE) Slog.v(TAG, "Rotation delta: " + delta + " finalWidth="
+ + finalWidth + " finalHeight=" + finalHeight
+ + " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight);
+
switch (delta) {
case Surface.ROTATION_0:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_enter);
break;
case Surface.ROTATION_90:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
- mExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_exit);
- mEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_enter);
break;
}
@@ -224,35 +292,85 @@ class ScreenRotationAnimation {
// means to allow supplying the last and next size. In this definition
// "%p" is the original (let's call it "previous") size, and "%" is the
// screen's current/new size.
- mEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
- mExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
- mStarted = false;
+ if (firstStart) {
+ if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
+ mStartEnterAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mStartExitAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mFinishEnterAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ mFinishExitAnimation.initialize(finalWidth, finalHeight,
+ mOriginalWidth, mOriginalHeight);
+ }
+ mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mAnimRunning = false;
+ mFinishAnimReady = false;
+ mFinishAnimStartTime = -1;
+
+ if (firstStart) {
+ mStartExitAnimation.restrictDuration(maxAnimationDuration);
+ mStartExitAnimation.scaleCurrentDuration(animationScale);
+ mStartEnterAnimation.restrictDuration(maxAnimationDuration);
+ mStartEnterAnimation.scaleCurrentDuration(animationScale);
+ mFinishExitAnimation.restrictDuration(maxAnimationDuration);
+ mFinishExitAnimation.scaleCurrentDuration(animationScale);
+ mFinishEnterAnimation.restrictDuration(maxAnimationDuration);
+ mFinishEnterAnimation.scaleCurrentDuration(animationScale);
+ }
+ mRotateExitAnimation.restrictDuration(maxAnimationDuration);
+ mRotateExitAnimation.scaleCurrentDuration(animationScale);
+ mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
+ mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+
+ if (mBlackFrame == null) {
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
+ WindowManagerService.TAG,
+ ">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
+ Surface.openTransaction();
- mExitAnimation.restrictDuration(maxAnimationDuration);
- mExitAnimation.scaleCurrentDuration(animationScale);
- mEnterAnimation.restrictDuration(maxAnimationDuration);
- mEnterAnimation.scaleCurrentDuration(animationScale);
+ try {
+ Rect outer = new Rect(-finalWidth*1, -finalHeight*1, finalWidth*2, finalHeight*2);
+ Rect inner = new Rect(0, 0, finalWidth, finalHeight);
+ mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate black surface", e);
+ } finally {
+ Surface.closeTransaction();
+ if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS || DEBUG_STATE) Slog.i(
+ WindowManagerService.TAG,
+ "<<< CLOSE TRANSACTION ScreenRotationAnimation.startAnimation");
+ }
+ }
- if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
- ">>> OPEN TRANSACTION ScreenRotationAnimation.dismiss");
- Surface.openTransaction();
+ return true;
+ }
- try {
- Rect outer = new Rect(-finalWidth, -finalHeight, finalWidth * 2, finalHeight * 2);
- Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER);
- } catch (Surface.OutOfResourcesException e) {
- Slog.w(TAG, "Unable to allocate black surface", e);
- } finally {
- Surface.closeTransaction();
- if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
- "<<< CLOSE TRANSACTION ScreenRotationAnimation.dismiss");
+ /**
+ * Returns true if animating.
+ */
+ public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
+ float animationScale, int finalWidth, int finalHeight) {
+ if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
+ if (mSurface == null) {
+ // Can't do animation.
+ return false;
}
-
+ if (!mStarted) {
+ startAnimation(session, maxAnimationDuration, animationScale, finalWidth, finalHeight,
+ true);
+ }
+ if (!mStarted) {
+ return false;
+ }
+ if (DEBUG_STATE) Slog.v(TAG, "Setting mFinishAnimReady = true");
+ mFinishAnimReady = true;
return true;
}
public void kill() {
+ if (DEBUG_STATE) Slog.v(TAG, "Kill!");
if (mSurface != null) {
if (WindowManagerService.SHOW_TRANSACTIONS ||
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
@@ -262,74 +380,198 @@ class ScreenRotationAnimation {
}
if (mBlackFrame != null) {
mBlackFrame.kill();
+ mBlackFrame = null;
+ }
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
+ }
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
}
- if (mExitAnimation != null) {
- mExitAnimation.cancel();
- mExitAnimation = null;
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
}
- if (mEnterAnimation != null) {
- mEnterAnimation.cancel();
- mEnterAnimation = null;
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ if (mRotateExitAnimation != null) {
+ mRotateExitAnimation.cancel();
+ mRotateExitAnimation = null;
+ }
+ if (mRotateEnterAnimation != null) {
+ mRotateEnterAnimation.cancel();
+ mRotateEnterAnimation = null;
}
}
public boolean isAnimating() {
- return mEnterAnimation != null || mExitAnimation != null;
+ return mStartEnterAnimation != null || mStartExitAnimation != null
+ && mFinishEnterAnimation != null || mFinishExitAnimation != null
+ && mRotateEnterAnimation != null || mRotateExitAnimation != null;
}
public boolean stepAnimation(long now) {
- if (mEnterAnimation == null && mExitAnimation == null) {
+ if (!isAnimating()) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
return false;
}
- if (!mStarted) {
- if (mEnterAnimation != null) {
- mEnterAnimation.setStartTime(now);
+ if (!mAnimRunning) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate");
+ if (mStartEnterAnimation != null) {
+ mStartEnterAnimation.setStartTime(now);
}
- if (mExitAnimation != null) {
- mExitAnimation.setStartTime(now);
+ if (mStartExitAnimation != null) {
+ mStartExitAnimation.setStartTime(now);
}
- mStarted = true;
- }
-
- mExitTransformation.clear();
- boolean moreExit = false;
- if (mExitAnimation != null) {
- moreExit = mExitAnimation.getTransformation(now, mExitTransformation);
- if (DEBUG) Slog.v(TAG, "Stepped exit: " + mExitTransformation);
- if (!moreExit) {
- if (DEBUG) Slog.v(TAG, "Exit animation done!");
- mExitAnimation.cancel();
- mExitAnimation = null;
- mExitTransformation.clear();
- if (mSurface != null) {
- mSurface.hide();
- }
+ if (mFinishEnterAnimation != null) {
+ mFinishEnterAnimation.setStartTime(0);
+ }
+ if (mFinishExitAnimation != null) {
+ mFinishExitAnimation.setStartTime(0);
+ }
+ if (mRotateEnterAnimation != null) {
+ mRotateEnterAnimation.setStartTime(now);
}
+ if (mRotateExitAnimation != null) {
+ mRotateExitAnimation.setStartTime(now);
+ }
+ mAnimRunning = true;
}
- mEnterTransformation.clear();
- boolean moreEnter = false;
- if (mEnterAnimation != null) {
- moreEnter = mEnterAnimation.getTransformation(now, mEnterTransformation);
- if (!moreEnter) {
- mEnterAnimation.cancel();
- mEnterAnimation = null;
- mEnterTransformation.clear();
- if (mBlackFrame != null) {
- mBlackFrame.hide();
- }
- } else {
- if (mBlackFrame != null) {
- mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
- }
+ if (mFinishAnimReady && mFinishAnimStartTime < 0) {
+ if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
+ mFinishAnimStartTime = now;
+ }
+
+ // If the start animation is no longer running, we want to keep its
+ // transformation intact until the finish animation also completes.
+
+ boolean moreStartExit = false;
+ if (mStartExitAnimation != null) {
+ mStartExitTransformation.clear();
+ moreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
+ if (!moreStartExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Start exit animation done!");
+ mStartExitAnimation.cancel();
+ mStartExitAnimation = null;
+ }
+ }
+
+ boolean moreStartEnter = false;
+ if (mStartEnterAnimation != null) {
+ mStartEnterTransformation.clear();
+ moreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
+ if (!moreStartEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Start enter animation done!");
+ mStartEnterAnimation.cancel();
+ mStartEnterAnimation = null;
+ }
+ }
+
+ long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0;
+ if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow);
+
+ mFinishExitTransformation.clear();
+ boolean moreFinishExit = false;
+ if (mFinishExitAnimation != null) {
+ moreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
+ if (!moreStartExit && !moreFinishExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Finish exit animation done, clearing start/finish anims!");
+ mStartExitTransformation.clear();
+ mFinishExitAnimation.cancel();
+ mFinishExitAnimation = null;
+ mFinishExitTransformation.clear();
+ }
+ }
+
+ mFinishEnterTransformation.clear();
+ boolean moreFinishEnter = false;
+ if (mFinishEnterAnimation != null) {
+ moreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
+ if (!moreStartEnter && !moreFinishEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Finish enter animation done, clearing start/finish anims!");
+ mStartEnterTransformation.clear();
+ mFinishEnterAnimation.cancel();
+ mFinishEnterAnimation = null;
+ mFinishEnterTransformation.clear();
+ }
+ }
+
+ mRotateExitTransformation.clear();
+ boolean moreRotateExit = false;
+ if (mRotateExitAnimation != null) {
+ moreRotateExit = mRotateExitAnimation.getTransformation(now, mRotateExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate exit: " + mRotateExitTransformation);
+ }
+
+ if (!moreFinishExit && !moreRotateExit) {
+ if (DEBUG_STATE) Slog.v(TAG, "Rotate exit animation done!");
+ mRotateExitAnimation.cancel();
+ mRotateExitAnimation = null;
+ mRotateExitTransformation.clear();
+ }
+
+ mRotateEnterTransformation.clear();
+ boolean moreRotateEnter = false;
+ if (mRotateEnterAnimation != null) {
+ moreRotateEnter = mRotateEnterAnimation.getTransformation(now, mRotateEnterTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation);
+ }
+
+ if (!moreFinishEnter && !moreRotateEnter) {
+ if (DEBUG_STATE) Slog.v(TAG, "Rotate enter animation done!");
+ mRotateEnterAnimation.cancel();
+ mRotateEnterAnimation = null;
+ mRotateEnterTransformation.clear();
+ }
+
+ mExitTransformation.set(mRotateExitTransformation);
+ mExitTransformation.compose(mStartExitTransformation);
+ mExitTransformation.compose(mFinishExitTransformation);
+
+ mEnterTransformation.set(mRotateEnterTransformation);
+ mEnterTransformation.compose(mStartEnterTransformation);
+ mEnterTransformation.compose(mFinishEnterTransformation);
+
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation);
+ if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation);
+
+ if (!moreStartExit && !moreFinishExit && !moreRotateExit) {
+ if (mSurface != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface");
+ mSurface.hide();
+ }
+ }
+
+ if (!moreStartEnter && !moreFinishEnter && !moreRotateEnter) {
+ if (mBlackFrame != null) {
+ if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, hiding black frame");
+ mBlackFrame.hide();
+ }
+ } else {
+ if (mBlackFrame != null) {
+ mBlackFrame.setMatrix(mEnterTransformation.getMatrix());
}
}
mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
- return moreEnter || moreExit;
+ final boolean more = moreStartEnter || moreStartExit || moreFinishEnter || moreFinishExit
+ || moreRotateEnter || moreRotateExit || !mFinishAnimReady;
+
+ if (DEBUG_STATE) Slog.v(TAG, "Step: more=" + more);
+
+ return more;
}
public Transformation getEnterTransformation() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index a702ce8..19d94a1 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -733,7 +733,7 @@ public class WindowManagerService extends IWindowManager.Stub
//Looper.myLooper().setMessageLogging(new LogPrinter(
// Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM));
android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_DISPLAY);
+ android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
mPolicy.init(mContext, mService, mService, mPM);
@@ -2543,8 +2543,12 @@ public class WindowManagerService extends IWindowManager.Stub
if (win == null) {
return 0;
}
- win.mRequestedWidth = requestedWidth;
- win.mRequestedHeight = requestedHeight;
+ if (win.mRequestedWidth != requestedWidth
+ || win.mRequestedHeight != requestedHeight) {
+ win.mLayoutNeeded = true;
+ win.mRequestedWidth = requestedWidth;
+ win.mRequestedHeight = requestedHeight;
+ }
if (attrs != null && seq == win.mSeq) {
win.mSystemUiVisibility = systemUiVisibility;
}
@@ -2565,6 +2569,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
flagChanges = win.mAttrs.flags ^= attrs.flags;
attrChanges = win.mAttrs.copyFrom(attrs);
+ if ((attrChanges&WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) {
+ win.mLayoutNeeded = true;
+ }
}
if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": " + win.mAttrs);
@@ -3446,7 +3453,7 @@ public class WindowManagerService extends IWindowManager.Stub
// the value of the previous configuration.
mTempConfiguration.setToDefaults();
mTempConfiguration.fontScale = currentConfig.fontScale;
- if (computeNewConfigurationLocked(mTempConfiguration)) {
+ if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
mLayoutNeeded = true;
@@ -5362,6 +5369,14 @@ public class WindowManagerService extends IWindowManager.Stub
startFreezingDisplayLocked(inTransaction);
mInputManager.setDisplayOrientation(0, rotation);
+ // We need to update our screen size information to match the new
+ // rotation. Note that this is redundant with the later call to
+ // sendNewConfiguration() that must be called after this function
+ // returns... however we need to do the screen size part of that
+ // before then so we have the correct size to use when initializiation
+ // the rotation animation for the new rotation.
+ computeScreenConfigurationLocked(null);
+
if (!inTransaction) {
if (SHOW_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION setRotationUnchecked");
@@ -5372,7 +5387,11 @@ public class WindowManagerService extends IWindowManager.Stub
// it doesn't support hardware OpenGL emulation yet.
if (CUSTOM_SCREEN_ROTATION && mScreenRotationAnimation != null
&& mScreenRotationAnimation.hasScreenshot()) {
- mScreenRotationAnimation.setRotation(rotation);
+ if (mScreenRotationAnimation.setRotation(rotation, mFxSession,
+ MAX_ANIMATION_DURATION, mTransitionAnimationScale,
+ mCurDisplayWidth, mCurDisplayHeight)) {
+ requestAnimationLocked(0);
+ }
}
Surface.setOrientation(0, rotation);
} finally {
@@ -5860,7 +5879,7 @@ public class WindowManagerService extends IWindowManager.Stub
Configuration computeNewConfigurationLocked() {
Configuration config = new Configuration();
config.fontScale = 0;
- if (!computeNewConfigurationLocked(config)) {
+ if (!computeScreenConfigurationLocked(config)) {
return null;
}
return config;
@@ -6011,12 +6030,10 @@ public class WindowManagerService extends IWindowManager.Stub
return sw;
}
- boolean computeNewConfigurationLocked(Configuration config) {
+ boolean computeScreenConfigurationLocked(Configuration config) {
if (mDisplay == null) {
return false;
}
-
- mInputManager.getInputConfiguration(config);
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -6050,13 +6067,17 @@ public class WindowManagerService extends IWindowManager.Stub
final int dw = mCurDisplayWidth;
final int dh = mCurDisplayHeight;
- int orientation = Configuration.ORIENTATION_SQUARE;
- if (dw < dh) {
- orientation = Configuration.ORIENTATION_PORTRAIT;
- } else if (dw > dh) {
- orientation = Configuration.ORIENTATION_LANDSCAPE;
+ if (config != null) {
+ mInputManager.getInputConfiguration(config);
+
+ int orientation = Configuration.ORIENTATION_SQUARE;
+ if (dw < dh) {
+ orientation = Configuration.ORIENTATION_PORTRAIT;
+ } else if (dw > dh) {
+ orientation = Configuration.ORIENTATION_LANDSCAPE;
+ }
+ config.orientation = orientation;
}
- config.orientation = orientation;
// Update real display metrics.
mDisplay.getMetricsWithSize(mRealDisplayMetrics, mCurDisplayWidth, mCurDisplayHeight);
@@ -6078,36 +6099,39 @@ public class WindowManagerService extends IWindowManager.Stub
mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(dm,
mCompatDisplayMetrics);
- config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation)
- / dm.density);
- config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation)
- / dm.density);
- computeSmallestWidthAndScreenLayout(rotated, dw, dh, dm.density, config);
+ if (config != null) {
+ config.screenWidthDp = (int)(mPolicy.getConfigDisplayWidth(dw, dh, mRotation)
+ / dm.density);
+ config.screenHeightDp = (int)(mPolicy.getConfigDisplayHeight(dw, dh, mRotation)
+ / dm.density);
+ computeSmallestWidthAndScreenLayout(rotated, dw, dh, dm.density, config);
- config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
- config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
- config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);
+ config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
+ config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
+ config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);
- // Determine whether a hard keyboard is available and enabled.
- boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
- if (hardKeyboardAvailable != mHardKeyboardAvailable) {
- mHardKeyboardAvailable = hardKeyboardAvailable;
- mHardKeyboardEnabled = hardKeyboardAvailable;
+ // Determine whether a hard keyboard is available and enabled.
+ boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
+ if (hardKeyboardAvailable != mHardKeyboardAvailable) {
+ mHardKeyboardAvailable = hardKeyboardAvailable;
+ mHardKeyboardEnabled = hardKeyboardAvailable;
- mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- }
- if (!mHardKeyboardEnabled) {
- config.keyboard = Configuration.KEYBOARD_NOKEYS;
+ mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+ }
+ if (!mHardKeyboardEnabled) {
+ config.keyboard = Configuration.KEYBOARD_NOKEYS;
+ }
+
+ // Update value of keyboardHidden, hardKeyboardHidden and navigationHidden
+ // based on whether a hard or soft keyboard is present, whether navigation keys
+ // are present and the lid switch state.
+ config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
+ config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
+ config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
+ mPolicy.adjustConfigurationLw(config);
}
- // Update value of keyboardHidden, hardKeyboardHidden and navigationHidden
- // based on whether a hard or soft keyboard is present, whether navigation keys
- // are present and the lid switch state.
- config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
- config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
- config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
- mPolicy.adjustConfigurationLw(config);
return true;
}
@@ -7114,7 +7138,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean configChanged = updateOrientationFromAppTokensLocked(false);
mTempConfiguration.setToDefaults();
mTempConfiguration.fontScale = mCurConfiguration.fontScale;
- if (computeNewConfigurationLocked(mTempConfiguration)) {
+ if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (mCurConfiguration.diff(mTempConfiguration) != 0) {
configChanged = true;
}
@@ -7443,12 +7467,13 @@ public class WindowManagerService extends IWindowManager.Stub
// if they want. (We do the normal layout for INVISIBLE
// windows, since that means "perform layout as normal,
// just don't display").
- if (!gone || !win.mHaveFrame) {
+ if (!gone || !win.mHaveFrame || win.mLayoutNeeded) {
if (!win.mLayoutAttached) {
if (initial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
win.mContentChanged = false;
}
+ win.mLayoutNeeded = false;
win.prelayout();
mPolicy.layoutWindowLw(win, win.mAttrs, null);
win.mLayoutSeq = seq;
@@ -7480,11 +7505,12 @@ public class WindowManagerService extends IWindowManager.Stub
// windows, since that means "perform layout as normal,
// just don't display").
if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)
- || !win.mHaveFrame) {
+ || !win.mHaveFrame || win.mLayoutNeeded) {
if (initial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
win.mContentChanged = false;
}
+ win.mLayoutNeeded = false;
win.prelayout();
mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
win.mLayoutSeq = seq;
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 9118381..1067cad 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -238,6 +238,12 @@ final class WindowState implements WindowManagerPolicy.WindowState {
// we can give the window focus before waiting for the relayout.
boolean mRelayoutCalled;
+ // If the application has called relayout() with changes that can
+ // impact its window's size, we need to perform a layout pass on it
+ // even if it is not currently visible for layout. This is set
+ // when in that case until the layout is done.
+ boolean mLayoutNeeded;
+
// This is set after the Surface has been created but before the
// window has been drawn. During this time the surface is hidden.
boolean mDrawPending;
@@ -1449,7 +1455,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mViewVisibility == View.GONE
|| !mRelayoutCalled
|| (atoken == null && mRootToken.hidden)
- || (atoken != null && atoken.hiddenRequested)
+ || (atoken != null && (atoken.hiddenRequested || atoken.hidden))
|| mAttachedHidden
|| mExiting || mDestroying;
}
@@ -1728,8 +1734,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
pw.print(mPolicyVisibilityAfterAnim);
pw.print(" mAttachedHidden="); pw.println(mAttachedHidden);
}
- if (!mRelayoutCalled) {
- pw.print(prefix); pw.print("mRelayoutCalled="); pw.println(mRelayoutCalled);
+ if (!mRelayoutCalled || mLayoutNeeded) {
+ pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled);
+ pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
}
if (mSurfaceResized || mSurfaceDestroyDeferred) {
pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
diff --git a/services/tests/servicestests/res/raw/netstats_uid_v4 b/services/tests/servicestests/res/raw/netstats_uid_v4
new file mode 100644
index 0000000..e75fc1c
--- /dev/null
+++ b/services/tests/servicestests/res/raw/netstats_uid_v4
Binary files differ
diff --git a/services/tests/servicestests/res/raw/netstats_v1 b/services/tests/servicestests/res/raw/netstats_v1
new file mode 100644
index 0000000..e80860a
--- /dev/null
+++ b/services/tests/servicestests/res/raw/netstats_v1
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 90b5a2e..8f5e77e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -39,6 +39,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -63,10 +64,12 @@ import android.os.INetworkManagementService;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.util.TrustedTime;
import com.android.server.net.NetworkStatsService;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
+import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
import org.easymock.Capture;
import org.easymock.EasyMock;
@@ -89,6 +92,10 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
private static final String IMSI_1 = "310004";
private static final String IMSI_2 = "310260";
+ private static final long KB_IN_BYTES = 1024;
+ private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
+ private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
+
private static NetworkTemplate sTemplateWifi = buildTemplateWifi();
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
@@ -282,13 +289,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
verifyAndReset();
- // talk with zombie service to assert stats have gone; and assert that
- // we persisted them to file.
- expectDefaultSettings();
- replay();
- assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
- verifyAndReset();
-
assertStatsFilesExist(true);
// boot through serviceReady() again
@@ -319,6 +319,8 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
}
+ // TODO: simulate reboot to test bucket resize
+ @Suppress
public void testStatsBucketResize() throws Exception {
NetworkStatsHistory history = null;
@@ -602,7 +604,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
verifyAndReset();
-
}
public void testSummaryForAllUid() throws Exception {
@@ -755,11 +756,15 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L));
+
+ final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
final String[] tetherIfacePairs = new String[] { TEST_IFACE, "wlan0" };
- expectNetworkStatsPoll(tetherIfacePairs, new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L));
+ final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L, 0L);
+
+ expectNetworkStatsUidDetail(uidStats, tetherIfacePairs, tetherStats);
+ expectNetworkStatsPoll();
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
@@ -808,6 +813,9 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
private void expectNetworkState(NetworkState... state) throws Exception {
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+
+ final LinkProperties linkProp = state.length > 0 ? state[0].linkProperties : null;
+ expect(mConnManager.getActiveLinkProperties()).andReturn(linkProp).atLeastOnce();
}
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
@@ -815,23 +823,35 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
}
private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
+ expectNetworkStatsUidDetail(detail, new String[0], new NetworkStats(0L, 0));
+ }
+
+ private void expectNetworkStatsUidDetail(
+ NetworkStats detail, String[] tetherIfacePairs, NetworkStats tetherStats)
+ throws Exception {
expect(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL))).andReturn(detail).atLeastOnce();
+
+ // also include tethering details, since they are folded into UID
+ expect(mConnManager.getTetheredIfacePairs()).andReturn(tetherIfacePairs).atLeastOnce();
+ expect(mNetManager.getNetworkStatsTethering(aryEq(tetherIfacePairs)))
+ .andReturn(tetherStats).atLeastOnce();
}
private void expectDefaultSettings() throws Exception {
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
}
- private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory)
+ private void expectSettings(long persistBytes, long bucketDuration, long deleteAge)
throws Exception {
expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
- expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes();
- expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes();
- expect(mSettings.getNetworkMaxHistory()).andReturn(maxHistory).anyTimes();
- expect(mSettings.getUidBucketDuration()).andReturn(bucketDuration).anyTimes();
- expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes();
- expect(mSettings.getTagMaxHistory()).andReturn(maxHistory).anyTimes();
expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
+ expect(mSettings.getGlobalAlertBytes()).andReturn(MB_IN_BYTES).anyTimes();
+ expect(mSettings.getSampleEnabled()).andReturn(true).anyTimes();
+
+ final Config config = new Config(bucketDuration, persistBytes, deleteAge, deleteAge);
+ expect(mSettings.getDevConfig()).andReturn(config).anyTimes();
+ expect(mSettings.getUidConfig()).andReturn(config).anyTimes();
+ expect(mSettings.getUidTagConfig()).andReturn(config).anyTimes();
}
private void expectCurrentTime() throws Exception {
@@ -843,27 +863,16 @@ public class NetworkStatsServiceTest extends AndroidTestCase {
}
private void expectNetworkStatsPoll() throws Exception {
- expectNetworkStatsPoll(new String[0], new NetworkStats(getElapsedRealtime(), 0));
- }
-
- private void expectNetworkStatsPoll(String[] tetherIfacePairs, NetworkStats tetherStats)
- throws Exception {
mNetManager.setGlobalAlert(anyLong());
expectLastCall().anyTimes();
- expect(mConnManager.getTetheredIfacePairs()).andReturn(tetherIfacePairs).anyTimes();
- expect(mNetManager.getNetworkStatsTethering(eq(tetherIfacePairs)))
- .andReturn(tetherStats).anyTimes();
}
private void assertStatsFilesExist(boolean exist) {
- final File networkFile = new File(mStatsDir, "netstats.bin");
- final File uidFile = new File(mStatsDir, "netstats_uid.bin");
+ final File basePath = new File(mStatsDir, "netstats");
if (exist) {
- assertTrue(networkFile.exists());
- assertTrue(uidFile.exists());
+ assertTrue(basePath.list().length > 0);
} else {
- assertFalse(networkFile.exists());
- assertFalse(uidFile.exists());
+ assertTrue(basePath.list().length == 0);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
new file mode 100644
index 0000000..7f05f56
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 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.net;
+
+import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.NetworkIdentity;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.frameworks.servicestests.R;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+/**
+ * Tests for {@link NetworkStatsCollection}.
+ */
+@MediumTest
+public class NetworkStatsCollectionTest extends AndroidTestCase {
+
+ private static final String TEST_FILE = "test.bin";
+ private static final String TEST_IMSI = "310260000000000";
+
+ public void testReadLegacyNetwork() throws Exception {
+ final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
+ stageFile(R.raw.netstats_v1, testFile);
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyNetwork(testFile);
+
+ // verify that history read correctly
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 636014522L, 709291L, 88037144L, 518820L);
+
+ // now export into a unified format
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ collection.write(new DataOutputStream(bos));
+
+ // clear structure completely
+ collection.reset();
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 0L, 0L, 0L, 0L);
+
+ // and read back into structure, verifying that totals are same
+ collection.read(new ByteArrayInputStream(bos.toByteArray()));
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 636014522L, 709291L, 88037144L, 518820L);
+ }
+
+ public void testReadLegacyUid() throws Exception {
+ final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
+ stageFile(R.raw.netstats_uid_v4, testFile);
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyUid(testFile, false);
+
+ // verify that history read correctly
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 637073904L, 711398L, 88342093L, 521006L);
+
+ // now export into a unified format
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ collection.write(new DataOutputStream(bos));
+
+ // clear structure completely
+ collection.reset();
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 0L, 0L, 0L, 0L);
+
+ // and read back into structure, verifying that totals are same
+ collection.read(new ByteArrayInputStream(bos.toByteArray()));
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
+ 637073904L, 711398L, 88342093L, 521006L);
+ }
+
+ public void testReadLegacyUidTags() throws Exception {
+ final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
+ stageFile(R.raw.netstats_uid_v4, testFile);
+
+ final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ collection.readLegacyUid(testFile, true);
+
+ // verify that history read correctly
+ assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
+ 77017831L, 100995L, 35436758L, 92344L);
+
+ // now export into a unified format
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ collection.write(new DataOutputStream(bos));
+
+ // clear structure completely
+ collection.reset();
+ assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
+ 0L, 0L, 0L, 0L);
+
+ // and read back into structure, verifying that totals are same
+ collection.read(new ByteArrayInputStream(bos.toByteArray()));
+ assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
+ 77017831L, 100995L, 35436758L, 92344L);
+ }
+
+ /**
+ * Copy a {@link Resources#openRawResource(int)} into {@link File} for
+ * testing purposes.
+ */
+ private void stageFile(int rawId, File file) throws Exception {
+ new File(file.getParent()).mkdirs();
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = getContext().getResources().openRawResource(rawId);
+ out = new FileOutputStream(file);
+ Streams.copy(in, out);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ public static NetworkIdentitySet buildWifiIdent() {
+ final NetworkIdentitySet set = new NetworkIdentitySet();
+ set.add(new NetworkIdentity(ConnectivityManager.TYPE_WIFI, 0, null, false));
+ return set;
+ }
+
+ private static void assertSummaryTotal(NetworkStatsCollection collection,
+ NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ final NetworkStats.Entry entry = collection.getSummary(
+ template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
+ assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
+ }
+
+ private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
+ NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ final NetworkStats.Entry entry = collection.getSummary(
+ template, Long.MIN_VALUE, Long.MAX_VALUE).getTotalIncludingTags(null);
+ assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
+ }
+
+ private static void assertEntry(
+ NetworkStats.Entry entry, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+ assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+ assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+ assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index 97fb73d..2b37072 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -427,11 +427,11 @@ public class CatService extends Handler implements AppInterface {
}
break;
default:
- CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
+ CatLog.d(this, "encodeOptionalTags() Unsupported Cmd details=" + cmdDet);
break;
}
} else {
- CatLog.d(this, "encodeOptionalTags() bad Cmd:" + cmdDet.typeOfCommand);
+ CatLog.d(this, "encodeOptionalTags() bad Cmd details=" + cmdDet);
}
}
diff --git a/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
index bd6b7d1..22cd5a4 100644
--- a/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
+++ b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -96,7 +96,6 @@ class ComprehensionTlv {
startIndex = ctlv.mValueIndex + ctlv.mLength;
} else {
CatLog.d(LOG_TAG, "decodeMany: ctlv is null, stop decoding");
- items.clear();
break;
}
}
diff --git a/tests/ActivityTests/res/anim/slow_enter.xml b/tests/ActivityTests/res/anim/slow_enter.xml
new file mode 100644
index 0000000..0309643
--- /dev/null
+++ b/tests/ActivityTests/res/anim/slow_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false" >
+ <scale android:fromXScale="0.9" android:toXScale="1.5"
+ android:fromYScale="0.9" android:toYScale="1.5"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/slow_enter"
+ android:duration="40000" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:interpolator="@android:interpolator/decelerate_cubic"
+ android:duration="1000" />
+</set>
diff --git a/tests/ActivityTests/res/anim/slow_exit.xml b/tests/ActivityTests/res/anim/slow_exit.xml
new file mode 100644
index 0000000..6cd3114
--- /dev/null
+++ b/tests/ActivityTests/res/anim/slow_exit.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false" >
+ <scale android:fromXScale="1.0" android:toXScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.9"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@android:interpolator/decelerate_quint"
+ android:duration="300" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@android:interpolator/decelerate_cubic"
+ android:duration="300"/>
+</set>
diff --git a/tests/ActivityTests/res/interpolator/slow_enter.xml b/tests/ActivityTests/res/interpolator/slow_enter.xml
new file mode 100644
index 0000000..ddab1aa
--- /dev/null
+++ b/tests/ActivityTests/res/interpolator/slow_enter.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:cycles="10" />
diff --git a/tests/ActivityTests/res/values/themes.xml b/tests/ActivityTests/res/values/themes.xml
new file mode 100644
index 0000000..67f5938
--- /dev/null
+++ b/tests/ActivityTests/res/values/themes.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<resources>
+ <style name="SlowDialog" parent="@android:style/Theme.Holo.Dialog">
+ <item name="android:windowAnimationStyle">@style/SlowDialog</item>
+ </style>
+ <style name="SlowDialog">
+ <item name="android:windowEnterAnimation">@anim/slow_enter</item>
+ <item name="android:windowExitAnimation">@anim/slow_exit</item>
+ </style>
+</resources>
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 583c13c..ae42e29 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -22,6 +22,7 @@ import java.util.List;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.AlertDialog;
import android.app.Application;
import android.content.ActivityNotFoundException;
import android.os.Bundle;
@@ -35,6 +36,8 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ScrollView;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -101,6 +104,20 @@ public class ActivityTestMain extends Activity {
}
@Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add("Animate!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override public boolean onMenuItemClick(MenuItem item) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(ActivityTestMain.this,
+ R.style.SlowDialog);
+ builder.setTitle("This is a title");
+ builder.show();
+ return true;
+ }
+ });
+ return true;
+ }
+
+ @Override
protected void onStart() {
super.onStart();
buildUi();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 112c606..5bbcce3 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -31,6 +31,15 @@
android:hardwareAccelerated="true">
<activity
+ android:name="PaintDrawFilterActivity"
+ android:label="_DrawFilter">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="DisplayListLayersActivity"
android:label="__DisplayListLayers">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java
new file mode 100644
index 0000000..8523272
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PaintDrawFilterActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new CustomTextView(this));
+ }
+
+ static class CustomTextView extends View {
+ private final Paint mMediumPaint;
+ private final PaintFlagsDrawFilter mDrawFilter;
+
+ CustomTextView(Context c) {
+ super(c);
+
+ mMediumPaint = new Paint();
+ mMediumPaint.setAntiAlias(true);
+ mMediumPaint.setColor(0xff000000);
+ mMediumPaint.setFakeBoldText(true);
+ mMediumPaint.setTextSize(24.0f);
+
+ mDrawFilter = new PaintFlagsDrawFilter(
+ Paint.FAKE_BOLD_TEXT_FLAG, Paint.UNDERLINE_TEXT_FLAG);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.setDrawFilter(null);
+ canvas.drawText("Hello OpenGL renderer!", 100, 120, mMediumPaint);
+ canvas.setDrawFilter(mDrawFilter);
+ canvas.drawText("Hello OpenGL renderer!", 100, 220, mMediumPaint);
+ }
+ }
+}
diff --git a/tests/TileBenchmark/res/values/strings.xml b/tests/TileBenchmark/res/values/strings.xml
index 5af52dc..6c7055b 100644
--- a/tests/TileBenchmark/res/values/strings.xml
+++ b/tests/TileBenchmark/res/values/strings.xml
@@ -49,8 +49,9 @@
<!-- Drop down menu entry - automatically scroll to the end of the page
with scrollBy() [CHAR LIMIT=15] -->
<string name="movement_auto_scroll">Auto-scroll</string>
- <!-- Drop down menu entry - [CHAR LIMIT=15] -->
- <string name="movement_auto_fling">Auto-fling</string>
+ <!-- Drop down menu entry - automatically record for a set time before
+ stopping [CHAR LIMIT=15] -->
+ <string name="movement_timed">Timed</string>
<!-- Drop down menu entry - manually navigate the page(s), hit 'capture'
button [CHAR LIMIT=15] -->
<string name="movement_manual">Manual</string>
@@ -67,14 +68,21 @@
<!-- 75th percentile - 75% of frames fall below this value [CHAR LIMIT=12]
-->
<string name="percentile_75">75%ile</string>
+ <!-- standard deviation [CHAR LIMIT=12] -->
+ <string name="std_dev">StdDev</string>
+ <!-- mean [CHAR LIMIT=12] -->
+ <string name="mean">mean</string>
+
+
+
<!-- Frame rate [CHAR LIMIT=15] -->
<string name="frames_per_second">Frames/sec</string>
<!-- Portion of viewport covered by good tiles [CHAR LIMIT=15] -->
<string name="viewport_coverage">Coverage</string>
<!-- Milliseconds taken to inval, and re-render the page [CHAR LIMIT=15] -->
<string name="render_millis">RenderMillis</string>
- <!-- Number of rendering stalls while running the test [CHAR LIMIT=15] -->
- <string name="render_stalls">Stalls</string>
+ <!-- Animation Framerate [CHAR LIMIT=15] -->
+ <string name="animation_framerate">AnimFramerate</string>
<!-- Format string for stat value overlay [CHAR LIMIT=15] -->
<string name="format_stat">%4.4f</string>
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java
index cc39b75..6356cc1 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java
@@ -27,17 +27,57 @@ import android.os.Bundle;
import android.os.Environment;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import android.webkit.WebSettings;
+import android.widget.Spinner;
public class PerformanceTest extends
ActivityInstrumentationTestCase2<ProfileActivity> {
+ public static class AnimStat {
+ double aggVal = 0;
+ double aggSqrVal = 0;
+ double count = 0;
+ }
+
private class StatAggregator extends PlaybackGraphs {
private HashMap<String, Double> mDataMap = new HashMap<String, Double>();
+ private HashMap<String, AnimStat> mAnimDataMap = new HashMap<String, AnimStat>();
private int mCount = 0;
+
public void aggregate() {
+ boolean inAnimTests = mAnimTests != null;
+ Resources resources = mWeb.getResources();
+ String animFramerateString = resources.getString(R.string.animation_framerate);
+ for (Map.Entry<String, Double> e : mSingleStats.entrySet()) {
+ String name = e.getKey();
+ if (inAnimTests) {
+ if (name.equals(animFramerateString)) {
+ // in animation testing phase, record animation framerate and aggregate
+ // stats, differentiating on values of mAnimTestNr and mDoubleBuffering
+ String fullName = ANIM_TEST_NAMES[mAnimTestNr] + " " + name;
+ fullName += mDoubleBuffering ? " tiled" : " webkit";
+
+ if (!mAnimDataMap.containsKey(fullName)) {
+ mAnimDataMap.put(fullName, new AnimStat());
+ }
+ AnimStat statVals = mAnimDataMap.get(fullName);
+ statVals.aggVal += e.getValue();
+ statVals.aggSqrVal += e.getValue() * e.getValue();
+ statVals.count += 1;
+ }
+ } else {
+ double aggVal = mDataMap.containsKey(name)
+ ? mDataMap.get(name) : 0;
+ mDataMap.put(name, aggVal + e.getValue());
+ }
+ }
+
+ if (inAnimTests) {
+ return;
+ }
+
mCount++;
- Resources resources = mView.getResources();
for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) {
for (int statIndex = 0; statIndex < Stats.length; statIndex++) {
String metricLabel = resources.getString(
@@ -53,34 +93,47 @@ public class PerformanceTest extends
mDataMap.put(label, aggVal);
}
}
- for (Map.Entry<String, Double> e : mSingleStats.entrySet()) {
- double aggVal = mDataMap.containsKey(e.getKey())
- ? mDataMap.get(e.getKey()) : 0;
- mDataMap.put(e.getKey(), aggVal + e.getValue());
- }
+
}
+ // build the final bundle of results
public Bundle getBundle() {
Bundle b = new Bundle();
- int count = 0 == mCount ? Integer.MAX_VALUE : mCount;
+ int count = (0 == mCount) ? Integer.MAX_VALUE : mCount;
for (Map.Entry<String, Double> e : mDataMap.entrySet()) {
b.putDouble(e.getKey(), e.getValue() / count);
}
+
+ for (Map.Entry<String, AnimStat> e : mAnimDataMap.entrySet()) {
+ String statName = e.getKey();
+ AnimStat statVals = e.getValue();
+
+ double avg = statVals.aggVal/statVals.count;
+ double stdDev = Math.sqrt((statVals.aggSqrVal / statVals.count) - avg * avg);
+
+ b.putDouble(statName, avg);
+ b.putDouble(statName + " STD DEV", stdDev);
+ }
+
return b;
}
}
ProfileActivity mActivity;
- ProfiledWebView mView;
- StatAggregator mStats = new StatAggregator();
+ ProfiledWebView mWeb;
+ Spinner mMovementSpinner;
+ StatAggregator mStats;
private static final String LOGTAG = "PerformanceTest";
private static final String TEST_LOCATION = "webkit/page_cycler";
private static final String URL_PREFIX = "file://";
private static final String URL_POSTFIX = "/index.html?skip=true";
private static final int MAX_ITERATIONS = 4;
- private static final String TEST_DIRS[] = {
- "alexa25_2011"//, "alexa_us", "android", "dom", "intl2", "moz", "moz2"
+ private static final String SCROLL_TEST_DIRS[] = {
+ "alexa25_2011"
+ };
+ private static final String ANIM_TEST_DIRS[] = {
+ "dhtml"
};
public PerformanceTest() {
@@ -91,7 +144,22 @@ public class PerformanceTest extends
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
- mView = (ProfiledWebView) mActivity.findViewById(R.id.web);
+ mWeb = (ProfiledWebView) mActivity.findViewById(R.id.web);
+ mMovementSpinner = (Spinner) mActivity.findViewById(R.id.movement);
+ mStats = new StatAggregator();
+
+ // use mStats as a condition variable between the UI thread and
+ // this(the testing) thread
+ mActivity.setCallback(new ProfileCallback() {
+ @Override
+ public void profileCallback(RunData data) {
+ mStats.setData(data);
+ synchronized (mStats) {
+ mStats.notify();
+ }
+ }
+ });
+
}
private boolean loadUrl(final String url) {
@@ -100,12 +168,13 @@ public class PerformanceTest extends
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
- mView.loadUrl(url);
+ mWeb.loadUrl(url);
}
});
synchronized (mStats) {
mStats.wait();
}
+
mStats.aggregate();
} catch (InterruptedException e) {
e.printStackTrace();
@@ -114,15 +183,30 @@ public class PerformanceTest extends
return true;
}
- private boolean runIteration() {
+ private boolean validTest(String nextTest) {
+ // if testing animations, test must be in mAnimTests
+ if (mAnimTests == null)
+ return true;
+
+ for (String test : mAnimTests) {
+ if (test.equals(nextTest)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean runIteration(String[] testDirs) {
File sdFile = Environment.getExternalStorageDirectory();
- for (String testDirName : TEST_DIRS) {
+ for (String testDirName : testDirs) {
File testDir = new File(sdFile, TEST_LOCATION + "/" + testDirName);
Log.d(LOGTAG, "Testing dir: '" + testDir.getAbsolutePath()
+ "', exists=" + testDir.exists());
+
for (File siteDir : testDir.listFiles()) {
- if (!siteDir.isDirectory())
+ if (!siteDir.isDirectory() || !validTest(siteDir.getName())) {
continue;
+ }
if (!loadUrl(URL_PREFIX + siteDir.getAbsolutePath()
+ URL_POSTFIX)) {
@@ -133,7 +217,44 @@ public class PerformanceTest extends
return true;
}
- public void testMetrics() {
+ private boolean runTestDirs(String[] testDirs) {
+ for (int i = 0; i < MAX_ITERATIONS; i++)
+ if (!runIteration(testDirs)) {
+ return false;
+ }
+ return true;
+ }
+
+ private void pushDoubleBuffering() {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ public void run() {
+ mWeb.setDoubleBuffering(mDoubleBuffering);
+ }
+ });
+ }
+
+ private void setScrollingTestingMode(final boolean scrolled) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ public void run() {
+ mMovementSpinner.setSelection(scrolled ? 0 : 2);
+ }
+ });
+ }
+
+
+ private String[] mAnimTests = null;
+ private int mAnimTestNr = -1;
+ private boolean mDoubleBuffering = true;
+ private static final String[] ANIM_TEST_NAMES = {
+ "slow", "fast"
+ };
+ private static final String[][] ANIM_TESTS = {
+ {"scrolling", "replaceimages", "layers5", "layers1"},
+ {"slidingballs", "meter", "slidein", "fadespacing", "colorfade",
+ "mozilla", "movingtext", "diagball", "zoom", "imageslide"},
+ };
+
+ private boolean checkMedia() {
String state = Environment.getExternalStorageState();
if (!Environment.MEDIA_MOUNTED.equals(state)
@@ -141,27 +262,43 @@ public class PerformanceTest extends
Log.d(LOGTAG, "ARG Can't access sd card!");
// Can't read the SD card, fail and die!
getInstrumentation().sendStatus(1, null);
- return;
+ return false;
}
+ return true;
+ }
- // use mGraphs as a condition variable between the UI thread and
- // this(the testing) thread
- mActivity.setCallback(new ProfileCallback() {
- @Override
- public void profileCallback(RunData data) {
- Log.d(LOGTAG, "test completion callback");
- mStats.setData(data);
- synchronized (mStats) {
- mStats.notify();
+ public void testMetrics() {
+ setScrollingTestingMode(true);
+ if (checkMedia() && runTestDirs(SCROLL_TEST_DIRS)) {
+ getInstrumentation().sendStatus(0, mStats.getBundle());
+ } else {
+ getInstrumentation().sendStatus(1, null);
+ }
+ }
+
+ private boolean runAnimationTests() {
+ for (int doubleBuffer = 0; doubleBuffer <= 1; doubleBuffer++) {
+ mDoubleBuffering = doubleBuffer == 1;
+ pushDoubleBuffering();
+ for (mAnimTestNr = 0; mAnimTestNr < ANIM_TESTS.length; mAnimTestNr++) {
+ mAnimTests = ANIM_TESTS[mAnimTestNr];
+ if (!runTestDirs(ANIM_TEST_DIRS)) {
+ return false;
}
}
- });
+ }
+ return true;
+ }
- for (int i = 0; i < MAX_ITERATIONS; i++)
- if (!runIteration()) {
- getInstrumentation().sendStatus(1, null);
- return;
- }
- getInstrumentation().sendStatus(0, mStats.getBundle());
+ public void testAnimations() {
+ // instead of autoscrolling, load each page until either an timer fires,
+ // or the animation signals complete via javascript
+ setScrollingTestingMode(false);
+
+ if (checkMedia() && runAnimationTests()) {
+ getInstrumentation().sendStatus(0, mStats.getBundle());
+ } else {
+ getInstrumentation().sendStatus(1, null);
+ }
}
}
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
index 9ea90f8..a3ae9be 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java
@@ -80,7 +80,7 @@ public class PlaybackGraphs {
for (int tileID = 1; tileID < frame.length; tileID++) {
TileData data = frame[tileID];
double coverage = viewportCoverage(frame[0], data);
- total += coverage * (data.isReady ? 1 : 0);
+ total += coverage * (data.isReady ? 100 : 0);
totalCount += coverage;
}
if (totalCount == 0) {
@@ -91,7 +91,7 @@ public class PlaybackGraphs {
@Override
public double getMax() {
- return 1;
+ return 100;
}
@Override
@@ -108,6 +108,9 @@ public class PlaybackGraphs {
}
public static double getPercentile(double sortedValues[], double ratioAbove) {
+ if (sortedValues.length == 0)
+ return -1;
+
double index = ratioAbove * (sortedValues.length - 1);
int intIndex = (int) Math.floor(index);
if (index == intIndex) {
@@ -118,6 +121,31 @@ public class PlaybackGraphs {
+ sortedValues[intIndex + 1] * (alpha);
}
+ public static double getMean(double sortedValues[]) {
+ if (sortedValues.length == 0)
+ return -1;
+
+ double agg = 0;
+ for (double val : sortedValues) {
+ agg += val;
+ }
+ return agg / sortedValues.length;
+ }
+
+ public static double getStdDev(double sortedValues[]) {
+ if (sortedValues.length == 0)
+ return -1;
+
+ double agg = 0;
+ double sqrAgg = 0;
+ for (double val : sortedValues) {
+ agg += val;
+ sqrAgg += val*val;
+ }
+ double mean = agg / sortedValues.length;
+ return Math.sqrt((sqrAgg / sortedValues.length) - (mean * mean));
+ }
+
protected static StatGen[] Stats = new StatGen[] {
new StatGen() {
@Override
@@ -149,6 +177,26 @@ public class PlaybackGraphs {
public int getLabelId() {
return R.string.percentile_75;
}
+ }, new StatGen() {
+ @Override
+ public double getValue(double[] sortedValues) {
+ return getStdDev(sortedValues);
+ }
+
+ @Override
+ public int getLabelId() {
+ return R.string.std_dev;
+ }
+ }, new StatGen() {
+ @Override
+ public double getValue(double[] sortedValues) {
+ return getMean(sortedValues);
+ }
+
+ @Override
+ public int getLabelId() {
+ return R.string.mean;
+ }
},
};
@@ -159,40 +207,47 @@ public class PlaybackGraphs {
}
private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>();
- protected double[][] mStats = new double[Metrics.length][Stats.length];
+ protected final double[][] mStats = new double[Metrics.length][Stats.length];
protected HashMap<String, Double> mSingleStats;
+ private void gatherFrameMetric(int metricIndex, double metricValues[], RunData data) {
+ // create graph out of rectangles, one per frame
+ int lastBar = 0;
+ for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) {
+ TileData frame[] = data.frames[frameIndex];
+ int newBar = (frame[0].top + frame[0].bottom) / 2;
+
+ MetricGen s = Metrics[metricIndex];
+ double absoluteValue = s.getValue(frame);
+ double relativeValue = absoluteValue / s.getMax();
+ relativeValue = Math.min(1,relativeValue);
+ relativeValue = Math.max(0,relativeValue);
+ int rightPos = (int) (-BAR_WIDTH * metricIndex);
+ int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue));
+
+ ShapeDrawable graphBar = new ShapeDrawable();
+ graphBar.getPaint().setColor(Color.BLUE);
+ graphBar.setBounds(leftPos, lastBar, rightPos, newBar);
+
+ mShapes.add(graphBar);
+ metricValues[frameIndex] = absoluteValue;
+ lastBar = newBar;
+ }
+ }
+
public void setData(RunData data) {
mShapes.clear();
double metricValues[] = new double[data.frames.length];
+ mSingleStats = data.singleStats;
+
if (data.frames.length == 0) {
return;
}
for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) {
- // create graph out of rectangles, one per frame
- int lastBar = 0;
- for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) {
- TileData frame[] = data.frames[frameIndex];
- int newBar = (frame[0].top + frame[0].bottom) / 2;
-
- MetricGen s = Metrics[metricIndex];
- double absoluteValue = s.getValue(frame);
- double relativeValue = absoluteValue / s.getMax();
- relativeValue = Math.min(1,relativeValue);
- relativeValue = Math.max(0,relativeValue);
- int rightPos = (int) (-BAR_WIDTH * metricIndex);
- int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue));
-
- ShapeDrawable graphBar = new ShapeDrawable();
- graphBar.getPaint().setColor(Color.BLUE);
- graphBar.setBounds(leftPos, lastBar, rightPos, newBar);
-
- mShapes.add(graphBar);
- metricValues[frameIndex] = absoluteValue;
- lastBar = newBar;
- }
+ // calculate metric based on list of frames
+ gatherFrameMetric(metricIndex, metricValues, data);
// store aggregate statistics per metric (median, and similar)
Arrays.sort(metricValues);
@@ -200,8 +255,6 @@ public class PlaybackGraphs {
mStats[metricIndex][statIndex] =
Stats[statIndex].getValue(metricValues);
}
-
- mSingleStats = data.singleStats;
}
}
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
index d38d006..2e77157 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java
@@ -22,11 +22,12 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
-import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.AdapterView;
@@ -49,6 +50,8 @@ import java.io.ObjectOutputStream;
*/
public class ProfileActivity extends Activity {
+ private static final int TIMED_RECORD_MILLIS = 2000;
+
public interface ProfileCallback {
public void profileCallback(RunData data);
}
@@ -65,6 +68,7 @@ public class ProfileActivity extends Activity {
LoggingWebViewClient mLoggingWebViewClient = new LoggingWebViewClient();
AutoLoggingWebViewClient mAutoLoggingWebViewClient = new AutoLoggingWebViewClient();
+ TimedLoggingWebViewClient mTimedLoggingWebViewClient = new TimedLoggingWebViewClient();
private enum TestingState {
NOT_TESTING,
@@ -93,18 +97,18 @@ public class ProfileActivity extends Activity {
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
String movementStr = parent.getItemAtPosition(position).toString();
- if (movementStr == getResources().getString(
- R.string.movement_auto_scroll)
- || movementStr == getResources().getString(
- R.string.movement_auto_fling)) {
+ if (movementStr == getResources().getString(R.string.movement_auto_scroll)) {
mWeb.setWebViewClient(mAutoLoggingWebViewClient);
mCaptureButton.setEnabled(false);
mVelocitySpinner.setEnabled(true);
- } else if (movementStr == getResources().getString(
- R.string.movement_manual)) {
+ } else if (movementStr == getResources().getString(R.string.movement_manual)) {
mWeb.setWebViewClient(mLoggingWebViewClient);
mCaptureButton.setEnabled(true);
mVelocitySpinner.setEnabled(false);
+ } else if (movementStr == getResources().getString(R.string.movement_timed)) {
+ mWeb.setWebViewClient(mTimedLoggingWebViewClient);
+ mCaptureButton.setEnabled(false);
+ mVelocitySpinner.setEnabled(false);
}
}
@@ -124,16 +128,46 @@ public class ProfileActivity extends Activity {
super.onPageStarted(view, url, favicon);
mUrl.setText(url);
}
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ view.requestFocus();
+ ((ProfiledWebView)view).onPageFinished();
+ }
}
private class AutoLoggingWebViewClient extends LoggingWebViewClient {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ startViewProfiling(true);
+ }
@Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ super.onPageStarted(view, url, favicon);
+ setTestingState(TestingState.PRE_TESTING);
+ }
+ }
+
+ private class TimedLoggingWebViewClient extends LoggingWebViewClient {
+ @Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
- view.requestFocus();
+ startViewProfiling(false);
- startViewProfiling(true);
+ // after a fixed time after page finished, stop testing
+ new CountDownTimer(TIMED_RECORD_MILLIS, TIMED_RECORD_MILLIS) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ }
+
+ @Override
+ public void onFinish() {
+ mWeb.stopScrollTest();
+ }
+ }.start();
}
@Override
@@ -178,11 +212,13 @@ public class ProfileActivity extends Activity {
mMovementSpinner.setEnabled(false);
break;
case START_TESTING:
+ mCaptureButton.setChecked(true);
mUrl.setBackgroundResource(R.color.background_start_testing);
mInspectButton.setEnabled(false);
mMovementSpinner.setEnabled(false);
break;
case STOP_TESTING:
+ mCaptureButton.setChecked(false);
mUrl.setBackgroundResource(R.color.background_stop_testing);
break;
case SAVED_TESTING:
@@ -195,7 +231,6 @@ public class ProfileActivity extends Activity {
/** auto - automatically scroll. */
private void startViewProfiling(boolean auto) {
// toggle capture button to indicate capture state to user
- mCaptureButton.setChecked(true);
mWeb.startScrollTest(mCallback, auto);
setTestingState(TestingState.START_TESTING);
}
@@ -217,7 +252,7 @@ public class ProfileActivity extends Activity {
public void profileCallback(RunData data) {
new StoreFileTask().execute(new Pair<String, RunData>(
TEMP_FILENAME, data));
- mCaptureButton.setChecked(false);
+ Log.d("ProfileActivity", "stored " + data.frames.length + " frames in file");
setTestingState(TestingState.STOP_TESTING);
}
});
@@ -245,8 +280,8 @@ public class ProfileActivity extends Activity {
// Movement spinner
String content[] = {
getResources().getString(R.string.movement_auto_scroll),
- getResources().getString(R.string.movement_auto_fling),
- getResources().getString(R.string.movement_manual)
+ getResources().getString(R.string.movement_manual),
+ getResources().getString(R.string.movement_timed)
};
adapter = new ArrayAdapter<CharSequence>(this,
android.R.layout.simple_spinner_item, content);
@@ -270,13 +305,7 @@ public class ProfileActivity extends Activity {
});
// Custom profiling WebView
- WebSettings settings = mWeb.getSettings();
- settings.setJavaScriptEnabled(true);
- settings.setSupportZoom(true);
- settings.setEnableSmoothTransition(true);
- settings.setBuiltInZoomControls(true);
- settings.setLoadWithOverviewMode(true);
- settings.setProperty("use_minimal_memory", "false"); // prefetch tiles, as browser does
+ mWeb.init(this);
mWeb.setWebViewClient(new LoggingWebViewClient());
// URL text entry
diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
index 83f1668..a706f78 100644
--- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
+++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java
@@ -20,21 +20,28 @@ import android.content.Context;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.util.Log;
+import android.webkit.WebSettings;
import android.webkit.WebView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
import com.test.tilebenchmark.ProfileActivity.ProfileCallback;
import com.test.tilebenchmark.RunData.TileData;
public class ProfiledWebView extends WebView {
+ private static final String LOGTAG = "ProfiledWebView";
+
private int mSpeed;
private boolean mIsTesting = false;
private boolean mIsScrolling = false;
private ProfileCallback mCallback;
private long mContentInvalMillis;
- private boolean mHadToBeForced = false;
private static final int LOAD_STALL_MILLIS = 2000; // nr of millis after load,
// before test is forced
+ private double mLoadTime;
+ private double mAnimationTime;
public ProfiledWebView(Context context) {
super(context);
@@ -53,6 +60,39 @@ public class ProfiledWebView extends WebView {
super(context, attrs, defStyle, privateBrowsing);
}
+ private class JavaScriptInterface {
+ Context mContext;
+
+ /** Instantiate the interface and set the context */
+ JavaScriptInterface(Context c) {
+ mContext = c;
+ }
+
+ /** Show a toast from the web page */
+ public void animationComplete() {
+ Toast.makeText(mContext, "Animation complete!", Toast.LENGTH_SHORT).show();
+ //Log.d(LOGTAG, "anim complete");
+ mAnimationTime = System.currentTimeMillis();
+ }
+ }
+
+ public void init(Context c) {
+ WebSettings settings = getSettings();
+ settings.setJavaScriptEnabled(true);
+ settings.setSupportZoom(true);
+ settings.setEnableSmoothTransition(true);
+ settings.setBuiltInZoomControls(true);
+ settings.setLoadWithOverviewMode(true);
+ settings.setProperty("use_minimal_memory", "false"); // prefetch tiles, as browser does
+ addJavascriptInterface(new JavaScriptInterface(c), "Android");
+ mAnimationTime = 0;
+ mLoadTime = 0;
+ }
+
+ public void onPageFinished() {
+ mLoadTime = System.currentTimeMillis();
+ }
+
@Override
protected void onDraw(android.graphics.Canvas canvas) {
if (mIsTesting && mIsScrolling) {
@@ -72,9 +112,12 @@ public class ProfiledWebView extends WebView {
* scrolling, invalidate all content and redraw it, measuring time taken.
*/
public void startScrollTest(ProfileCallback callback, boolean autoScrolling) {
- mIsScrolling = autoScrolling;
mCallback = callback;
mIsTesting = false;
+ mIsScrolling = false;
+ WebSettings settings = getSettings();
+ settings.setProperty("tree_updates", "0");
+
if (autoScrolling) {
// after a while, force it to start even if the pages haven't swapped
@@ -86,13 +129,18 @@ public class ProfiledWebView extends WebView {
@Override
public void onFinish() {
// invalidate all content, and kick off redraw
+ Log.d("ProfiledWebView",
+ "kicking off test with callback registration, and tile discard...");
registerPageSwapCallback();
discardAllTextures();
invalidate();
-
+ mIsScrolling = true;
mContentInvalMillis = System.currentTimeMillis();
}
}.start();
+ } else {
+ mIsTesting = true;
+ tileProfilingStart();
}
}
@@ -102,13 +150,35 @@ public class ProfiledWebView extends WebView {
*/
@Override
protected void pageSwapCallback(boolean startAnim) {
- mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis;
- super.pageSwapCallback(startAnim);
- Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis
- + "millis");
- mIsTesting = true;
- invalidate(); // ensure a redraw so that auto-scrolling can occur
- tileProfilingStart();
+ if (!mIsTesting && mIsScrolling) {
+ // kick off testing
+ mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis;
+ super.pageSwapCallback(startAnim);
+ Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis + "millis");
+ mIsTesting = true;
+ invalidate(); // ensure a redraw so that auto-scrolling can occur
+ tileProfilingStart();
+ }
+ }
+
+ private double animFramerate() {
+ WebSettings settings = getSettings();
+ String updatesString = settings.getProperty("tree_updates");
+ int updates = (updatesString == null) ? -1 : Integer.parseInt(updatesString);
+
+ double animationTime;
+ if (mAnimationTime == 0) {
+ animationTime = System.currentTimeMillis() - mLoadTime;
+ } else {
+ animationTime = mAnimationTime - mLoadTime;
+ }
+
+ return updates * 1000 / animationTime;
+ }
+
+ public void setDoubleBuffering(boolean useDoubleBuffering) {
+ WebSettings settings = getSettings();
+ settings.setProperty("use_double_buffering", useDoubleBuffering ? "true" : "false");
}
/*
@@ -127,11 +197,12 @@ public class ProfiledWebView extends WebView {
// record the time spent (before scrolling) rendering the page
data.singleStats.put(getResources().getString(R.string.render_millis),
(double)mContentInvalMillis);
- // record if the page render timed out
- Log.d("ProfiledWebView", "hadtobeforced = " + mHadToBeForced);
- data.singleStats.put(getResources().getString(R.string.render_stalls),
- mHadToBeForced ? 1.0 : 0.0);
- mHadToBeForced = false;
+
+ // record framerate
+ double framerate = animFramerate();
+ Log.d(LOGTAG, "anim framerate was "+framerate);
+ data.singleStats.put(getResources().getString(R.string.animation_framerate),
+ framerate);
for (int frame = 0; frame < data.frames.length; frame++) {
data.frames[frame] = new TileData[
@@ -159,6 +230,8 @@ public class ProfiledWebView extends WebView {
@Override
public void loadUrl(String url) {
+ mAnimationTime = 0;
+ mLoadTime = 0;
if (!url.startsWith("http://") && !url.startsWith("file://")) {
url = "http://" + url;
}