From 4d7a38588911bf7de3803ea7d53783d3a89bd2f2 Mon Sep 17 00:00:00 2001 From: Mike Lockwood <> Date: Tue, 31 Mar 2009 13:59:40 -0700 Subject: [PATCH 01/19] AI 143648: am: CL 143616 Fix ANR that could occur when disabling GPS in Settings while the GPS is active. Exit from GpsEventThread immediately when the GPS is disabled instead of waiting for the GPS to shut down fully. BUG=1729031 Original author: lockwood Merged from: //branches/donutburger/... Automated import of CL 143648 --- .../android/internal/location/GpsLocationProvider.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 887574a66bad1..f5950501d5107 100644 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -412,6 +412,12 @@ public class GpsLocationProvider extends LocationProviderImpl { mNetworkThread = null; } + // The GpsEventThread does not wait for the GPS to shutdown + // so we need to report the GPS_STATUS_ENGINE_OFF event here + if (mNavigating) { + reportStatus(GPS_STATUS_ENGINE_OFF); + } + native_cleanup(); } @@ -797,8 +803,8 @@ public class GpsLocationProvider extends LocationProviderImpl { public void run() { if (Config.LOGD) Log.d(TAG, "GpsEventThread starting"); - // thread exits after disable() is called and navigation has stopped - while (mEnabled || mNavigating) { + // Exit as soon as disable() is called instead of waiting for the GPS to stop. + while (mEnabled) { // this will wait for an event from the GPS, // which will be reported via reportLocation or reportStatus native_wait_for_event(); From a3ea3ae07ff3fec565215ad73fc7430a07b57418 Mon Sep 17 00:00:00 2001 From: Eric Fischer <> Date: Tue, 31 Mar 2009 14:17:10 -0700 Subject: [PATCH 02/19] AI 143697: am: CL 143669 am: CL 143495 Add more accented letters to the character picker. The hardware keyboard doesn't have all the accents needed for Czech and Polish, so round out the selection. Original author: enf Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143697 --- .../text/method/QwertyKeyListener.java | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 21bc2a6311b64..e420c27516e30 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -405,23 +405,34 @@ public class QwertyKeyListener extends BaseKeyListener { PICKER_SETS.put('<', "\u00AB"); PICKER_SETS.put('>', "\u00BB"); PICKER_SETS.put('?', "\u00BF"); - PICKER_SETS.put('A', "\u00C0\u00C1\u00C2\u00C4\u00C6\u00C3\u00C5"); - PICKER_SETS.put('C', "\u00C7"); - PICKER_SETS.put('E', "\u00C8\u00C9\u00CA\u00CB"); - PICKER_SETS.put('I', "\u00CC\u00CD\u00CE\u00CF"); - PICKER_SETS.put('N', "\u00D1"); - PICKER_SETS.put('O', "\u00D8\u0152\u00D5\u00D2\u00D3\u00D4\u00D6"); - PICKER_SETS.put('U', "\u00D9\u00DA\u00DB\u00DC"); + PICKER_SETS.put('A', "\u00C0\u00C1\u00C2\u00C4\u00C6\u00C3\u00C5\u0104\u0100"); + PICKER_SETS.put('C', "\u00C7\u0106\u010C"); + PICKER_SETS.put('D', "\u010E"); + PICKER_SETS.put('E', "\u00C8\u00C9\u00CA\u00CB\u0118\u011A\u0112"); + PICKER_SETS.put('L', "\u0141"); + PICKER_SETS.put('I', "\u00CC\u00CD\u00CE\u00CF\u012A"); + PICKER_SETS.put('N', "\u00D1\u0143\u0147"); + PICKER_SETS.put('O', "\u00D8\u0152\u00D5\u00D2\u00D3\u00D4\u00D6\u014C"); + PICKER_SETS.put('R', "\u0158"); + PICKER_SETS.put('S', "\u015A\u0160"); + PICKER_SETS.put('T', "\u0164"); + PICKER_SETS.put('U', "\u00D9\u00DA\u00DB\u00DC\u016E\u016A"); PICKER_SETS.put('Y', "\u00DD\u0178"); - PICKER_SETS.put('a', "\u00E0\u00E1\u00E2\u00E4\u00E6\u00E3\u00E5"); - PICKER_SETS.put('c', "\u00E7"); - PICKER_SETS.put('e', "\u00E8\u00E9\u00EA\u00EB"); - PICKER_SETS.put('i', "\u00EC\u00ED\u00EE\u00EF"); - PICKER_SETS.put('n', "\u00F1"); - PICKER_SETS.put('o', "\u00F8\u0153\u00F5\u00F2\u00F3\u00F4\u00F6"); - PICKER_SETS.put('s', "\u00A7\u00DF"); - PICKER_SETS.put('u', "\u00F9\u00FA\u00FB\u00FC"); + PICKER_SETS.put('Z', "\u0179\u017B\u017D"); + PICKER_SETS.put('a', "\u00E0\u00E1\u00E2\u00E4\u00E6\u00E3\u00E5\u0105\u0101"); + PICKER_SETS.put('c', "\u00E7\u0107\u010D"); + PICKER_SETS.put('d', "\u010F"); + PICKER_SETS.put('e', "\u00E8\u00E9\u00EA\u00EB\u0119\u011B\u0113"); + PICKER_SETS.put('i', "\u00EC\u00ED\u00EE\u00EF\u012B"); + PICKER_SETS.put('l', "\u0142"); + PICKER_SETS.put('n', "\u00F1\u0144\u0148"); + PICKER_SETS.put('o', "\u00F8\u0153\u00F5\u00F2\u00F3\u00F4\u00F6\u014D"); + PICKER_SETS.put('r', "\u0159"); + PICKER_SETS.put('s', "\u00A7\u00DF\u015B\u0161"); + PICKER_SETS.put('t', "\u0165"); + PICKER_SETS.put('u', "\u00F9\u00FA\u00FB\u00FC\u016F\u016B"); PICKER_SETS.put('y', "\u00FD\u00FF"); + PICKER_SETS.put('z', "\u017A\u017C\u017E"); PICKER_SETS.put(KeyCharacterMap.PICKER_DIALOG_INPUT, "\u2026\u00A5\u2022\u00AE\u00A9\u00B1"); }; From 549d7243ff9cf638a63a0d5cc82c792b39484e8e Mon Sep 17 00:00:00 2001 From: Eric Fischer <> Date: Tue, 31 Mar 2009 14:19:47 -0700 Subject: [PATCH 03/19] AI 143709: am: CL 143678 am: CL 143540 Try not to start TextView lines with non-starter characters. TextView was previously following the "relaxed" line breaking convention and would allow a line break between any two ideographic characters. Tighten that up and do not allow line breaks before non-starter characters (sound and iteration marks and small Hiragana and Katakana). Original author: enf Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143709 --- core/java/android/text/StaticLayout.java | 57 ++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 686e8f5327e85..c133cf2b30a00 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -618,7 +618,9 @@ extends Layout * - is class HY: a breakpoint * except when followed by a digit. * - * Ideographs are class ID: breakpoints when adjacent. + * Ideographs are class ID: breakpoints when adjacent, + * except for NS (non-starters), which can be broken + * after but not before. */ if (c == ' ' || c == '\t' || @@ -627,8 +629,8 @@ extends Layout (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) || ((c == '/' || c == '-') && (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) || - (c >= FIRST_CJK && isIdeographic(c) && - j + 1 < next && isIdeographic(chs[j + 1 - start]))) { + (c >= FIRST_CJK && isIdeographic(c, true) && + j + 1 < next && isIdeographic(chs[j + 1 - start], false))) { okwidth = w; ok = j + 1; @@ -807,8 +809,12 @@ extends Layout * as being Ideographic (class ID) by the Unicode Line Breaking Algorithm * (http://www.unicode.org/unicode/reports/tr14/), and is therefore OK * to break between a pair of. + * + * @param includeNonStarters also return true for category NS + * (non-starters), which can be broken + * after but not before. */ - private static final boolean isIdeographic(char c) { + private static final boolean isIdeographic(char c, boolean includeNonStarters) { if (c >= '\u2E80' && c <= '\u2FFF') { return true; // CJK, KANGXI RADICALS, DESCRIPTION SYMBOLS } @@ -816,9 +822,52 @@ extends Layout return true; // IDEOGRAPHIC SPACE } if (c >= '\u3040' && c <= '\u309F') { + if (!includeNonStarters) { + switch (c) { + case '\u3041': // # HIRAGANA LETTER SMALL A + case '\u3043': // # HIRAGANA LETTER SMALL I + case '\u3045': // # HIRAGANA LETTER SMALL U + case '\u3047': // # HIRAGANA LETTER SMALL E + case '\u3049': // # HIRAGANA LETTER SMALL O + case '\u3063': // # HIRAGANA LETTER SMALL TU + case '\u3083': // # HIRAGANA LETTER SMALL YA + case '\u3085': // # HIRAGANA LETTER SMALL YU + case '\u3087': // # HIRAGANA LETTER SMALL YO + case '\u308E': // # HIRAGANA LETTER SMALL WA + case '\u3095': // # HIRAGANA LETTER SMALL KA + case '\u3096': // # HIRAGANA LETTER SMALL KE + case '\u309B': // # KATAKANA-HIRAGANA VOICED SOUND MARK + case '\u309C': // # KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK + case '\u309D': // # HIRAGANA ITERATION MARK + case '\u309E': // # HIRAGANA VOICED ITERATION MARK + return false; + } + } return true; // Hiragana (except small characters) } if (c >= '\u30A0' && c <= '\u30FF') { + if (!includeNonStarters) { + switch (c) { + case '\u30A0': // # KATAKANA-HIRAGANA DOUBLE HYPHEN + case '\u30A1': // # KATAKANA LETTER SMALL A + case '\u30A3': // # KATAKANA LETTER SMALL I + case '\u30A5': // # KATAKANA LETTER SMALL U + case '\u30A7': // # KATAKANA LETTER SMALL E + case '\u30A9': // # KATAKANA LETTER SMALL O + case '\u30C3': // # KATAKANA LETTER SMALL TU + case '\u30E3': // # KATAKANA LETTER SMALL YA + case '\u30E5': // # KATAKANA LETTER SMALL YU + case '\u30E7': // # KATAKANA LETTER SMALL YO + case '\u30EE': // # KATAKANA LETTER SMALL WA + case '\u30F5': // # KATAKANA LETTER SMALL KA + case '\u30F6': // # KATAKANA LETTER SMALL KE + case '\u30FB': // # KATAKANA MIDDLE DOT + case '\u30FC': // # KATAKANA-HIRAGANA PROLONGED SOUND MARK + case '\u30FD': // # KATAKANA ITERATION MARK + case '\u30FE': // # KATAKANA VOICED ITERATION MARK + return false; + } + } return true; // Katakana (except small characters) } if (c >= '\u3400' && c <= '\u4DB5') { From dd5ebe0c5b4ebc7748f894ca199acc9fbaad8928 Mon Sep 17 00:00:00 2001 From: Eric Fischer <> Date: Tue, 31 Mar 2009 14:20:40 -0700 Subject: [PATCH 04/19] AI 143712: am: CL 143685 am: CL 143547 Import revised translations. Original author: enf Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143712 --- core/res/res/values-ja/strings.xml | 38 +++++++++++++-------------- core/res/res/values-zh-rCN/arrays.xml | 32 ---------------------- core/res/res/values-zh-rTW/arrays.xml | 32 ---------------------- 3 files changed, 19 insertions(+), 83 deletions(-) delete mode 100644 core/res/res/values-zh-rCN/arrays.xml delete mode 100644 core/res/res/values-zh-rTW/arrays.xml diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index ccc7f198ac3a6..29e4a6d994a0b 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -572,7 +572,7 @@ "%1$s %2$s" "%1$s %2$s" "%2$s%1$s" - "yyyy'/'MMMM'/'d" + "MMMMd'日 'yyyy" "yyyy'年'MMMM'月'd'日'" "MMM'/'d' 'yyyy'年'" "d'/'MMM'/'yyyy" @@ -586,16 +586,16 @@ "%Y%B%-d日" - "%Y%B月" + "%Y%B" "%H:%M:%S" "%Y/%B/%-d %H:%M:%S" - "%2$s%3$s日~%7$s%8$s日" + "%2$s/%3$s%7$s/%8$s" "%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%9$s%2$s%3$s日~%7$s%8$s日" + "%2$s/%3$s%7$s/%8$s, %9$s" "%9$s/%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%2$s%3$s%5$s%7$s%8$s%10$s" + "%2$s/%3$s %5$s%7$s/%8$s %10$s" "%2$s/%3$s%1$s%5$s%7$s/%8$s%6$s%10$s" - "%4$s%2$s%3$s%5$s%9$s%7$s%8$s%10$s" + "%4$s/%2$s/%3$s %5$s%9$s/%7$s/%8$s %10$s" "%4$s/%2$s/%3$s%1$s%5$s%9$s/%7$s/%8$s%6$s%10$s" "%2$s/%3$s%7$s/%8$s" "%2$s/%3$s%1$s%7$s/%8$s%6$s" @@ -607,7 +607,7 @@ "%4$s/%2$s/%3$s%1$s%5$s%9$s/%7$s/%8$s%6$s%10$s" "%2$s%3$s日~%8$s日" "%2$s/%3$s%1$s%7$s/%8$s%6$s" - "%9$s%2$s%3$s日~%8$s日" + "%2$s/%3$s - %8$s, %9$s" "%4$s/%2$s/%3$s%1$s%9$s/%7$s/%8$s%6$s" "%2$s/%3$s%5$s%7$s/%8$s%10$s" "%2$s/%3$s%1$s%5$s%7$s/%8$s%6$s%10$s" @@ -665,18 +665,18 @@ "10月" "11月" "12月" - "1月" - "2月" - "3月" - "4月" - "5月" - "6月" - "7月" - "8月" - "9月" - "10月" - "11月" - "12月" + "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10" + "11" + "12" "1" "2" "3" diff --git a/core/res/res/values-zh-rCN/arrays.xml b/core/res/res/values-zh-rCN/arrays.xml deleted file mode 100644 index 16da6d91e49fb..0000000000000 --- a/core/res/res/values-zh-rCN/arrays.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - 39937795 - 116387224 - - - - 2 - - - diff --git a/core/res/res/values-zh-rTW/arrays.xml b/core/res/res/values-zh-rTW/arrays.xml deleted file mode 100644 index be0795b84af01..0000000000000 --- a/core/res/res/values-zh-rTW/arrays.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - 25022112 - 121478019 - - - - 3 - - - From 538e53569b0c67dacf9f8f0b17ac8e5c080b55bb Mon Sep 17 00:00:00 2001 From: Dirk Dougherty <> Date: Tue, 31 Mar 2009 14:24:19 -0700 Subject: [PATCH 05/19] AI 143716: am: CL 143696 am: CL 143576 SDK doc change: Fix links from SDK upgrading docs to migration and diff info. Original author: ddougherty Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143716 --- docs/html/sdk/1.0_r1/upgrading.jd | 12 +++++++++--- docs/html/sdk/1.0_r2/upgrading.jd | 12 +++++++++--- docs/html/sdk/1.1_r1/upgrading.jd | 10 +++++----- docs/html/sdk/1.5_r1/upgrading.jd | 11 ++++++----- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/html/sdk/1.0_r1/upgrading.jd b/docs/html/sdk/1.0_r1/upgrading.jd index 480bff384a9f5..d6d5dc486c59e 100644 --- a/docs/html/sdk/1.0_r1/upgrading.jd +++ b/docs/html/sdk/1.0_r1/upgrading.jd @@ -133,11 +133,17 @@ to place 3rd jar files, which are now automatically handled by the Ant script.Migrate your applications -

After updating your SDK, you will likely encounter breakages in your code, due to -framework and API changes. You'll need to update your code to match changes in the Android APIs.

+

If (and only if) you have written apps in an SDK released previous to +the Android 1.0 SDK, you will need to migrate your applications. After +updating your SDK, you may encounter breakages in your code, due to +framework and API changes. You'll need to update your code to match +changes in the Android APIs.

One way to start is to open your project in Eclipse and see where the ADT -identifies errors in your application.

+identifies errors in your application. You can also look up +specific changes in the Android APIs in the Overview of Changes and +API Diffs Report, both available in the documentation included with the +Android 1.0 SDK package.

If you have additional trouble updating your code, visit the Android Developers Group diff --git a/docs/html/sdk/1.0_r2/upgrading.jd b/docs/html/sdk/1.0_r2/upgrading.jd index df9b6579273b0..409e30e8c1959 100644 --- a/docs/html/sdk/1.0_r2/upgrading.jd +++ b/docs/html/sdk/1.0_r2/upgrading.jd @@ -133,11 +133,17 @@ to place 3rd jar files, which are now automatically handled by the Ant script.Migrate your applications -

After updating your SDK, you will likely encounter breakages in your code, due to -framework and API changes. You'll need to update your code to match changes in the Android APIs.

+

If (and only if) you have written apps in an SDK released previous to +the Android 1.0 SDK, you will need to migrate your applications. After +updating your SDK, you may encounter breakages in your code, due to +framework and API changes. You'll need to update your code to match +changes in the Android APIs.

One way to start is to open your project in Eclipse and see where the ADT -identifies errors in your application.

+identifies errors in your application. You can also look up +specific changes in the Android APIs in the Overview of Changes and +API Diffs Report, both available in the documentation included with the +Android 1.0 SDK package.

If you have additional trouble updating your code, visit the Android Developers Group diff --git a/docs/html/sdk/1.1_r1/upgrading.jd b/docs/html/sdk/1.1_r1/upgrading.jd index 2ad6757c0904d..19095c0be948e 100644 --- a/docs/html/sdk/1.1_r1/upgrading.jd +++ b/docs/html/sdk/1.1_r1/upgrading.jd @@ -131,11 +131,11 @@ framework and API changes. You'll need to update your code to match the latest APIs.

One way to start is to open your project in Eclipse and see where the ADT -identifies errors in your application. From there, you can lookup -specific API changes in the Android 1.0 APIs in the - -Overview of Changes and -API Diffs Report.

+identifies errors in your application. You can also look up +specific changes in the Android APIs in the +Android 1.1 Version +Notes document.

+

If you have additional trouble updating your code, visit the Android Developers Group diff --git a/docs/html/sdk/1.5_r1/upgrading.jd b/docs/html/sdk/1.5_r1/upgrading.jd index 23cb82c29fa07..63b7a3ce559c4 100644 --- a/docs/html/sdk/1.5_r1/upgrading.jd +++ b/docs/html/sdk/1.5_r1/upgrading.jd @@ -131,11 +131,12 @@ framework and API changes. You'll need to update your code to match the latest APIs.

One way to start is to open your project in Eclipse and see where the ADT -identifies errors in your application. From there, you can lookup -specific API changes in the Android 1.0 APIs in the - -Overview of Changes and -API Diffs Report.

+identifies errors in your application. You can also look up +specific changes in the Android APIs in the + +Android 1.5 Version Notes + +document.

If you have additional trouble updating your code, visit the Android Developers Group From c38c0f6b9869889fbb6743275e35f120a27f6361 Mon Sep 17 00:00:00 2001 From: Evan Millar <> Date: Tue, 31 Mar 2009 14:27:47 -0700 Subject: [PATCH 06/19] AI 143720: am: CL 143710 am: CL 143615 -Add more stats: -total network sent/received -total full/partial wakelock times. -Format the network sent/received amounts to be more easily readable. Original author: emillar Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143720 --- core/java/android/os/BatteryStats.java | 113 +++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 8 deletions(-) diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index b0ae0e60b42e9..c747c43b81e7c 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -77,7 +77,11 @@ public abstract class BatteryStats implements Parcelable { /** * Bump the version on this if the checkin format changes. */ - private static final int BATTERY_STATS_CHECKIN_VERSION = 1; + private static final int BATTERY_STATS_CHECKIN_VERSION = 2; + + private static final long BYTES_PER_KB = 1024; + private static final long BYTES_PER_MB = 1048576; // 1024^2 + private static final long BYTES_PER_GB = 1073741824; //1024^3 // TODO: Update this list if you add/change any stats above. private static final String[] STAT_NAMES = { "total", "last", "current", "unplugged" }; @@ -482,6 +486,23 @@ public abstract class BatteryStats implements Parcelable { return mFormatBuilder.toString(); } + private final String formatBytesLocked(long bytes) { + mFormatBuilder.setLength(0); + + if (bytes < BYTES_PER_KB) { + return bytes + "B"; + } else if (bytes < BYTES_PER_MB) { + mFormatter.format("%.2fKB", bytes / (double) BYTES_PER_KB); + return mFormatBuilder.toString(); + } else if (bytes < BYTES_PER_GB){ + mFormatter.format("%.2fMB", bytes / (double) BYTES_PER_MB); + return mFormatBuilder.toString(); + } else { + mFormatter.format("%.2fGB", bytes / (double) BYTES_PER_GB); + return mFormatBuilder.toString(); + } + } + /** * * @param sb a StringBuilder object. @@ -590,6 +611,9 @@ public abstract class BatteryStats implements Parcelable { StringBuilder sb = new StringBuilder(128); + SparseArray uidStats = getUidStats(); + final int NU = uidStats.size(); + String category = STAT_NAMES[which]; // Dump "battery" stat @@ -598,10 +622,42 @@ public abstract class BatteryStats implements Parcelable { whichBatteryUptime / 1000, whichBatteryRealtime / 1000, totalUptime / 1000, totalRealtime / 1000); + // Calculate total network and wakelock times across all uids. + long rxTotal = 0; + long txTotal = 0; + long fullWakeLockTimeTotal = 0; + long partialWakeLockTimeTotal = 0; + + for (int iu = 0; iu < NU; iu++) { + Uid u = uidStats.valueAt(iu); + rxTotal += u.getTcpBytesReceived(which); + txTotal += u.getTcpBytesSent(which); + + Map wakelocks = u.getWakelockStats(); + if (wakelocks.size() > 0) { + for (Map.Entry ent + : wakelocks.entrySet()) { + Uid.Wakelock wl = ent.getValue(); + + Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL); + if (fullWakeTimer != null) { + fullWakeLockTimeTotal += fullWakeTimer.getTotalTime(batteryRealtime, which); + } + + Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL); + if (partialWakeTimer != null) { + partialWakeLockTimeTotal += partialWakeTimer.getTotalTime( + batteryRealtime, which); + } + } + } + } + // Dump misc stats dumpLine(pw, 0 /* uid */, category, MISC_DATA, screenOnTime / 1000, phoneOnTime / 1000, wifiOnTime / 1000, - wifiRunningTime / 1000, bluetoothOnTime / 1000); + wifiRunningTime / 1000, bluetoothOnTime / 1000, rxTotal, txTotal, + fullWakeLockTimeTotal, partialWakeLockTimeTotal); // Dump signal strength stats Object[] args = new Object[NUM_SIGNAL_STRENGTH_BINS]; @@ -622,8 +678,6 @@ public abstract class BatteryStats implements Parcelable { getPluggedStartLevel()); } - SparseArray uidStats = getUidStats(); - final int NU = uidStats.size(); for (int iu = 0; iu < NU; iu++) { final int uid = uidStats.keyAt(iu); Uid u = uidStats.valueAt(iu); @@ -741,6 +795,9 @@ public abstract class BatteryStats implements Parcelable { final long totalUptime = computeUptime(rawUptime, which); StringBuilder sb = new StringBuilder(128); + + SparseArray uidStats = getUidStats(); + final int NU = uidStats.size(); pw.println(prefix + " Time on battery: " + formatTimeMs(whichBatteryUptime / 1000) @@ -774,6 +831,47 @@ public abstract class BatteryStats implements Parcelable { + "), Bluetooth on: " + formatTimeMs(bluetoothOnTime / 1000) + "(" + formatRatioLocked(bluetoothOnTime, whichBatteryRealtime)+ ")"); + // Calculate total network and wakelock times across all uids. + long rxTotal = 0; + long txTotal = 0; + long fullWakeLockTimeTotalMicros = 0; + long partialWakeLockTimeTotalMicros = 0; + + for (int iu = 0; iu < NU; iu++) { + Uid u = uidStats.valueAt(iu); + rxTotal += u.getTcpBytesReceived(which); + txTotal += u.getTcpBytesSent(which); + + Map wakelocks = u.getWakelockStats(); + if (wakelocks.size() > 0) { + for (Map.Entry ent + : wakelocks.entrySet()) { + Uid.Wakelock wl = ent.getValue(); + + Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL); + if (fullWakeTimer != null) { + fullWakeLockTimeTotalMicros += fullWakeTimer.getTotalTime( + batteryRealtime, which); + } + + Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL); + if (partialWakeTimer != null) { + partialWakeLockTimeTotalMicros += partialWakeTimer.getTotalTime( + batteryRealtime, which); + } + } + } + } + + pw.println(prefix + + " Total received: " + formatBytesLocked(rxTotal) + + ", Total sent: " + formatBytesLocked(txTotal)); + pw.println(prefix + + " Total full wakelock time: " + formatTimeMs( + (fullWakeLockTimeTotalMicros + 500) / 1000) + + ", Total partial waklock time: " + formatTimeMs( + (partialWakeLockTimeTotalMicros + 500) / 1000)); + sb.setLength(0); sb.append(" Signal strengths: "); boolean didOne = false; @@ -832,8 +930,7 @@ public abstract class BatteryStats implements Parcelable { pw.println(" "); - SparseArray uidStats = getUidStats(); - final int NU = uidStats.size(); + for (int iu=0; iu Date: Tue, 31 Mar 2009 14:29:35 -0700 Subject: [PATCH 07/19] AI 143733: Fix for issue 1648553: IME overlaps over focus ring in gmail search box. In WebView::requestChildRectangleOnScreen we now move a rectangle up if its bottom is offscreen, even if its top is not. We also move it so that its top is at the top third of the WebView's height (if it still fits on screen) so that you can see space (and potentially other input fields) below it. BUG=1648553 Automated import of CL 143733 --- core/java/android/webkit/WebView.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 052c2d3835d30..17ab8c92365d4 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4405,14 +4405,19 @@ public class WebView extends AbsoluteLayout int scrollYDelta = 0; - if (rect.bottom > screenBottom && rect.top > screenTop) { - if (rect.height() > height) { - scrollYDelta += (rect.top - screenTop); + if (rect.bottom > screenBottom) { + int oneThirdOfScreenHeight = height / 3; + if (rect.height() > 2 * oneThirdOfScreenHeight) { + // If the rectangle is too tall to fit in the bottom two thirds + // of the screen, place it at the top. + scrollYDelta = rect.top - screenTop; } else { - scrollYDelta += (rect.bottom - screenBottom); + // If the rectangle will still fit on screen, we want its + // top to be in the top third of the screen. + scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight); } } else if (rect.top < screenTop) { - scrollYDelta -= (screenTop - rect.top); + scrollYDelta = rect.top - screenTop; } int width = getWidth() - getVerticalScrollbarWidth(); From d3b4d0cfc10c95c9190a2b0e6a8c8e8f3d448b87 Mon Sep 17 00:00:00 2001 From: Eric Laurent <> Date: Tue, 31 Mar 2009 14:34:35 -0700 Subject: [PATCH 08/19] AI 143785: am: CL 143775 am: CL 143620 Attempt for fixing crash in AudioFlinger::MixerThread::dumpTracks() seen in bug report for issue 1747119. AudioFlinger::MixerThread::dumpTracks() was reading mTracks[] vector instead of mActiveTracks[] when dumping active tracks. Original author: elaurent Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143785 --- libs/audioflinger/AudioFlinger.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index b9ecdd8e41fb2..d11e13a7fb4aa 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -941,13 +941,10 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector& a result.append(buffer); result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); for (size_t i = 0; i < mTracks.size(); ++i) { - wp wTrack = mTracks[i]; - if (wTrack != 0) { - sp track = wTrack.promote(); - if (track != 0) { - track->dump(buffer, SIZE); - result.append(buffer); - } + sp track = mTracks[i]; + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); } } @@ -955,7 +952,7 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector& a result.append(buffer); result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); for (size_t i = 0; i < mActiveTracks.size(); ++i) { - wp wTrack = mTracks[i]; + wp wTrack = mActiveTracks[i]; if (wTrack != 0) { sp track = wTrack.promote(); if (track != 0) { From 4896cc81dd921e1274007f29689adac639ba771f Mon Sep 17 00:00:00 2001 From: Sridhar Gurivireddy <> Date: Tue, 31 Mar 2009 14:39:23 -0700 Subject: [PATCH 09/19] AI 143801: am: CL 143779 am: CL 143727 Make the test runner and results assets of DumpRendertree. This is done so that we could have 1) Lab machines can run layout tests without having build environment setup 2) We could have different set of pass/fail results per branch Also added a simple python script which runs run_layout_tests.py Original author: sridharg Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143801 --- .../results/layout_tests_crashed.txt | 0 .../results/layout_tests_failed.txt | 0 .../results/layout_tests_nontext.txt | 0 .../results/layout_tests_passed.txt | 0 .../{ => assets}/run_layout_tests.py | 0 .../dumprendertree/LayoutTestsAutoTest.java | 38 +++++++++++++++++++ 6 files changed, 38 insertions(+) rename tests/DumpRenderTree/{ => assets}/results/layout_tests_crashed.txt (100%) rename tests/DumpRenderTree/{ => assets}/results/layout_tests_failed.txt (100%) rename tests/DumpRenderTree/{ => assets}/results/layout_tests_nontext.txt (100%) rename tests/DumpRenderTree/{ => assets}/results/layout_tests_passed.txt (100%) rename tests/DumpRenderTree/{ => assets}/run_layout_tests.py (100%) diff --git a/tests/DumpRenderTree/results/layout_tests_crashed.txt b/tests/DumpRenderTree/assets/results/layout_tests_crashed.txt similarity index 100% rename from tests/DumpRenderTree/results/layout_tests_crashed.txt rename to tests/DumpRenderTree/assets/results/layout_tests_crashed.txt diff --git a/tests/DumpRenderTree/results/layout_tests_failed.txt b/tests/DumpRenderTree/assets/results/layout_tests_failed.txt similarity index 100% rename from tests/DumpRenderTree/results/layout_tests_failed.txt rename to tests/DumpRenderTree/assets/results/layout_tests_failed.txt diff --git a/tests/DumpRenderTree/results/layout_tests_nontext.txt b/tests/DumpRenderTree/assets/results/layout_tests_nontext.txt similarity index 100% rename from tests/DumpRenderTree/results/layout_tests_nontext.txt rename to tests/DumpRenderTree/assets/results/layout_tests_nontext.txt diff --git a/tests/DumpRenderTree/results/layout_tests_passed.txt b/tests/DumpRenderTree/assets/results/layout_tests_passed.txt similarity index 100% rename from tests/DumpRenderTree/results/layout_tests_passed.txt rename to tests/DumpRenderTree/assets/results/layout_tests_passed.txt diff --git a/tests/DumpRenderTree/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py similarity index 100% rename from tests/DumpRenderTree/run_layout_tests.py rename to tests/DumpRenderTree/assets/run_layout_tests.py diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java index a857e681aa5c2..39eae02940d21 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java @@ -42,6 +42,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Vector; //TestRecorder creates two files, one for passing tests @@ -122,6 +124,18 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2 mTestList; @@ -452,4 +466,28 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2 0 ) { + out.write(buf, 0, len); + } + out.close(); + in.close(); + } + }catch (IOException e) { + e.printStackTrace(); + } + + } + } From 2b5be0748d160c6df1a9197272a776dc35879081 Mon Sep 17 00:00:00 2001 From: Nick Pelly <> Date: Tue, 31 Mar 2009 14:42:33 -0700 Subject: [PATCH 10/19] AI 143812: am: CL 143788 am: CL 143740 Don't clear supported profiles in settings app if getRemoteClass returns error. Also clean up the error codes returned by the framework, so that the settings app can properly detect an error. Original author: npelly Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143812 --- core/java/android/bluetooth/BluetoothDevice.java | 3 ++- core/java/android/server/BluetoothDeviceService.java | 6 +++--- core/jni/android_server_BluetoothDeviceService.cpp | 10 +++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index abf08cbfc7658..951b4b0ab471c 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -485,7 +485,8 @@ public class BluetoothDevice { * Get the major, minor and servics classes of a remote device. * These classes are encoded as a 32-bit integer. See BluetoothClass. * @param address remote device - * @return 32-bit class suitable for use with BluetoothClass. + * @return 32-bit class suitable for use with BluetoothClass, or + * BluetoothClass.ERROR on error */ public int getRemoteClass(String address) { try { diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index f1a7d2571e76f..8e5cee9bbfba9 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -24,6 +24,7 @@ package android.server; +import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothError; import android.bluetooth.BluetoothHeadset; @@ -970,8 +971,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { */ public synchronized int getRemoteClass(String address) { if (!BluetoothDevice.checkBluetoothAddress(address)) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return -1; + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return BluetoothClass.ERROR; } return getRemoteClassNative(address); } @@ -1254,4 +1255,3 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { Log.d(TAG, msg); } } - diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index 796da1536b1c2..a0e0b84aed668 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -44,6 +44,8 @@ namespace android { +#define BLUETOOTH_CLASS_ERROR 0xFF000000 + #ifdef HAVE_BLUETOOTH // We initialize these variables when we load class // android.server.BluetoothDeviceService @@ -724,11 +726,11 @@ static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) { } static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { + jint result = BLUETOOTH_CLASS_ERROR; #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); native_data_t *nat = get_native_data(env, object); if (nat) { - jint ret = 0; const char *c_address = env->GetStringUTFChars(address, NULL); LOGV("... address = %s", c_address); @@ -744,17 +746,15 @@ static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { DBusError err; dbus_error_init(&err); if (!dbus_message_get_args(reply, &err, - DBUS_TYPE_UINT32, &ret, + DBUS_TYPE_UINT32, &result, DBUS_TYPE_INVALID)) { LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); } dbus_message_unref(reply); } - - return ret; } #endif - return 0; + return result; } static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object, From bed30e1b6ea4a1d71dbe5e731c274cc66974283a Mon Sep 17 00:00:00 2001 From: Dianne Hackborn <> Date: Tue, 31 Mar 2009 14:46:20 -0700 Subject: [PATCH 11/19] AI 143823: am: CL 143800 am: CL 143748 Fix issue #1743326 (More battery stats) Adds stats for: - Number of raw user events that have happened in the system. - Number of times user activity has been reported, dividied by UID and type of activity. - Duration of screen brightness levels in 4 buckets. - Per-UID tracking of who has turned on Wifi and how long we can attribute it being on because of them. Original author: hackbod Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143823 --- core/java/android/os/BatteryStats.java | 245 +++++++++++--- core/java/android/os/LocalPowerManager.java | 2 + .../android/internal/app/IBatteryStats.aidl | 7 +- .../android/internal/os/BatteryStatsImpl.java | 308 +++++++++++++++++- .../com/android/server/HardwareService.java | 19 +- .../android/server/PowerManagerService.java | 44 ++- .../java/com/android/server/WifiService.java | 44 ++- .../android/server/WindowManagerService.java | 5 + .../server/am/BatteryStatsService.java | 29 +- 9 files changed, 623 insertions(+), 80 deletions(-) diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index c747c43b81e7c..17594d410d701 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -40,19 +40,26 @@ public abstract class BatteryStats implements Parcelable { */ public static final int SENSOR = 3; + /** + * A constant indicating a a wifi turn on timer + * + * {@hide} + */ + public static final int WIFI_TURNED_ON = 4; + /** * A constant indicating a full wifi lock timer * * {@hide} */ - public static final int FULL_WIFI_LOCK = 4; + public static final int FULL_WIFI_LOCK = 5; /** * A constant indicating a scan wifi lock timer * * {@hide} */ - public static final int SCAN_WIFI_LOCK = 5; + public static final int SCAN_WIFI_LOCK = 6; /** * Include all of the data in the stats, including previously saved data. @@ -77,7 +84,7 @@ public abstract class BatteryStats implements Parcelable { /** * Bump the version on this if the checkin format changes. */ - private static final int BATTERY_STATS_CHECKIN_VERSION = 2; + private static final int BATTERY_STATS_CHECKIN_VERSION = 3; private static final long BYTES_PER_KB = 1024; private static final long BYTES_PER_MB = 1048576; // 1024^2 @@ -91,15 +98,38 @@ public abstract class BatteryStats implements Parcelable { private static final String SENSOR_DATA = "sensor"; private static final String WAKELOCK_DATA = "wakelock"; private static final String NETWORK_DATA = "network"; + private static final String USER_ACTIVITY_DATA = "useract"; private static final String BATTERY_DATA = "battery"; private static final String WIFI_LOCK_DATA = "wifilock"; private static final String MISC_DATA = "misc"; - private static final String SIGNAL_STRENGTH_DATA = "signal"; - private static final String DATA_CONNECTION_DATA = "dataconn"; + private static final String SCREEN_BRIGHTNESS_DATA = "brightness"; + private static final String SIGNAL_STRENGTH_TIME_DATA = "sigtime"; + private static final String SIGNAL_STRENGTH_COUNT_DATA = "sigcnt"; + private static final String DATA_CONNECTION_TIME_DATA = "dconntime"; + private static final String DATA_CONNECTION_COUNT_DATA = "dconncnt"; private final StringBuilder mFormatBuilder = new StringBuilder(8); private final Formatter mFormatter = new Formatter(mFormatBuilder); + /** + * State for keeping track of counting information. + */ + public static abstract class Counter { + + /** + * Returns the count associated with this Counter for the + * selected type of statistics. + * + * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT + */ + public abstract int getCount(int which); + + /** + * Temporary for debugging. + */ + public abstract void logState(Printer pw, String prefix); + } + /** * State for keeping track of timing information. */ @@ -184,13 +214,29 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getTcpBytesSent(int which); + public abstract void noteWifiTurnedOnLocked(); + public abstract void noteWifiTurnedOffLocked(); public abstract void noteFullWifiLockAcquiredLocked(); public abstract void noteFullWifiLockReleasedLocked(); public abstract void noteScanWifiLockAcquiredLocked(); public abstract void noteScanWifiLockReleasedLocked(); + public abstract long getWifiTurnedOnTime(long batteryRealtime, int which); public abstract long getFullWifiLockTime(long batteryRealtime, int which); public abstract long getScanWifiLockTime(long batteryRealtime, int which); + /** + * Note that these must match the constants in android.os.LocalPowerManager. + */ + static final String[] USER_ACTIVITY_TYPES = { + "other", "cheek", "touch", "long_touch", "touch_up", "button", "unknown" + }; + + public static final int NUM_USER_ACTIVITY_TYPES = 7; + + public abstract void noteUserActivityLocked(int type); + public abstract boolean hasUserActivity(); + public abstract int getUserActivityCount(int type, int which); + public static abstract class Sensor { // Magic sensor number for the GPS. public static final int GPS = -10000; @@ -289,6 +335,29 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getScreenOnTime(long batteryRealtime, int which); + public static final int SCREEN_BRIGHTNESS_DARK = 0; + public static final int SCREEN_BRIGHTNESS_DIM = 1; + public static final int SCREEN_BRIGHTNESS_MEDIUM = 2; + public static final int SCREEN_BRIGHTNESS_LIGHT = 3; + public static final int SCREEN_BRIGHTNESS_BRIGHT = 4; + + static final String[] SCREEN_BRIGHTNESS_NAMES = { + "dark", "dim", "medium", "light", "bright" + }; + + public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5; + + /** + * Returns the time in milliseconds that the screen has been on with + * the given brightness + * + * {@hide} + */ + public abstract long getScreenBrightnessTime(int brightnessBin, + long batteryRealtime, int which); + + public abstract int getInputEventCount(int which); + /** * Returns the time in milliseconds that the phone has been on while the device was * running on battery. @@ -318,6 +387,13 @@ public abstract class BatteryStats implements Parcelable { public abstract long getPhoneSignalStrengthTime(int strengthBin, long batteryRealtime, int which); + /** + * Returns the number of times the phone has entered the given signal strength. + * + * {@hide} + */ + public abstract int getPhoneSignalStrengthCount(int strengthBin, int which); + public static final int DATA_CONNECTION_NONE = 0; public static final int DATA_CONNECTION_GPRS = 1; public static final int DATA_CONNECTION_EDGE = 2; @@ -339,6 +415,14 @@ public abstract class BatteryStats implements Parcelable { public abstract long getPhoneDataConnectionTime(int dataType, long batteryRealtime, int which); + /** + * Returns the number of times the phone has entered the given data + * connection type. + * + * {@hide} + */ + public abstract int getPhoneDataConnectionCount(int dataType, int which); + /** * Returns the time in milliseconds that wifi has been on while the device was * running on battery. @@ -619,8 +703,8 @@ public abstract class BatteryStats implements Parcelable { // Dump "battery" stat dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, which == STATS_TOTAL ? getStartCount() : "N/A", - whichBatteryUptime / 1000, whichBatteryRealtime / 1000, - totalUptime / 1000, totalRealtime / 1000); + whichBatteryRealtime / 1000, whichBatteryUptime / 1000, + totalRealtime / 1000, totalUptime / 1000); // Calculate total network and wakelock times across all uids. long rxTotal = 0; @@ -657,21 +741,37 @@ public abstract class BatteryStats implements Parcelable { dumpLine(pw, 0 /* uid */, category, MISC_DATA, screenOnTime / 1000, phoneOnTime / 1000, wifiOnTime / 1000, wifiRunningTime / 1000, bluetoothOnTime / 1000, rxTotal, txTotal, - fullWakeLockTimeTotal, partialWakeLockTimeTotal); + fullWakeLockTimeTotal, partialWakeLockTimeTotal, + getInputEventCount(which)); + + // Dump screen brightness stats + Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS]; + for (int i=0; i 0 || tx > 0) dumpLine(pw, uid, category, NETWORK_DATA, rx, tx); - if (fullWifiLockOnTime != 0 || scanWifiLockOnTime != 0) { + if (fullWifiLockOnTime != 0 || scanWifiLockOnTime != 0 + || wifiTurnedOnTime != 0) { dumpLine(pw, uid, category, WIFI_LOCK_DATA, - fullWifiLockOnTime, scanWifiLockOnTime); + fullWifiLockOnTime, scanWifiLockOnTime, wifiTurnedOnTime); } + if (u.hasUserActivity()) { + args = new Object[Uid.NUM_USER_ACTIVITY_TYPES]; + boolean hasData = false; + for (int i=0; i wakelocks = u.getWakelockStats(); if (wakelocks.size() > 0) { for (Map.Entry ent @@ -800,18 +915,19 @@ public abstract class BatteryStats implements Parcelable { final int NU = uidStats.size(); pw.println(prefix - + " Time on battery: " + formatTimeMs(whichBatteryUptime / 1000) - + "(" + formatRatioLocked(whichBatteryUptime, totalRealtime) - + ") uptime, " + + " Time on battery: " + formatTimeMs(whichBatteryRealtime / 1000) + "(" + formatRatioLocked(whichBatteryRealtime, totalRealtime) - + ") realtime"); + + ") realtime, " + + formatTimeMs(whichBatteryUptime / 1000) + + "(" + formatRatioLocked(whichBatteryUptime, totalRealtime) + + ") uptime"); pw.println(prefix - + " Total: " - + formatTimeMs(totalUptime / 1000) - + "uptime, " + + " Total run time: " + formatTimeMs(totalRealtime / 1000) - + "realtime"); + + "realtime, " + + formatTimeMs(totalUptime / 1000) + + "uptime, "); final long screenOnTime = getScreenOnTime(batteryRealtime, which); final long phoneOnTime = getPhoneOnTime(batteryRealtime, which); @@ -821,15 +937,28 @@ public abstract class BatteryStats implements Parcelable { pw.println(prefix + " Screen on: " + formatTimeMs(screenOnTime / 1000) + "(" + formatRatioLocked(screenOnTime, whichBatteryRealtime) - + "), Phone on: " + formatTimeMs(phoneOnTime / 1000) - + "(" + formatRatioLocked(phoneOnTime, whichBatteryRealtime)); - pw.println(prefix - + " Wifi on: " + formatTimeMs(wifiOnTime / 1000) - + "(" + formatRatioLocked(wifiOnTime, whichBatteryRealtime) - + "), Wifi running: " + formatTimeMs(wifiRunningTime / 1000) - + "(" + formatRatioLocked(wifiRunningTime, whichBatteryRealtime) - + "), Bluetooth on: " + formatTimeMs(bluetoothOnTime / 1000) - + "(" + formatRatioLocked(bluetoothOnTime, whichBatteryRealtime)+ ")"); + + "), Input events: " + getInputEventCount(which) + + ", Active phone call: " + formatTimeMs(phoneOnTime / 1000) + + "(" + formatRatioLocked(phoneOnTime, whichBatteryRealtime) + ")"); + sb.setLength(0); + sb.append(" Screen brightnesses: "); + boolean didOne = false; + for (int i=0; i unpluggables, Parcel in) { + mPluggedCount = mCount = in.readInt(); + mLoadedCount = in.readInt(); + mLastCount = in.readInt(); + mUnpluggedCount = in.readInt(); + unpluggables.add(this); + } + + Counter(ArrayList unpluggables) { + unpluggables.add(this); + } + + public void writeToParcel(Parcel out) { + out.writeInt(mCount); + out.writeInt(mLoadedCount); + out.writeInt(mLastCount); + out.writeInt(mUnpluggedCount); + } + + public void unplug(long batteryUptime, long batteryRealtime) { + mUnpluggedCount = mCount = mPluggedCount; + } + + public void plug(long batteryUptime, long batteryRealtime) { + mPluggedCount = mCount; + } + + /** + * Writes a possibly null Counter to a Parcel. + * + * @param out the Parcel to be written to. + * @param counter a Counter, or null. + */ + public static void writeCounterToParcel(Parcel out, Counter counter) { + if (counter == null) { + out.writeInt(0); // indicates null + return; + } + out.writeInt(1); // indicates non-null + + counter.writeToParcel(out); + } + + @Override + public int getCount(int which) { + int val; + if (which == STATS_LAST) { + val = mLastCount; + } else { + val = mCount; + if (which == STATS_UNPLUGGED) { + val -= mUnpluggedCount; + } else if (which != STATS_TOTAL) { + val -= mLoadedCount; + } + } + + return val; + } + + public void logState(Printer pw, String prefix) { + pw.println(prefix + "mCount=" + mCount + + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount + + " mUnpluggedCount=" + mUnpluggedCount + + " mPluggedCount=" + mPluggedCount); + } + + void stepLocked() { + mCount++; + } + + void writeSummaryFromParcelLocked(Parcel out) { + out.writeInt(mCount); + out.writeInt(mCount - mLoadedCount); + } + + void readSummaryFromParcelLocked(Parcel in) { + mCount = mLoadedCount = in.readInt(); + mLastCount = in.readInt(); + mUnpluggedCount = mPluggedCount = mCount; + } + } + /** * State for keeping track of timing information. */ @@ -469,6 +568,9 @@ public final class BatteryStatsImpl extends BatteryStats { if (!mScreenOn) { mScreenOn = true; mScreenOnTimer.startRunningLocked(this); + if (mScreenBrightnessBin >= 0) { + mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this); + } } } @@ -476,6 +578,36 @@ public final class BatteryStatsImpl extends BatteryStats { if (mScreenOn) { mScreenOn = false; mScreenOnTimer.stopRunningLocked(this); + if (mScreenBrightnessBin >= 0) { + mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this); + } + } + } + + public void noteScreenBrightnessLocked(int brightness) { + // Bin the brightness. + int bin = brightness / (256/NUM_SCREEN_BRIGHTNESS_BINS); + if (bin < 0) bin = 0; + else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1; + if (mScreenBrightnessBin != bin) { + if (mScreenOn) { + if (mScreenBrightnessBin >= 0) { + mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this); + } + mScreenBrightnessTimer[bin].startRunningLocked(this); + } + mScreenBrightnessBin = bin; + } + } + + public void noteInputEventLocked() { + mInputEventCounter.stepLocked(); + } + + public void noteUserActivityLocked(int uid, int event) { + Uid u = mUidStats.get(uid); + if (u != null) { + u.noteUserActivityLocked(event); } } @@ -537,18 +669,38 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteWifiOnLocked() { + public void noteWifiOnLocked(int uid) { if (!mWifiOn) { mWifiOn = true; mWifiOnTimer.startRunningLocked(this); } + if (mWifiOnUid != uid) { + if (mWifiOnUid >= 0) { + Uid u = mUidStats.get(mWifiOnUid); + if (u != null) { + u.noteWifiTurnedOffLocked(); + } + } + mWifiOnUid = uid; + Uid u = mUidStats.get(uid); + if (u != null) { + u.noteWifiTurnedOnLocked(); + } + } } - public void noteWifiOffLocked() { + public void noteWifiOffLocked(int uid) { if (mWifiOn) { mWifiOn = false; mWifiOnTimer.stopRunningLocked(this); } + if (mWifiOnUid >= 0) { + Uid u = mUidStats.get(mWifiOnUid); + if (u != null) { + u.noteWifiTurnedOffLocked(); + } + mWifiOnUid = -1; + } } public void noteWifiRunningLocked() { @@ -611,6 +763,16 @@ public final class BatteryStatsImpl extends BatteryStats { return mScreenOnTimer.getTotalTime(batteryRealtime, which); } + @Override public long getScreenBrightnessTime(int brightnessBin, + long batteryRealtime, int which) { + return mScreenBrightnessTimer[brightnessBin].getTotalTime( + batteryRealtime, which); + } + + @Override public int getInputEventCount(int which) { + return mInputEventCounter.getCount(which); + } + @Override public long getPhoneOnTime(long batteryRealtime, int which) { return mPhoneOnTimer.getTotalTime(batteryRealtime, which); } @@ -621,12 +783,20 @@ public final class BatteryStatsImpl extends BatteryStats { batteryRealtime, which); } + @Override public int getPhoneSignalStrengthCount(int dataType, int which) { + return mPhoneDataConnectionsTimer[dataType].getCount(which); + } + @Override public long getPhoneDataConnectionTime(int dataType, long batteryRealtime, int which) { return mPhoneDataConnectionsTimer[dataType].getTotalTime( batteryRealtime, which); } + @Override public int getPhoneDataConnectionCount(int dataType, int which) { + return mPhoneDataConnectionsTimer[dataType].getCount(which); + } + @Override public long getWifiOnTime(long batteryRealtime, int which) { return mWifiOnTimer.getTotalTime(batteryRealtime, which); } @@ -665,12 +835,17 @@ public final class BatteryStatsImpl extends BatteryStats { long mStartedTcpBytesReceived = -1; long mStartedTcpBytesSent = -1; + boolean mWifiTurnedOn; + Timer mWifiTurnedOnTimer; + boolean mFullWifiLockOut; Timer mFullWifiLockTimer; boolean mScanWifiLockOut; Timer mScanWifiLockTimer; + Counter[] mUserActivityCounters; + /** * The statistics we have collected for this uid's wake locks. */ @@ -693,6 +868,7 @@ public final class BatteryStatsImpl extends BatteryStats { public Uid(int uid) { mUid = uid; + mWifiTurnedOnTimer = new Timer(WIFI_TURNED_ON, null, mUnpluggables); mFullWifiLockTimer = new Timer(FULL_WIFI_LOCK, null, mUnpluggables); mScanWifiLockTimer = new Timer(SCAN_WIFI_LOCK, null, mUnpluggables); } @@ -754,6 +930,22 @@ public final class BatteryStatsImpl extends BatteryStats { } } + @Override + public void noteWifiTurnedOnLocked() { + if (!mWifiTurnedOn) { + mWifiTurnedOn = true; + mWifiTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this); + } + } + + @Override + public void noteWifiTurnedOffLocked() { + if (mWifiTurnedOn) { + mWifiTurnedOn = false; + mWifiTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this); + } + } + @Override public void noteFullWifiLockAcquiredLocked() { if (!mFullWifiLockOut) { @@ -785,6 +977,12 @@ public final class BatteryStatsImpl extends BatteryStats { mScanWifiLockTimer.stopRunningLocked(BatteryStatsImpl.this); } } + + @Override + public long getWifiTurnedOnTime(long batteryRealtime, int which) { + return mWifiTurnedOnTimer.getTotalTime(batteryRealtime, which); + } + @Override public long getFullWifiLockTime(long batteryRealtime, int which) { return mFullWifiLockTimer.getTotalTime(batteryRealtime, which); @@ -795,6 +993,36 @@ public final class BatteryStatsImpl extends BatteryStats { return mScanWifiLockTimer.getTotalTime(batteryRealtime, which); } + @Override + public void noteUserActivityLocked(int type) { + if (mUserActivityCounters == null) { + initUserActivityLocked(); + } + if (type < 0) type = 0; + else if (type >= NUM_USER_ACTIVITY_TYPES) type = NUM_USER_ACTIVITY_TYPES-1; + mUserActivityCounters[type].stepLocked(); + } + + @Override + public boolean hasUserActivity() { + return mUserActivityCounters != null; + } + + @Override + public int getUserActivityCount(int type, int which) { + if (mUserActivityCounters == null) { + return 0; + } + return mUserActivityCounters[type].getCount(which); + } + + void initUserActivityLocked() { + mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES]; + for (int i=0; i= 0 ? (NetStat.getUidTxBytes(mUid) - mStartedTcpBytesSent) : 0); @@ -835,8 +1063,17 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(computeCurrentTcpBytesSent()); out.writeLong(mTcpBytesReceivedAtLastUnplug); out.writeLong(mTcpBytesSentAtLastUnplug); + mWifiTurnedOnTimer.writeToParcel(out, batteryRealtime); mFullWifiLockTimer.writeToParcel(out, batteryRealtime); mScanWifiLockTimer.writeToParcel(out, batteryRealtime); + if (mUserActivityCounters == null) { + out.writeInt(0); + } else { + out.writeInt(1); + for (int i=0; i unpluggables, Parcel in) { @@ -882,10 +1119,20 @@ public final class BatteryStatsImpl extends BatteryStats { mCurrentTcpBytesSent = in.readLong(); mTcpBytesReceivedAtLastUnplug = in.readLong(); mTcpBytesSentAtLastUnplug = in.readLong(); + mWifiTurnedOn = false; + mWifiTurnedOnTimer = new Timer(WIFI_TURNED_ON, null, mUnpluggables, in); mFullWifiLockOut = false; mFullWifiLockTimer = new Timer(FULL_WIFI_LOCK, null, mUnpluggables, in); mScanWifiLockOut = false; mScanWifiLockTimer = new Timer(SCAN_WIFI_LOCK, null, mUnpluggables, in); + if (in.readInt() == 0) { + mUserActivityCounters = null; + } else { + mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES]; + for (int i=0; i 0) { @@ -2265,13 +2545,17 @@ public final class BatteryStatsImpl extends BatteryStats { mBatteryLastRealtime = in.readLong(); mScreenOn = false; mScreenOnTimer = new Timer(-1, null, mUnpluggables, in); + for (int i=0; i 1.0f) ratio = 1.0f; if ((newState & SCREEN_ON_BIT) == 0) { - int steps; if ((oldState & SCREEN_BRIGHT_BIT) != 0) { // was bright steps = ANIM_STEPS; @@ -1471,10 +1474,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage // was dim steps = (int)(ANIM_STEPS*ratio*scale); } - mScreenBrightness.setTargetLocked(Power.BRIGHTNESS_OFF, - steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); + brightness = Power.BRIGHTNESS_OFF; } else { - int steps; if ((oldState & SCREEN_ON_BIT) != 0) { // was bright steps = (int)(ANIM_STEPS*(1.0f-ratio)*scale); @@ -1490,13 +1491,19 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage // will then count going dim as turning off. mScreenOffTime = SystemClock.elapsedRealtime(); } - mScreenBrightness.setTargetLocked(Power.BRIGHTNESS_DIM, - steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); + brightness = Power.BRIGHTNESS_DIM; } - } else { - mScreenBrightness.setTargetLocked(preferredBrightness, - ANIM_STEPS, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); } + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteScreenBrightness(brightness); + } catch (RemoteException e) { + // Nothing interesting to do. + } finally { + Binder.restoreCallingIdentity(identity); + } + mScreenBrightness.setTargetLocked(brightness, + steps, INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue); startAnimation = true; } else { if ((newState & SCREEN_BRIGHT_BIT) == 0) { @@ -1735,6 +1742,16 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage mUserState |= SCREEN_BRIGHT; } + int uid = Binder.getCallingUid(); + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteUserActivity(uid, eventType); + } catch (RemoteException e) { + // Ignore + } finally { + Binder.restoreCallingIdentity(ident); + } + reactivateWakeLocksLocked(); mWakeLockState = mLocks.gatherState(); setPowerState(mUserState | mWakeLockState, noChangeLights, true); @@ -1951,6 +1968,15 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage synchronized (mLocks) { Log.d(TAG, "system ready!"); mDoneBooting = true; + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteScreenBrightness(getPreferredBrightness()); + mBatteryStats.noteScreenOn(); + } catch (RemoteException e) { + // Nothing interesting to do. + } finally { + Binder.restoreCallingIdentity(identity); + } userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true); updateWakeLockLocked(); mLocks.notifyAll(); diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 8e1d053a675bb..54e77f0222380 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -47,6 +47,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.provider.Settings; import android.util.Log; @@ -167,6 +168,11 @@ public class WifiService extends IWifiManager.Stub { private char[] mScanResultBuffer; private boolean mNeedReconfig; + /* + * Last UID that asked to enable WIFI. + */ + private int mLastEnableUid = Process.myUid(); + /** * Number of allowed radio frequency channels in various regulatory domains. * This list is sufficient for 802.11b/g networks (2.4GHz range). @@ -239,7 +245,7 @@ public class WifiService extends IWifiManager.Stub { }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); - setWifiEnabledBlocking(wifiEnabled, false); + setWifiEnabledBlocking(wifiEnabled, false, Process.myUid()); } /** @@ -455,7 +461,8 @@ public class WifiService extends IWifiManager.Stub { synchronized (mWifiHandler) { sWakeLock.acquire(); - sendEnableMessage(enable, true); + mLastEnableUid = Binder.getCallingUid(); + sendEnableMessage(enable, true, Binder.getCallingUid()); } return true; @@ -465,10 +472,11 @@ public class WifiService extends IWifiManager.Stub { * Enables/disables Wi-Fi synchronously. * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off. * @param persist {@code true} if the setting should be persisted. + * @param uid The UID of the process making the request. * @return {@code true} if the operation succeeds (or if the existing state * is the same as the requested state) */ - private boolean setWifiEnabledBlocking(boolean enable, boolean persist) { + private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) { final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED; if (mWifiState == eventualWifiState) { @@ -478,18 +486,18 @@ public class WifiService extends IWifiManager.Stub { return false; } - setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING); + setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid); if (enable) { if (!WifiNative.loadDriver()) { Log.e(TAG, "Failed to load Wi-Fi driver."); - setWifiEnabledState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); return false; } if (!WifiNative.startSupplicant()) { WifiNative.unloadDriver(); Log.e(TAG, "Failed to start supplicant daemon."); - setWifiEnabledState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); return false; } registerForBroadcasts(); @@ -503,7 +511,7 @@ public class WifiService extends IWifiManager.Stub { boolean failedToStopSupplicantOrUnloadDriver = false; if (!WifiNative.stopSupplicant()) { Log.e(TAG, "Failed to stop supplicant daemon."); - setWifiEnabledState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); failedToStopSupplicantOrUnloadDriver = true; } @@ -513,7 +521,7 @@ public class WifiService extends IWifiManager.Stub { if (!WifiNative.unloadDriver()) { Log.e(TAG, "Failed to unload Wi-Fi driver."); if (!failedToStopSupplicantOrUnloadDriver) { - setWifiEnabledState(WIFI_STATE_UNKNOWN); + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); failedToStopSupplicantOrUnloadDriver = true; } } @@ -527,7 +535,7 @@ public class WifiService extends IWifiManager.Stub { if (persist) { persistWifiEnabled(enable); } - setWifiEnabledState(eventualWifiState); + setWifiEnabledState(eventualWifiState, uid); /* * Initialize the hidden networks state and the number of allowed @@ -541,15 +549,15 @@ public class WifiService extends IWifiManager.Stub { return true; } - private void setWifiEnabledState(int wifiState) { + private void setWifiEnabledState(int wifiState, int uid) { final int previousWifiState = mWifiState; long ident = Binder.clearCallingIdentity(); try { if (wifiState == WIFI_STATE_ENABLED) { - mBatteryStats.noteWifiOn(); + mBatteryStats.noteWifiOn(uid); } else if (wifiState == WIFI_STATE_DISABLED) { - mBatteryStats.noteWifiOff(); + mBatteryStats.noteWifiOff(uid); } } catch (RemoteException e) { } finally { @@ -1571,10 +1579,10 @@ public class WifiService extends IWifiManager.Stub { } }; - private void sendEnableMessage(boolean enable, boolean persist) { + private void sendEnableMessage(boolean enable, boolean persist, int uid) { Message msg = Message.obtain(mWifiHandler, (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI), - (persist ? 1 : 0), 0); + (persist ? 1 : 0), uid); msg.sendToTarget(); } @@ -1602,7 +1610,7 @@ public class WifiService extends IWifiManager.Stub { if (wifiShouldBeEnabled) { if (wifiShouldBeStarted) { sWakeLock.acquire(); - sendEnableMessage(true, false); + sendEnableMessage(true, false, mLastEnableUid); sWakeLock.acquire(); sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); } else { @@ -1626,7 +1634,7 @@ public class WifiService extends IWifiManager.Stub { } } else { sWakeLock.acquire(); - sendEnableMessage(false, false); + sendEnableMessage(false, false, mLastEnableUid); } } } @@ -1671,7 +1679,7 @@ public class WifiService extends IWifiManager.Stub { switch (msg.what) { case MESSAGE_ENABLE_WIFI: - setWifiEnabledBlocking(true, msg.arg1 == 1); + setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2); sWakeLock.release(); break; @@ -1684,7 +1692,7 @@ public class WifiService extends IWifiManager.Stub { case MESSAGE_DISABLE_WIFI: // a non-zero msg.arg1 value means the "enabled" setting // should be persisted - setWifiEnabledBlocking(false, msg.arg1 == 1); + setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2); sWakeLock.release(); break; diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 2e16157fa294c..270d15f6e7021 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5085,6 +5085,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } else { eventType = LocalPowerManager.OTHER_EVENT; } + try { + mBatteryStats.noteInputEvent(); + } catch (RemoteException e) { + // Ignore + } mPowerManager.userActivity(curTime, false, eventType); switch (ev.classType) { case RawInputEvent.CLASS_KEYBOARD: diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 2192e062c2284..9ad994c52c1d0 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -129,6 +129,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } + public void noteScreenBrightness(int brightness) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteScreenBrightnessLocked(brightness); + } + } + public void noteScreenOff() { enforceCallingPermission(); synchronized (mStats) { @@ -136,6 +143,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } + public void noteInputEvent() { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteInputEventLocked(); + } + } + + public void noteUserActivity(int uid, int event) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteUserActivityLocked(uid, event); + } + } + public void notePhoneOn() { enforceCallingPermission(); synchronized (mStats) { @@ -164,17 +185,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } - public void noteWifiOn() { + public void noteWifiOn(int uid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteWifiOnLocked(); + mStats.noteWifiOnLocked(uid); } } - public void noteWifiOff() { + public void noteWifiOff(int uid) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteWifiOffLocked(); + mStats.noteWifiOffLocked(uid); } } From 7474a5acbe5b73342e4cf129d39cd3bc1195292c Mon Sep 17 00:00:00 2001 From: Romain Guy <> Date: Tue, 31 Mar 2009 14:50:15 -0700 Subject: [PATCH 12/19] AI 143830: am: CL 143815 am: CL 143758 Fixes #1749387. When a client of VelocityTracker obtains an instance from the pool, actually remove the object from the pool. Otherwise, several clients can share the same VelocityTracker which can lead to really weird side effects (including concurrency and UI issues.) Original author: romainguy Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143830 --- core/java/android/view/VelocityTracker.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index c80167ef5542f..3951b2c323cf2 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -58,6 +58,7 @@ public final class VelocityTracker { VelocityTracker vt = mPool[0]; if (vt != null) { vt.clear(); + mPool[0] = null; return vt; } return new VelocityTracker(); From f70188aa4716625781d9952c6b883180528d4644 Mon Sep 17 00:00:00 2001 From: Andy McFadden <> Date: Tue, 31 Mar 2009 15:52:13 -0700 Subject: [PATCH 13/19] AI 143840: Split VM initialization out into a separate function. This makes the code marginally more readable, and cuts about 500 bytes off the size of the main thread's stack. Automated import of CL 143840 --- core/jni/AndroidRuntime.cpp | 94 +++++++++++++++--------- include/android_runtime/AndroidRuntime.h | 1 + 2 files changed, 62 insertions(+), 33 deletions(-) diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 7dd59c4d5f03c..3c162feb6dd48 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -493,11 +493,17 @@ static void readLocale(char* language, char* region) //LOGD("language=%s region=%s\n", language, region); } -void AndroidRuntime::start(const char* className, const bool startSystemServer) +/* + * Start the Dalvik Virtual Machine. + * + * Various arguments, most determined by system properties, are passed in. + * The "mOptions" vector is updated. + * + * Returns 0 on success. + */ +int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) { - LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n"); - - JNIEnv* env; + int result = -1; JavaVMInitArgs initArgs; JavaVMOption opt; char propBuf[PROPERTY_VALUE_MAX]; @@ -506,24 +512,10 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX]; char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX]; char* stackTraceFile = NULL; - char* slashClassName = NULL; - char* cp; bool checkJni = false; bool logStdio = false; enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault; - blockSigpipe(); - - /* - * 'startSystemServer == true' means runtime is obslete and not run from - * init.rc anymore, so we print out the boot start event here. - */ - if (startSystemServer) { - /* track our progress through the boot sequence */ - const int LOG_BOOT_PROGRESS_START = 3000; - LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, - ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - } property_get("dalvik.vm.checkjni", propBuf, ""); if (strcmp(propBuf, "true") == 0) { @@ -556,19 +548,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) strcpy(jniOptsBuf, "-Xjniopts:"); property_get("dalvik.vm.jniopts", jniOptsBuf+10, ""); - const char* rootDir = getenv("ANDROID_ROOT"); - if (rootDir == NULL) { - rootDir = "/system"; - if (!hasDir("/system")) { - LOG_FATAL("No root directory specified, and /android does not exist."); - return; - } - setenv("ANDROID_ROOT", rootDir, 1); - } - - const char* kernelHack = getenv("LD_ASSUME_KERNEL"); - //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); - /* route exit() to our handler */ opt.extraInfo = (void*) runtime_exit; opt.optionString = "exit"; @@ -759,11 +738,61 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) * If this call succeeds, the VM is ready, and we can start issuing * JNI calls. */ - if (JNI_CreateJavaVM(&mJavaVM, &env, &initArgs) < 0) { + if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { LOGE("JNI_CreateJavaVM failed\n"); goto bail; } + result = 0; + +bail: + free(stackTraceFile); + return result; +} + +/* + * Start the Android runtime. This involves starting the virtual machine + * and calling the "static void main(String[] args)" method in the class + * named by "className". + */ +void AndroidRuntime::start(const char* className, const bool startSystemServer) +{ + LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n"); + + char* slashClassName = NULL; + char* cp; + JNIEnv* env; + + blockSigpipe(); + + /* + * 'startSystemServer == true' means runtime is obslete and not run from + * init.rc anymore, so we print out the boot start event here. + */ + if (startSystemServer) { + /* track our progress through the boot sequence */ + const int LOG_BOOT_PROGRESS_START = 3000; + LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, + ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + } + + const char* rootDir = getenv("ANDROID_ROOT"); + if (rootDir == NULL) { + rootDir = "/system"; + if (!hasDir("/system")) { + LOG_FATAL("No root directory specified, and /android does not exist."); + goto bail; + } + setenv("ANDROID_ROOT", rootDir, 1); + } + + //const char* kernelHack = getenv("LD_ASSUME_KERNEL"); + //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); + + /* start the virtual machine */ + if (startVm(&mJavaVM, &env) != 0) + goto bail; + /* * Register android functions. */ @@ -833,7 +862,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) bail: free(slashClassName); - free(stackTraceFile); } void AndroidRuntime::start() diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h index 78bef91e8595b..0304309963cef 100644 --- a/include/android_runtime/AndroidRuntime.h +++ b/include/android_runtime/AndroidRuntime.h @@ -98,6 +98,7 @@ public: private: static int startReg(JNIEnv* env); + int startVm(JavaVM** pJavaVM, JNIEnv** pEnv); Vector mOptions; From 2c62f84add1fdddd51b38d0cc373be6b8b75a28b Mon Sep 17 00:00:00 2001 From: Brett Chabot <> Date: Tue, 31 Mar 2009 17:07:19 -0700 Subject: [PATCH 14/19] AI 143872: am: CL 143870 am: CL 143869 Change InstrumentationTestRunner so all tests in the application are run when no other arguments are supplied, instead of running only tests in the app's package. Original author: brettchabot Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143872 --- .../android/test/ClassPathPackageInfoSource.java | 5 ++++- .../android/test/InstrumentationTestRunner.java | 15 ++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test-runner/android/test/ClassPathPackageInfoSource.java b/test-runner/android/test/ClassPathPackageInfoSource.java index 12bc7f3a00e45..877075f2d2af9 100644 --- a/test-runner/android/test/ClassPathPackageInfoSource.java +++ b/test-runner/android/test/ClassPathPackageInfoSource.java @@ -226,8 +226,11 @@ public class ClassPathPackageInfoSource { String className = apkClassNames.nextElement(); if (className.startsWith(packageName)) { + String subPackageName = packageName; int lastPackageSeparator = className.lastIndexOf('.'); - String subPackageName = className.substring(0, lastPackageSeparator); + if (lastPackageSeparator > 0) { + subPackageName = className.substring(0, lastPackageSeparator); + } if (subPackageName.length() > packageName.length()) { subpackageNames.add(subPackageName); } else if (isToplevelClass(className)) { diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java index f038612cbd0ed..044f55581a3d9 100644 --- a/test-runner/android/test/InstrumentationTestRunner.java +++ b/test-runner/android/test/InstrumentationTestRunner.java @@ -300,16 +300,17 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu } if (testClassesArg == null) { - TestSuite testSuite = null; if (mPackageOfTests != null) { testSuiteBuilder.includePackages(mPackageOfTests); } else { - testSuite = getTestSuite(); - testSuiteBuilder.addTestSuite(testSuite); - } - - if (testSuite == null) { - testSuiteBuilder.includePackages(getTargetContext().getPackageName()); + TestSuite testSuite = getTestSuite(); + if (testSuite != null) { + testSuiteBuilder.addTestSuite(testSuite); + } else { + // no package or class bundle arguments were supplied, and no test suite + // provided so add all tests in application + testSuiteBuilder.includePackages(""); + } } } else { parseTestClasses(testClassesArg, testSuiteBuilder); From d1b3dd058d2015cd1f2a3ef7fb3798f0d7923fe3 Mon Sep 17 00:00:00 2001 From: Romain Guy <> Date: Tue, 31 Mar 2009 17:53:57 -0700 Subject: [PATCH 15/19] AI 143894: am: CL 143890 Fixes #1749387. Improve the pooling of the VelocityTracker class. This introduces a new, hidden, API for pooling objects easily. Original author: romainguy Merged from: //branches/donutburger/... Automated import of CL 143894 --- core/java/android/util/FinitePool.java | 86 ++++++++++++++++++++ core/java/android/util/Pool.java | 25 ++++++ core/java/android/util/PoolFactory.java | 41 ++++++++++ core/java/android/util/Poolable.java | 25 ++++++ core/java/android/util/PoolableManager.java | 27 ++++++ core/java/android/util/SynchronizedPool.java | 48 +++++++++++ core/java/android/view/VelocityTracker.java | 65 ++++++++++----- core/java/android/view/View.java | 62 +++++++------- 8 files changed, 324 insertions(+), 55 deletions(-) create mode 100644 core/java/android/util/FinitePool.java create mode 100644 core/java/android/util/Pool.java create mode 100644 core/java/android/util/PoolFactory.java create mode 100644 core/java/android/util/Poolable.java create mode 100644 core/java/android/util/PoolableManager.java create mode 100644 core/java/android/util/SynchronizedPool.java diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java new file mode 100644 index 0000000000000..3ef82930e9a90 --- /dev/null +++ b/core/java/android/util/FinitePool.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 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 android.util; + +/** + * @hide + */ +class FinitePool> implements Pool { + /** + * Factory used to create new pool objects + */ + private final PoolableManager mManager; + /** + * Maximum number of objects in the pool + */ + private final int mLimit; + /** + * If true, mLimit is ignored + */ + private final boolean mInfinite; + + /** + * Next object to acquire + */ + private T mRoot; + /** + * Number of objects in the pool + */ + private int mPoolCount; + + FinitePool(PoolableManager manager) { + mManager = manager; + mLimit = 0; + mInfinite = true; + } + + FinitePool(PoolableManager manager, int limit) { + if (limit <= 0) throw new IllegalArgumentException("The pool limit must be > 0"); + + mManager = manager; + mLimit = limit; + mInfinite = false; + } + + public T acquire() { + T element; + + if (mRoot != null) { + element = mRoot; + mRoot = element.getNextPoolable(); + mPoolCount--; + } else { + element = mManager.newInstance(); + } + + if (element != null) { + element.setNextPoolable(null); + mManager.onAcquired(element); + } + + return element; + } + + public void release(T element) { + if (mInfinite || mPoolCount < mLimit) { + mPoolCount++; + element.setNextPoolable(mRoot); + mRoot = element; + } + mManager.onReleased(element); + } +} diff --git a/core/java/android/util/Pool.java b/core/java/android/util/Pool.java new file mode 100644 index 0000000000000..8cd4f3ed07a05 --- /dev/null +++ b/core/java/android/util/Pool.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2009 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 android.util; + +/** + * @hide + */ +public interface Pool> { + public abstract T acquire(); + public abstract void release(T element); +} diff --git a/core/java/android/util/PoolFactory.java b/core/java/android/util/PoolFactory.java new file mode 100644 index 0000000000000..4f72bf7c4b169 --- /dev/null +++ b/core/java/android/util/PoolFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 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 android.util; + +/** + * @hide + */ +public class PoolFactory { + private PoolFactory() { + } + + public static > Pool simplePool(PoolableManager manager) { + return new FinitePool(manager); + } + + public static > Pool finitePool(PoolableManager manager, int limit) { + return new FinitePool(manager, limit); + } + + public static > Pool synchronizedPool(Pool pool) { + return new SynchronizedPool(pool); + } + + public static > Pool synchronizedPool(Pool pool, Object lock) { + return new SynchronizedPool(pool, lock); + } +} diff --git a/core/java/android/util/Poolable.java b/core/java/android/util/Poolable.java new file mode 100644 index 0000000000000..fd9bd9b7f136d --- /dev/null +++ b/core/java/android/util/Poolable.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2009 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 android.util; + +/** + * @hide + */ +public interface Poolable { + void setNextPoolable(T element); + T getNextPoolable(); +} diff --git a/core/java/android/util/PoolableManager.java b/core/java/android/util/PoolableManager.java new file mode 100644 index 0000000000000..8773e6339b31c --- /dev/null +++ b/core/java/android/util/PoolableManager.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009 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 android.util; + +/** + * @hide + */ +public interface PoolableManager> { + T newInstance(); + + void onAcquired(T element); + void onReleased(T element); +} diff --git a/core/java/android/util/SynchronizedPool.java b/core/java/android/util/SynchronizedPool.java new file mode 100644 index 0000000000000..651e0c335e8b6 --- /dev/null +++ b/core/java/android/util/SynchronizedPool.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 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 android.util; + +/** + * + * @hide + */ +class SynchronizedPool> implements Pool { + private final Pool mPool; + private final Object mLock; + + public SynchronizedPool(Pool pool) { + mPool = pool; + mLock = this; + } + + public SynchronizedPool(Pool pool, Object lock) { + mPool = pool; + mLock = lock; + } + + public T acquire() { + synchronized (mLock) { + return mPool.acquire(); + } + } + + public void release(T element) { + synchronized (mLock) { + mPool.release(element); + } + } +} diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 3951b2c323cf2..256525a8b3f69 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -18,6 +18,10 @@ package android.view; import android.util.Config; import android.util.Log; +import android.util.Poolable; +import android.util.Pool; +import android.util.PoolFactory; +import android.util.PoolableManager; /** * Helper for tracking the velocity of touch events, for implementing @@ -28,53 +32,72 @@ import android.util.Log; * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()} * and {@link #getXVelocity()}. */ -public final class VelocityTracker { +public final class VelocityTracker implements Poolable { static final String TAG = "VelocityTracker"; static final boolean DEBUG = false; static final boolean localLOGV = DEBUG || Config.LOGV; - + static final int NUM_PAST = 10; static final int LONGEST_PAST_TIME = 200; - + static final VelocityTracker[] mPool = new VelocityTracker[1]; - + private static final Pool sPool = PoolFactory.synchronizedPool( + PoolFactory.finitePool(new PoolableManager() { + public VelocityTracker newInstance() { + return new VelocityTracker(); + } + + public void onAcquired(VelocityTracker element) { + element.clear(); + } + + public void onReleased(VelocityTracker element) { + } + }, 2)); + final float mPastX[] = new float[NUM_PAST]; final float mPastY[] = new float[NUM_PAST]; final long mPastTime[] = new long[NUM_PAST]; - + float mYVelocity; float mXVelocity; - + + private VelocityTracker mNext; + /** * Retrieve a new VelocityTracker object to watch the velocity of a * motion. Be sure to call {@link #recycle} when done. You should * generally only maintain an active object while tracking a movement, * so that the VelocityTracker can be re-used elsewhere. - * + * * @return Returns a new VelocityTracker. */ static public VelocityTracker obtain() { - synchronized (mPool) { - VelocityTracker vt = mPool[0]; - if (vt != null) { - vt.clear(); - mPool[0] = null; - return vt; - } - return new VelocityTracker(); - } + return sPool.acquire(); } - + /** * Return a VelocityTracker object back to be re-used by others. You must * not touch the object after calling this function. */ public void recycle() { - synchronized (mPool) { - mPool[0] = this; - } + sPool.release(this); } - + + /** + * @hide + */ + public void setNextPoolable(VelocityTracker element) { + mNext = element; + } + + /** + * @hide + */ + public VelocityTracker getNextPoolable() { + return mNext; + } + private VelocityTracker() { } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 04447ca4719df..9f4143cee60d3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -45,6 +45,10 @@ import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; import android.util.SparseArray; +import android.util.Poolable; +import android.util.Pool; +import android.util.PoolFactory; +import android.util.PoolableManager; import android.view.ContextMenu.ContextMenuInfo; import android.view.animation.Animation; import android.view.inputmethod.InputConnection; @@ -7827,26 +7831,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback { * For performance purposes, this class also implements a pool of up to * POOL_LIMIT objects that get reused. This reduces memory allocations * whenever possible. - * - * The pool is implemented as a linked list of InvalidateInfo object with - * the root pointing to the next available InvalidateInfo. If the root - * is null (i.e. when all instances from the pool have been acquired), - * then a new InvalidateInfo is created and returned to the caller. - * - * An InvalidateInfo is sent back to the pool by calling its release() - * method. If the pool is full the object is simply discarded. - * - * This implementation follows the object pool pattern used in the - * MotionEvent class. */ - static class InvalidateInfo { + static class InvalidateInfo implements Poolable { private static final int POOL_LIMIT = 10; - private static final Object sLock = new Object(); + private static final Pool sPool = PoolFactory.synchronizedPool( + PoolFactory.finitePool(new PoolableManager() { + public InvalidateInfo newInstance() { + return new InvalidateInfo(); + } - private static int sAcquiredCount = 0; - private static InvalidateInfo sRoot; + public void onAcquired(InvalidateInfo element) { + } - private InvalidateInfo next; + public void onReleased(InvalidateInfo element) { + } + }, POOL_LIMIT) + ); + + private InvalidateInfo mNext; View target; @@ -7855,28 +7857,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback { int right; int bottom; + public void setNextPoolable(InvalidateInfo element) { + mNext = element; + } + + public InvalidateInfo getNextPoolable() { + return mNext; + } + static InvalidateInfo acquire() { - synchronized (sLock) { - if (sRoot == null) { - return new InvalidateInfo(); - } - - InvalidateInfo info = sRoot; - sRoot = info.next; - sAcquiredCount--; - - return info; - } + return sPool.acquire(); } void release() { - synchronized (sLock) { - if (sAcquiredCount < POOL_LIMIT) { - sAcquiredCount++; - next = sRoot; - sRoot = this; - } - } + sPool.release(this); } } From 7ac3f67c179ec77caeee59b86d87d4ec007c4586 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn <> Date: Tue, 31 Mar 2009 18:00:53 -0700 Subject: [PATCH 16/19] AI 143901: am: CL 143899 am: CL 143896 Fix issue #1748954 and #1737952: #1748954 (New status bar fades into all white background): FrameLayout wasn't updating its foreground drawable when its padding changed, which would happen as the status bar is shown and hidden. To fix this I also ended up fixing a problem in the view debug stuff where we couldn't get a bitmap for a view that is the full screen size because it is too big... actually I just went ahead and added another function to snapshot the view hierarchy which works a lot better for us anyway. #1737952 (Home screen icons overlap with the notification bar after exiting any camera app): Originally I punted this because it only happened in rare situations, but now that home is always portrait it happens a lot more so it is more important to fix. This involved a few things to clean up hiding/showing the status bar: - We now determine when to hide and show it during layout, which allows us to do this at the time it is actually needed rather than during animation after we can actually catch it for the initial display of a window. This required tweaking the layout API so the policy can request a second layout pass if needed. - When doing layout, we are now much more aggressive about skipping the layout of windows. Basically anything that we know will be hidden in the near future is ignored for layout, so that it doesn't glitch as it is transfered out of the screen. The theory being that it is better to leave it as it was originally placed while we are transitioning it out, than to switch it to something slightly more correct. Original author: hackbod Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143901 --- core/java/android/view/View.java | 58 ++++++++++++++++++- core/java/android/view/ViewDebug.java | 33 ++++------- .../android/view/WindowManagerPolicy.java | 5 +- core/java/android/widget/FrameLayout.java | 54 +++++++++++------ .../android/server/WindowManagerService.java | 27 +++++++-- 5 files changed, 131 insertions(+), 46 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9f4143cee60d3..8a6521924bdb2 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5535,7 +5535,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE); if (width <= 0 || height <= 0 || - (width * height * (opaque ? 2 : 4) >= // Projected bitmap size in bytes + (width * height * (opaque ? 2 : 4) > // Projected bitmap size in bytes ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { destroyDrawingCache(); return; @@ -5631,6 +5631,62 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } } + /** + * Create a snapshot of the view into a bitmap. We should probably make + * some form of this public, but should think about the API. + */ + /*package*/ Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor) { + final int width = mRight - mLeft; + final int height = mBottom - mTop; + + Bitmap bitmap = Bitmap.createBitmap(width, height, quality); + if (bitmap == null) { + throw new OutOfMemoryError(); + } + + Canvas canvas; + final AttachInfo attachInfo = mAttachInfo; + if (attachInfo != null) { + canvas = attachInfo.mCanvas; + if (canvas == null) { + canvas = new Canvas(); + } + canvas.setBitmap(bitmap); + // Temporarily clobber the cached Canvas in case one of our children + // is also using a drawing cache. Without this, the children would + // steal the canvas by attaching their own bitmap to it and bad, bad + // things would happen (invisible views, corrupted drawings, etc.) + attachInfo.mCanvas = null; + } else { + // This case should hopefully never or seldom happen + canvas = new Canvas(bitmap); + } + + if ((backgroundColor&0xff000000) != 0) { + bitmap.eraseColor(backgroundColor); + } + + computeScroll(); + final int restoreCount = canvas.save(); + canvas.translate(-mScrollX, -mScrollY); + + // Fast path for layouts with no backgrounds + if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { + dispatchDraw(canvas); + } else { + draw(canvas); + } + + canvas.restoreToCount(restoreCount); + + if (attachInfo != null) { + // Restore the cached Canvas for our siblings + attachInfo.mCanvas = canvas; + } + + return bitmap; + } + /** * Indicates whether this View is currently in edit mode. A View is usually * in edit mode when displayed within a developer tool. For instance, if diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index f604bc56a06e7..c1e9ed8efa0b2 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -741,22 +741,18 @@ public class ViewDebug { final CountDownLatch latch = new CountDownLatch(1); final Bitmap[] cache = new Bitmap[1]; - final boolean hasCache = captureView.isDrawingCacheEnabled(); - final boolean willNotCache = captureView.willNotCacheDrawing(); - - if (willNotCache) { - // TODO: Should happen on the UI thread - captureView.setWillNotCacheDrawing(false); - } - root.post(new Runnable() { public void run() { try { - if (!hasCache) { - captureView.buildDrawingCache(); + cache[0] = captureView.createSnapshot( + Bitmap.Config.ARGB_8888, 0); + } catch (OutOfMemoryError e) { + try { + cache[0] = captureView.createSnapshot( + Bitmap.Config.ARGB_4444, 0); + } catch (OutOfMemoryError e2) { + Log.w("View", "Out of memory for bitmap"); } - - cache[0] = captureView.getDrawingCache(); } finally { latch.countDown(); } @@ -776,20 +772,15 @@ public class ViewDebug { if (out != null) { out.close(); } + cache[0].recycle(); } + } else { + Log.w("View", "Failed to create capture bitmap!"); + clientStream.close(); } } catch (InterruptedException e) { Log.w("View", "Could not complete the capture of the view " + captureView); Thread.currentThread().interrupt(); - } finally { - if (willNotCache) { - // TODO: Should happen on the UI thread - captureView.setWillNotCacheDrawing(true); - } - if (!hasCache) { - // TODO: Should happen on the UI thread - captureView.destroyDrawingCache(); - } } } } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 220869c1d236e..13719323cb68b 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -580,8 +580,11 @@ public interface WindowManagerPolicy { * Called when layout of the windows is finished. After this function has * returned, all windows given to layoutWindow() must have had a * frame assigned. + * + * @return Return true if layout state may have changed (so that another + * layout will be performed). */ - public void finishLayoutLw(); + public boolean finishLayoutLw(); /** * Called when animation of the windows is about to start. diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 47db6f2745382..80fbf9eddecfd 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -24,6 +24,7 @@ import android.graphics.Region; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; +import android.view.ViewDebug; import android.view.ViewGroup; import android.view.Gravity; import android.widget.RemoteViews.RemoteView; @@ -45,21 +46,31 @@ import android.widget.RemoteViews.RemoteView; */ @RemoteView public class FrameLayout extends ViewGroup { + @ViewDebug.ExportedProperty boolean mMeasureAllChildren = false; + @ViewDebug.ExportedProperty private Drawable mForeground; + @ViewDebug.ExportedProperty private int mForegroundPaddingLeft = 0; + @ViewDebug.ExportedProperty private int mForegroundPaddingTop = 0; + @ViewDebug.ExportedProperty private int mForegroundPaddingRight = 0; + @ViewDebug.ExportedProperty private int mForegroundPaddingBottom = 0; private final Rect mSelfBounds = new Rect(); private final Rect mOverlayBounds = new Rect(); + @ViewDebug.ExportedProperty private int mForegroundGravity = Gravity.FILL; /** {@hide} */ + @ViewDebug.ExportedProperty protected boolean mForegroundInPadding = true; + boolean mForegroundBoundsChanged = false; + public FrameLayout(Context context) { super(context); } @@ -269,6 +280,8 @@ public class FrameLayout extends ViewGroup { final int parentTop = mPaddingTop + mForegroundPaddingTop; final int parentBottom = bottom - top - mPaddingBottom - mForegroundPaddingBottom; + mForegroundBoundsChanged = true; + for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { @@ -328,22 +341,7 @@ public class FrameLayout extends ViewGroup { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - - final Drawable foreground = mForeground; - if (foreground != null) { - final Rect selfBounds = mSelfBounds; - final Rect overlayBounds = mOverlayBounds; - - if (mForegroundInPadding) { - selfBounds.set(0, 0, w, h); - } else { - selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); - } - - Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), - foreground.getIntrinsicHeight(), selfBounds, overlayBounds); - foreground.setBounds(overlayBounds); - } + mForegroundBoundsChanged = true; } /** @@ -354,7 +352,29 @@ public class FrameLayout extends ViewGroup { super.draw(canvas); if (mForeground != null) { - mForeground.draw(canvas); + final Drawable foreground = mForeground; + if (mForegroundBoundsChanged) { + mForegroundBoundsChanged = false; + if (foreground != null) { + final Rect selfBounds = mSelfBounds; + final Rect overlayBounds = mOverlayBounds; + + final int w = mRight-mLeft; + final int h = mBottom-mTop; + + if (mForegroundInPadding) { + selfBounds.set(0, 0, w, h); + } else { + selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom); + } + + Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), + foreground.getIntrinsicHeight(), selfBounds, overlayBounds); + foreground.setBounds(overlayBounds); + } + } + + foreground.draw(canvas); } } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 270d15f6e7021..0b1ddc846be5d 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -7485,11 +7485,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final int dh = mDisplay.getHeight(); final int N = mWindows.size(); + int repeats = 0; int i; // FIRST LOOP: Perform a layout, if needed. - if (mLayoutNeeded) { + while (mLayoutNeeded) { mPolicy.beginLayoutLw(dw, dh); // First perform layout of any root windows (not attached @@ -7497,10 +7498,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo int topAttached = -1; for (i = N-1; i >= 0; i--) { WindowState win = (WindowState) mWindows.get(i); - - boolean gone = win.mViewVisibility == View.GONE + + // Don't do layout of a window if it is not visible, or + // soon won't be visible, to avoid wasting time and funky + // changes while a window is animating away. + final AppWindowToken atoken = win.mAppToken; + final boolean gone = win.mViewVisibility == View.GONE || !win.mRelayoutCalled - || win.mRootToken.hidden; + || win.mRootToken.hidden + || (atoken != null && atoken.hiddenRequested) + || !win.mPolicyVisibility + || win.mAttachedHidden + || win.mExiting || win.mDestroying; // If this view is GONE, then skip it -- keep the current // frame, and let the caller know so they can ignore it @@ -7536,8 +7545,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } - mPolicy.finishLayoutLw(); - mLayoutNeeded = false; + if (!mPolicy.finishLayoutLw()) { + mLayoutNeeded = false; + } else if (repeats > 2) { + Log.w(TAG, "Layout repeat aborted after too many iterations"); + mLayoutNeeded = false; + } else { + repeats++; + } } } From 8696e0156c66fae35c7644ea732b213c62d5c972 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt <> Date: Tue, 31 Mar 2009 18:02:21 -0700 Subject: [PATCH 17/19] AI 143902: am: CL 143900 am: CL 143898 Attempt to set the device Locale (if not already set) at phone init based on the phone's reported carrier ID. Uses a core system resource string-array to contain the mapping of carrier ID -> default locale. This should be set per project in an overlay. Original author: rgreenwalt Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143902 --- core/res/res/values/strings.xml | 5 +- .../android/internal/telephony/PhoneBase.java | 94 +++++++++++++++++++ .../internal/telephony/gsm/SIMRecords.java | 48 +--------- 3 files changed, 101 insertions(+), 46 deletions(-) diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6cfdf8f0e8a0e..e0bc9d21072db 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2321,5 +2321,8 @@ Create contact\nusing %s - + + + + diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 580814f0a020d..4fb5f6119f708 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -16,15 +16,22 @@ package com.android.internal.telephony; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.content.Context; +import android.content.res.Configuration; import android.os.AsyncResult; import android.os.Handler; import android.os.Looper; import android.os.RegistrantList; +import android.os.SystemProperties; import android.telephony.ServiceState; +import android.util.Log; +import com.android.internal.R; import com.android.internal.telephony.test.SimulatedRadioControl; import java.util.List; +import java.util.Locale; /** * (Not for SDK use) @@ -109,6 +116,8 @@ public abstract class PhoneBase implements Phone { this.mContext = context; mLooper = Looper.myLooper(); + setLocaleByCarrier(); + setUnitTestMode(unitTestMode); } @@ -307,4 +316,89 @@ public abstract class PhoneBase implements Phone { } } + /** + * Set the locale by matching the carrier string in + * a string-array resource + */ + private void setLocaleByCarrier() { + String carrier = SystemProperties.get("ro.carrier"); + + if (null == carrier || 0 == carrier.length()) { + return; + } + + CharSequence[] carrierLocales = mContext. + getResources().getTextArray(R.array.carrier_locales); + + for (int i = 0; i < carrierLocales.length-1; i+=2) { + String c = carrierLocales[i].toString(); + String l = carrierLocales[i+1].toString(); + if (carrier.equals(c)) { + String language = l.substring(0, 2); + String country = ""; + if (l.length() >=5) { + country = l.substring(3, 5); + } + setSystemLocale(language, country); + return; + } + } + } + + /** + * Utility code to set the system locale if it's not set already + * @param langauge Two character language code desired + * @param country Two character country code desired + * + * {@hide} + */ + public void setSystemLocale(String language, String country) { + String l = SystemProperties.get("persist.sys.language"); + String c = SystemProperties.get("persist.sys.country"); + + if (null == language) { + return; // no match possible + } + language.toLowerCase(); + if (null != country) { + country = ""; + } + country = country.toUpperCase(); + + if((null == l || 0 == l.length()) && (null == c || 0 == c.length())) { + try { + // try to find a good match + String[] locales = mContext.getAssets().getLocales(); + final int N = locales.length; + String bestMatch = null; + for(int i = 0; i < N; i++) { + if (locales[i]!=null && locales[i].length() >= 2 && + locales[i].substring(0,2).equals(language)) { + if (locales[i].length() >= 5 && + locales[i].substring(3,5).equals(country)) { + bestMatch = locales[i]; + break; + } else if (null == bestMatch) { + bestMatch = locales[i]; + } + } + } + if (null != bestMatch) { + IActivityManager am = ActivityManagerNative.getDefault(); + Configuration config = am.getConfiguration(); + + if (bestMatch.length() >= 5) { + config.locale = new Locale(bestMatch.substring(0,2), + bestMatch.substring(3,5)); + } else { + config.locale = new Locale(bestMatch.substring(0,2)); + } + config.userSetLocale = true; + am.updateConfiguration(config); + } + } catch (Exception e) { + // Intentionally left blank + } + } + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index a62f6cfd4d09d..4467536212ba8 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -27,10 +27,6 @@ import android.os.SystemProperties; import android.telephony.gsm.SmsMessage; import android.util.Log; import java.util.ArrayList; -import android.app.ActivityManagerNative; -import android.app.IActivityManager; -import java.util.Locale; -import android.content.res.Configuration; import static com.android.internal.telephony.TelephonyProperties.*; import com.android.internal.telephony.SimCard; @@ -554,48 +550,10 @@ public final class SIMRecords extends Handler implements SimConstants * @param mcc Mobile Country Code of the SIM */ private void setLocaleFromMccIfNeeded(int mcc) { - String language = SystemProperties.get("persist.sys.language"); - String country = SystemProperties.get("persist.sys.country"); - Log.d(LOG_TAG,"setLocaleFromMcc"); - if((language == null || language.length() == 0) && (country == null || country.length() == 0)) { - try { - language = MccTable.defaultLanguageForMcc(mcc); - country = MccTable.countryCodeForMcc(mcc).toUpperCase(); - // try to find a good match - String[] locales = phone.getContext().getAssets().getLocales(); - final int N = locales.length; - String bestMatch = null; - for(int i = 0; i < N; i++) { - Log.d(LOG_TAG," trying "+locales[i]); - if(locales[i]!=null && locales[i].length() >= 2 && - locales[i].substring(0,2).equals(language)) { - if(locales[i].length() >= 5 && - locales[i].substring(3,5).equals(country)) { - bestMatch = locales[i]; - break; - } else if(bestMatch == null) { - bestMatch = locales[i]; - } - } - } - Log.d(LOG_TAG," got bestmatch = "+bestMatch); - if(bestMatch != null) { - IActivityManager am = ActivityManagerNative.getDefault(); - Configuration config = am.getConfiguration(); + String language = MccTable.defaultLanguageForMcc(mcc); + String country = MccTable.countryCodeForMcc(mcc); - if(bestMatch.length() >= 5) { - config.locale = new Locale(bestMatch.substring(0,2), - bestMatch.substring(3,5)); - } else { - config.locale = new Locale(bestMatch.substring(0,2)); - } - config.userSetLocale = true; - am.updateConfiguration(config); - } - } catch (Exception e) { - // Intentionally left blank - } - } + phone.setSystemLocale(language, country); } //***** Overridden from Handler From aa7d8c88d35fe531d1c535566b73175d4afb6f5b Mon Sep 17 00:00:00 2001 From: Yu Shan Emily Lau <> Date: Tue, 31 Mar 2009 18:16:08 -0700 Subject: [PATCH 18/19] AI 143906: am: CL 143905 am: CL 143904 Fix the failure in the media recorder large test by adding the Camera permission. Original author: yslau Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143906 --- media/tests/MediaFrameworkTest/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml index a32f5907f391f..d0ff9ce017647 100644 --- a/media/tests/MediaFrameworkTest/AndroidManifest.xml +++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml @@ -18,6 +18,7 @@ package="com.android.mediaframeworktest"> + Date: Tue, 31 Mar 2009 18:25:33 -0700 Subject: [PATCH 19/19] AI 143909: am: CL 143908 am: CL 143907 Make search widget drop-down wider. Original author: jsharkey Merged from: //branches/cupcake/... Original author: android-build Merged from: //branches/donutburger/... Automated import of CL 143909 --- .../android/widget/AutoCompleteTextView.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index a17c78d4aa2bd..bc9589073a4ad 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -885,6 +885,22 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return result; } + /** + * Set the horizontal offset with respect to {@link #setDropDownAnchor(int)} + * @hide pending API council review + */ + public void setDropDownHorizontalOffset(int horizontalOffset) { + mDropDownHorizontalOffset = horizontalOffset; + } + + /** + * Set the vertical offset with respect to {@link #setDropDownAnchor(int)} + * @hide pending API council review + */ + public void setDropDownVerticalOffset(int verticalOffset) { + mDropDownVerticalOffset = verticalOffset; + } + /** *

Used for lazy instantiation of the anchor view from the id we have. If the value of * the id is NO_ID or we can't find a view for the given id, we return this TextView as