diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:33 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-02 22:54:33 -0800 |
commit | 3dec7d563a2f3e1eb967ce2054a00b6620e3558c (patch) | |
tree | aa3b0365c47cb3c1607c0dc76c8d32b4046fc287 /core/java/com/android | |
parent | 15ab3eae2ec3d73b3e8aa60b33ae41445bf83f4b (diff) | |
download | frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.zip frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.tar.gz frameworks_base-3dec7d563a2f3e1eb967ce2054a00b6620e3558c.tar.bz2 |
auto import from //depot/cupcake/@137055
Diffstat (limited to 'core/java/com/android')
15 files changed, 746 insertions, 166 deletions
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index b502a6c..edda1d9 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -28,6 +28,8 @@ interface IBatteryStats { void noteStopGps(int uid); void noteScreenOn(); void noteScreenOff(); + void notePhoneOn(); + void notePhoneOff(); void setOnBattery(boolean onBattery); long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/core/java/com/android/internal/app/UsbStorageStopActivity.java b/core/java/com/android/internal/app/UsbStorageStopActivity.java index 30523c4..557a523 100644 --- a/core/java/com/android/internal/app/UsbStorageStopActivity.java +++ b/core/java/com/android/internal/app/UsbStorageStopActivity.java @@ -54,7 +54,7 @@ public class UsbStorageStopActivity extends AlertActivity implements DialogInter // Set up the "dialog" final AlertController.AlertParams p = mAlertParams; - p.mIconId = com.android.internal.R.drawable.stat_sys_warning; + p.mIconId = com.android.internal.R.drawable.ic_dialog_alert; p.mTitle = getString(com.android.internal.R.string.usb_storage_stop_title); p.mMessage = getString(com.android.internal.R.string.usb_storage_stop_message); p.mPositiveButtonText = getString(com.android.internal.R.string.usb_storage_stop_button_mount); diff --git a/core/java/com/android/internal/gadget/IGadgetService.aidl b/core/java/com/android/internal/gadget/IGadgetService.aidl index a22f3f3..9c66b95 100644 --- a/core/java/com/android/internal/gadget/IGadgetService.aidl +++ b/core/java/com/android/internal/gadget/IGadgetService.aidl @@ -44,6 +44,7 @@ interface IGadgetService { List<GadgetProviderInfo> getInstalledProviders(); GadgetProviderInfo getGadgetInfo(int gadgetId); void bindGadgetId(int gadgetId, in ComponentName provider); + int[] getGadgetIds(in ComponentName provider); } diff --git a/core/java/com/android/internal/logging/AndroidConfig.java b/core/java/com/android/internal/logging/AndroidConfig.java index d8be889..f8002c6 100644 --- a/core/java/com/android/internal/logging/AndroidConfig.java +++ b/core/java/com/android/internal/logging/AndroidConfig.java @@ -34,11 +34,14 @@ public class AndroidConfig { super(); try { - Logger.global.addHandler(new AndroidHandler()); - Logger.global.setLevel(Level.ALL); + Logger rootLogger = Logger.getLogger(""); + rootLogger.addHandler(new AndroidHandler()); + rootLogger.setLevel(Level.INFO); + + // Turn down logging in Apache libraries. + Logger.getLogger("org.apache").setLevel(Level.WARNING); } catch (Exception ex) { ex.printStackTrace(); } - } - + } } diff --git a/core/java/com/android/internal/logging/AndroidHandler.java b/core/java/com/android/internal/logging/AndroidHandler.java index a6a4c64..d9fcf60 100644 --- a/core/java/com/android/internal/logging/AndroidHandler.java +++ b/core/java/com/android/internal/logging/AndroidHandler.java @@ -18,14 +18,14 @@ package com.android.internal.logging; import android.util.Log; -import java.util.logging.Formatter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.SimpleFormatter; +import java.util.logging.*; +import java.util.Date; +import java.text.MessageFormat; +import java.io.PrintWriter; +import java.io.StringWriter; /** - * Implements a {@link java.util.Logger} handler that writes to the Android log. The + * Implements a {@link java.util.logging.Logger} handler that writes to the Android log. The * implementation is rather straightforward. The name of the logger serves as * the log tag. Only the log levels need to be converted appropriately. For * this purpose, the following mapping is being used: @@ -81,8 +81,24 @@ public class AndroidHandler extends Handler { /** * Holds the formatter for all Android log handlers. */ - private static final Formatter THE_FORMATTER = new SimpleFormatter(); - + private static final Formatter THE_FORMATTER = new Formatter() { + @Override + public String format(LogRecord r) { + Throwable thrown = r.getThrown(); + if (thrown != null) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + sw.write(r.getMessage()); + sw.write("\n"); + thrown.printStackTrace(pw); + pw.flush(); + return sw.toString(); + } else { + return r.getMessage(); + } + } + }; + /** * Constructs a new instance of the Android log handler. */ @@ -106,27 +122,40 @@ public class AndroidHandler extends Handler { int level = getAndroidLevel(record.getLevel()); String tag = record.getLoggerName(); + if (tag == null) { + // Anonymous logger. + tag = "null"; + } else { + // Tags must be <= 23 characters. + int length = tag.length(); + if (length > 23) { + // Most loggers use the full class name. Try dropping the + // package. + int lastPeriod = tag.lastIndexOf("."); + if (length - lastPeriod - 1 <= 23) { + tag = tag.substring(lastPeriod + 1); + } else { + // Use last 23 chars. + tag = tag.substring(tag.length() - 23); + } + } + } + if (!Log.isLoggable(tag, level)) { return; } - - String msg; - try { - msg = getFormatter().format(record); - } catch (RuntimeException e) { - Log.e("AndroidHandler", "Error formatting log record", e); - msg = record.getMessage(); - } - Log.println(level, tag, msg); + + String message = getFormatter().format(record); + Log.println(level, tag, message); } catch (RuntimeException e) { - Log.e("AndroidHandler", "Error publishing log record", e); + Log.e("AndroidHandler", "Error logging message.", e); } } /** - * Converts a {@link java.util.Logger} logging level into an Android one. + * Converts a {@link java.util.logging.Logger} logging level into an Android one. * - * @param level The {@link java.util.Logger} logging level. + * @param level The {@link java.util.logging.Logger} logging level. * * @return The resulting Android logging level. */ diff --git a/core/java/com/android/internal/net/DbSSLSessionCache.java b/core/java/com/android/internal/net/DbSSLSessionCache.java new file mode 100644 index 0000000..06e4ca8 --- /dev/null +++ b/core/java/com/android/internal/net/DbSSLSessionCache.java @@ -0,0 +1,269 @@ +// Copyright 2009 The Android Open Source Project + +package com.android.internal.net; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import org.apache.commons.codec.binary.Base64; +import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; + +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.SSLSession; + +/** + * Hook into harmony SSL cache to persist the SSL sessions. + * + * Current implementation is suitable for saving a small number of hosts - + * like google services. It can be extended with expiration and more features + * to support more hosts. + * + * {@hide} + */ +public class DbSSLSessionCache implements SSLClientSessionCache { + private static final String TAG = "DbSSLSessionCache"; + + /** + * Table where sessions are stored. + */ + public static final String SSL_CACHE_TABLE = "ssl_sessions"; + + private static final String SSL_CACHE_ID = "_id"; + + /** + * Key is host:port - port is not optional. + */ + private static final String SSL_CACHE_HOSTPORT = "hostport"; + + /** + * Base64-encoded DER value of the session. + */ + private static final String SSL_CACHE_SESSION = "session"; + + /** + * Time when the record was added - should be close to the time + * of the initial session negotiation. + */ + private static final String SSL_CACHE_TIME_SEC = "time_sec"; + + public static final String DATABASE_NAME = "ssl_sessions.db"; + + public static final int DATABASE_VERSION = 2; + + /** public for testing + */ + public static final int SSL_CACHE_ID_COL = 0; + public static final int SSL_CACHE_HOSTPORT_COL = 1; + public static final int SSL_CACHE_SESSION_COL = 2; + public static final int SSL_CACHE_TIME_SEC_COL = 3; + + public static final int MAX_CACHE_SIZE = 256; + + private final Map<String, byte[]> mExternalCache = + new HashMap<String, byte[]>(); + + + private DatabaseHelper mDatabaseHelper; + + private boolean mNeedsCacheLoad = true; + + public static final String[] PROJECTION = new String[] { + SSL_CACHE_ID, + SSL_CACHE_HOSTPORT, + SSL_CACHE_SESSION, + SSL_CACHE_TIME_SEC + }; + + /** + * Create a SslSessionCache instance, using the specified context to + * initialize the database. + * + * This constructor will use the default database - created for the application + * context. + * + * @param activityContext + */ + public DbSSLSessionCache(Context activityContext) { + Context appContext = activityContext.getApplicationContext(); + mDatabaseHelper = new DatabaseHelper(appContext); + } + + /** + * Create a SslSessionCache that uses a specific database. + * + * + * @param database + */ + public DbSSLSessionCache(DatabaseHelper database) { + this.mDatabaseHelper = database; + } + + public void putSessionData(SSLSession session, byte[] der) { + if (mDatabaseHelper == null) { + return; + } + synchronized (this.getClass()) { + SQLiteDatabase db = mDatabaseHelper.getWritableDatabase(); + if (mExternalCache.size() == MAX_CACHE_SIZE) { + // remove oldest. + // TODO: check if the new one is in cached already ( i.e. update ). + Cursor byTime = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE, + PROJECTION, null, null, null, null, SSL_CACHE_TIME_SEC); + if (byTime.moveToFirst()) { + // TODO: can I do byTime.deleteRow() ? + String hostPort = byTime.getString(SSL_CACHE_HOSTPORT_COL); + db.delete(SSL_CACHE_TABLE, + SSL_CACHE_HOSTPORT + "= ?" , new String[] { hostPort }); + mExternalCache.remove(hostPort); + } else { + Log.w(TAG, "No rows found"); + // something is wrong, clear it + clear(); + } + } + // Serialize native session to standard DER encoding + long t0 = System.currentTimeMillis(); + + String b64 = new String(Base64.encodeBase64(der)); + String key = session.getPeerHost() + ":" + session.getPeerPort(); + + ContentValues values = new ContentValues(); + values.put(SSL_CACHE_HOSTPORT, key); + values.put(SSL_CACHE_SESSION, b64); + values.put(SSL_CACHE_TIME_SEC, System.currentTimeMillis() / 1000); + + mExternalCache.put(key, der); + + try { + db.insert(SSL_CACHE_TABLE, null /*nullColumnHack */ , values); + } catch(SQLException ex) { + // Ignore - nothing we can do to recover, and caller shouldn't + // be affected. + Log.w(TAG, "Ignoring SQL exception when caching session", ex); + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + long t1 = System.currentTimeMillis(); + Log.d(TAG, "New SSL session " + session.getPeerHost() + + " DER len: " + der.length + " " + (t1 - t0)); + } + } + + } + + public byte[] getSessionData(String host, int port) { + // Current (simple) implementation does a single lookup to DB, then saves + // all entries to the cache. + + // This works for google services - i.e. small number of certs. + // If we extend this to all processes - we should hold a separate cache + // or do lookups to DB each time. + if (mDatabaseHelper == null) { + return null; + } + synchronized(this.getClass()) { + if (mNeedsCacheLoad) { + // Don't try to load again, if something is wrong on the first + // request it'll likely be wrong each time. + mNeedsCacheLoad = false; + long t0 = System.currentTimeMillis(); + + Cursor cur = null; + try { + cur = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE, + PROJECTION, null, null, null, null, null); + if (cur.moveToFirst()) { + do { + String hostPort = cur.getString(SSL_CACHE_HOSTPORT_COL); + String value = cur.getString(SSL_CACHE_SESSION_COL); + + if (hostPort == null || value == null) { + continue; + } + // TODO: blob support ? + byte[] der = Base64.decodeBase64(value.getBytes()); + mExternalCache.put(hostPort, der); + } while (cur.moveToNext()); + + } + } catch (SQLException ex) { + Log.d(TAG, "Error loading SSL cached entries ", ex); + } finally { + if (cur != null) { + cur.close(); + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + long t1 = System.currentTimeMillis(); + Log.d(TAG, "LOADED CACHED SSL " + (t1 - t0) + " ms"); + } + } + } + + String key = host + ":" + port; + + return mExternalCache.get(key); + } + } + + /** + * Reset the database and internal state. + * Used for testing or to free space. + */ + public void clear() { + synchronized(this) { + try { + mExternalCache.clear(); + mNeedsCacheLoad = true; + mDatabaseHelper.getWritableDatabase().delete(SSL_CACHE_TABLE, + null, null); + } catch (SQLException ex) { + Log.d(TAG, "Error removing SSL cached entries ", ex); + // ignore - nothing we can do about it + } + } + } + + public byte[] getSessionData(byte[] id) { + // We support client side only - the cache will do nothing for + // server-side sessions. + return null; + } + + /** Visible for testing. + */ + public static class DatabaseHelper extends SQLiteOpenHelper { + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null /* factory */, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + SSL_CACHE_TABLE + " (" + + SSL_CACHE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + SSL_CACHE_HOSTPORT + " TEXT UNIQUE ON CONFLICT REPLACE," + + SSL_CACHE_SESSION + " TEXT," + + SSL_CACHE_TIME_SEC + " INTEGER" + + ");"); + + // No index - we load on startup, index would slow down inserts. + // If we want to scale this to lots of rows - we could use + // index, but then we'll hit DB a bit too often ( including + // negative hits ) + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL("DROP TABLE IF EXISTS " + SSL_CACHE_TABLE ); + onCreate(db); + } + + } + +} diff --git a/core/java/com/android/internal/net/SSLSessionCache.java b/core/java/com/android/internal/net/SSLSessionCache.java new file mode 100644 index 0000000..ec02fe5 --- /dev/null +++ b/core/java/com/android/internal/net/SSLSessionCache.java @@ -0,0 +1,101 @@ +// Copyright 2009 The Android Open Source Project +package com.android.internal.net; + +import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; +import org.apache.harmony.xnet.provider.jsse.SSLContextImpl; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; +import android.util.Log; + +/** + * Utility class to configure SSL session caching. + * + * + * + * {@hide} + */ +public class SSLSessionCache { + private static final String TAG = "SSLSessionCache"; + + private static final String CACHE_TYPE_DB = "db"; + + private static boolean sInitializationDone = false; + + // One per process + private static DbSSLSessionCache sDbCache; + + /** + * Check settings for ssl session caching. + * + * @return false if disabled. + */ + public static boolean isEnabled(ContentResolver resolver) { + String sslCache = Settings.Gservices.getString(resolver, + Settings.Gservices.SSL_SESSION_CACHE); + + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "enabled " + sslCache); + } + + return CACHE_TYPE_DB.equals(sslCache); + } + + /** + * Return the configured session cache, or null if not enabled. + */ + public static SSLClientSessionCache getSessionCache(Context context) { + if (context == null) { + return null; + } + if (!sInitializationDone) { + if (isEnabled(context.getContentResolver())) { + sDbCache = new DbSSLSessionCache(context); + return sDbCache; + } + // Don't check again. + sInitializationDone = true; + } + return sDbCache; + } + + /** + * Construct the factory, using default constructor if caching is disabled. + * Refactored here to avoid duplication, used in tests. + */ + public static SSLSocketFactory getSocketFactory(Context androidContext, + TrustManager[] trustManager) { + try { + if (androidContext != null) { + SSLClientSessionCache sessionCache = getSessionCache(androidContext); + + if (sessionCache != null) { + SSLContextImpl sslContext = new SSLContextImpl(); + sslContext.engineInit(null /* kms */, + trustManager, new java.security.SecureRandom(), + sessionCache, null /* serverCache */); + return sslContext.engineGetSocketFactory(); + } + } + // default + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, trustManager, new java.security.SecureRandom()); + return context.getSocketFactory(); + + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (KeyManagementException e) { + throw new AssertionError(e); + } + } + + +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 558a122..7eea8b7 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -23,6 +23,7 @@ import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.SystemClock; import android.util.Log; +import android.util.Printer; import android.util.SparseArray; import java.io.File; @@ -39,14 +40,14 @@ import java.util.Map; * otherwise. */ public final class BatteryStatsImpl extends BatteryStats { - private static final String TAG = "BatteryStatsImpl"; + private static final boolean DEBUG = false; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 23; + private static final int VERSION = 25; private final File mFile; private final File mBackupFile; @@ -86,9 +87,11 @@ public final class BatteryStatsImpl extends BatteryStats { long mLastRealtime; boolean mScreenOn; - long mLastScreenOnTimeMillis; - long mBatteryScreenOnTimeMillis; - long mPluggedScreenOnTimeMillis; + Timer mScreenOnTimer; + + boolean mPhoneOn; + Timer mPhoneOnTimer; + /** * These provide time bases that discount the time the device is plugged * in to power. @@ -132,16 +135,45 @@ public final class BatteryStatsImpl extends BatteryStats { // Times are in microseconds for better accuracy when dividing by the // lock count, and are in "battery realtime" units. + /** + * The total time we have accumulated since the start of the original + * boot, to the last time something interesting happened in the + * current run. + */ long mTotalTime; - long mLoadedTotalTime; - long mLastTotalTime; - long mUpdateTime; /** - * The value of mTotalTime when unplug() was last called, initially 0. + * The total time we loaded for the previous runs. Subtract this from + * mTotalTime to find the time for the current run of the system. */ - long mUnpluggedTotalTime; + long mLoadedTime; + + /** + * The run time of the last run of the system, as loaded from the + * saved data. + */ + long mLastTime; + + /** + * The value of mTotalTime when unplug() was last called. Subtract + * this from mTotalTime to find the time since the last unplug from + * power. + */ + long mUnpluggedTime; + /** + * The last time at which we updated the timer. If mNesting is > 0, + * subtract this from the current battery time to find the amount of + * time we have been running since we last computed an update. + */ + long mUpdateTime; + + /** + * The total time at which the timer was acquired, to determine if + * was actually held for an interesting duration. + */ + long mAcquireTime; + Timer(int type, ArrayList<Timer> timerPool, ArrayList<Unpluggable> unpluggables, Parcel in) { mType = type; @@ -151,10 +183,10 @@ public final class BatteryStatsImpl extends BatteryStats { mLastCount = in.readInt(); mUnpluggedCount = in.readInt(); mTotalTime = in.readLong(); - mLoadedTotalTime = in.readLong(); - mLastTotalTime = in.readLong(); + mLoadedTime = in.readLong(); + mLastTime = in.readLong(); mUpdateTime = in.readLong(); - mUnpluggedTotalTime = in.readLong(); + mUnpluggedTime = in.readLong(); unpluggables.add(this); } @@ -171,21 +203,41 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mLastCount); out.writeInt(mUnpluggedCount); out.writeLong(computeRunTimeLocked(batteryRealtime)); - out.writeLong(mLoadedTotalTime); - out.writeLong(mLastTotalTime); + out.writeLong(mLoadedTime); + out.writeLong(mLastTime); out.writeLong(mUpdateTime); - out.writeLong(mUnpluggedTotalTime); + out.writeLong(mUnpluggedTime); } public void unplug(long batteryUptime, long batteryRealtime) { - mUnpluggedTotalTime = computeRunTimeLocked(batteryRealtime); + if (DEBUG && mType < 0) { + Log.v(TAG, "unplug #" + mType + ": realtime=" + batteryRealtime + + " old mUnpluggedTime=" + mUnpluggedTime + + " old mUnpluggedCount=" + mUnpluggedCount); + } + mUnpluggedTime = computeRunTimeLocked(batteryRealtime); mUnpluggedCount = mCount; + if (DEBUG && mType < 0) { + Log.v(TAG, "unplug #" + mType + + ": new mUnpluggedTime=" + mUnpluggedTime + + " new mUnpluggedCount=" + mUnpluggedCount); + } } public void plug(long batteryUptime, long batteryRealtime) { if (mNesting > 0) { + if (DEBUG && mType < 0) { + Log.v(TAG, "plug #" + mType + ": realtime=" + batteryRealtime + + " old mTotalTime=" + mTotalTime + + " old mUpdateTime=" + mUpdateTime); + } mTotalTime = computeRunTimeLocked(batteryRealtime); mUpdateTime = batteryRealtime; + if (DEBUG && mType < 0) { + Log.v(TAG, "plug #" + mType + + ": new mTotalTime=" + mTotalTime + + " old mUpdateTime=" + mUpdateTime); + } } } @@ -207,16 +259,16 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override - public long getTotalTime(long now, int which) { + public long getTotalTime(long batteryRealtime, int which) { long val; if (which == STATS_LAST) { - val = mLastTotalTime; + val = mLastTime; } else { - val = computeRunTimeLocked(now); + val = computeRunTimeLocked(batteryRealtime); if (which == STATS_UNPLUGGED) { - val -= mUnpluggedTotalTime; + val -= mUnpluggedTime; } else if (which != STATS_TOTAL) { - val -= mLoadedTotalTime; + val -= mLoadedTime; } } @@ -245,22 +297,32 @@ public final class BatteryStatsImpl extends BatteryStats { + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount + " mUnpluggedCount=" + mUnpluggedCount); Log.i("foo", "mTotalTime=" + mTotalTime - + " mLoadedTotalTime=" + mLoadedTotalTime); - Log.i("foo", "mLastTotalTime=" + mLastTotalTime - + " mUpdateTime=" + mUpdateTime); + + " mLoadedTime=" + mLoadedTime); + Log.i("foo", "mLastTime=" + mLastTime + + " mUnpluggedTime=" + mUnpluggedTime); + Log.i("foo", "mUpdateTime=" + mUpdateTime + + " mAcquireTime=" + mAcquireTime); } void startRunningLocked(BatteryStatsImpl stats) { if (mNesting++ == 0) { mUpdateTime = stats.getBatteryRealtimeLocked( SystemClock.elapsedRealtime() * 1000); - // Accumulate time to all currently active timers before adding - // this new one to the pool. - refreshTimersLocked(stats, mTimerPool); - // Add this timer to the active pool - mTimerPool.add(this); + if (mTimerPool != null) { + // Accumulate time to all currently active timers before adding + // this new one to the pool. + refreshTimersLocked(stats, mTimerPool); + // Add this timer to the active pool + mTimerPool.add(this); + } // Increment the count mCount++; + mAcquireTime = mTotalTime; + if (DEBUG && mType < 0) { + Log.v(TAG, "start #" + mType + ": mUpdateTime=" + mUpdateTime + + " mTotalTime=" + mTotalTime + " mCount=" + mCount + + " mAcquireTime=" + mAcquireTime); + } } } @@ -270,11 +332,31 @@ public final class BatteryStatsImpl extends BatteryStats { return; } if (--mNesting == 0) { - // Accumulate time to all active counters, scaled by the total - // active in the pool, before taking this one out of the pool. - refreshTimersLocked(stats, mTimerPool); - // Remove this timer from the active pool - mTimerPool.remove(this); + if (mTimerPool != null) { + // Accumulate time to all active counters, scaled by the total + // active in the pool, before taking this one out of the pool. + refreshTimersLocked(stats, mTimerPool); + // Remove this timer from the active pool + mTimerPool.remove(this); + } else { + final long realtime = SystemClock.elapsedRealtime() * 1000; + final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime); + mNesting = 1; + mTotalTime = computeRunTimeLocked(batteryRealtime); + mNesting = 0; + } + + if (DEBUG && mType < 0) { + Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTime + + " mTotalTime=" + mTotalTime + " mCount=" + mCount + + " mAcquireTime=" + mAcquireTime); + } + + if (mTotalTime == mAcquireTime) { + // If there was no change in the time, then discard this + // count. A somewhat cheezy strategy, but hey. + mCount--; + } } } @@ -297,24 +379,28 @@ public final class BatteryStatsImpl extends BatteryStats { private long computeRunTimeLocked(long curBatteryRealtime) { return mTotalTime + (mNesting > 0 - ? (curBatteryRealtime - mUpdateTime) / mTimerPool.size() : 0); + ? (curBatteryRealtime - mUpdateTime) + / (mTimerPool != null ? mTimerPool.size() : 1) + : 0); } - void writeSummaryFromParcelLocked(Parcel out, long curBatteryUptime) { - long runTime = computeRunTimeLocked(curBatteryUptime); + void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) { + long runTime = computeRunTimeLocked(batteryRealtime); // Divide by 1000 for backwards compatibility out.writeLong((runTime + 500) / 1000); - out.writeLong(((runTime - mLoadedTotalTime) + 500) / 1000); + out.writeLong(((runTime - mLoadedTime) + 500) / 1000); out.writeInt(mCount); out.writeInt(mCount - mLoadedCount); } void readSummaryFromParcelLocked(Parcel in) { // Multiply by 1000 for backwards compatibility - mTotalTime = mLoadedTotalTime = in.readLong() * 1000; - mLastTotalTime = in.readLong(); + mTotalTime = mLoadedTime = in.readLong() * 1000; + mLastTime = in.readLong() * 1000; + mUnpluggedTime = mTotalTime; mCount = mLoadedCount = in.readInt(); mLastCount = in.readInt(); + mUnpluggedCount = mCount; mNesting = 0; } } @@ -357,47 +443,40 @@ public final class BatteryStatsImpl extends BatteryStats { mUidStats.get(uid).noteStopGps(); } - /** - * When the device screen or battery state changes, update the appropriate "screen on time" - * counter. - */ - private void updateScreenOnTimeLocked(boolean screenOn) { + public void noteScreenOnLocked() { if (!mScreenOn) { - Log.w(TAG, "updateScreenOnTime without mScreenOn, ignored"); - return; - } - long now = SystemClock.elapsedRealtime(); - long elapsed = now - mLastScreenOnTimeMillis; - if (mOnBattery) { - mBatteryScreenOnTimeMillis += elapsed; - } else { - mPluggedScreenOnTimeMillis += elapsed; + mScreenOn = true; + mScreenOnTimer.startRunningLocked(this); } - if (screenOn) { - mLastScreenOnTimeMillis = now; + } + + public void noteScreenOffLocked() { + if (mScreenOn) { + mScreenOn = false; + mScreenOnTimer.stopRunningLocked(this); } } - public void noteScreenOnLocked() { - mScreenOn = true; - mLastScreenOnTimeMillis = SystemClock.elapsedRealtime(); + public void notePhoneOnLocked() { + if (!mPhoneOn) { + mPhoneOn = true; + mPhoneOnTimer.startRunningLocked(this); + } } - public void noteScreenOffLocked() { - if (!mScreenOn) { - Log.w(TAG, "noteScreenOff without mScreenOn, ignored"); - return; + public void notePhoneOffLocked() { + if (mPhoneOn) { + mPhoneOn = false; + mPhoneOnTimer.stopRunningLocked(this); } - updateScreenOnTimeLocked(false); - mScreenOn = false; } - @Override public long getBatteryScreenOnTime() { - return mBatteryScreenOnTimeMillis; + @Override public long getScreenOnTime(long batteryRealtime, int which) { + return mScreenOnTimer.getTotalTime(batteryRealtime, which); } - @Override public long getPluggedScreenOnTime() { - return mPluggedScreenOnTimeMillis; + @Override public long getPhoneOnTime(long batteryRealtime, int which) { + return mPhoneOnTimer.getTotalTime(batteryRealtime, which); } @Override public boolean getIsOnBattery() { @@ -1381,6 +1460,8 @@ public final class BatteryStatsImpl extends BatteryStats { mFile = new File(filename); mBackupFile = new File(filename + ".bak"); mStartCount++; + mScreenOnTimer = new Timer(-1, null, mUnpluggables); + mPhoneOnTimer = new Timer(-2, null, mUnpluggables); mOnBattery = mOnBatteryInternal = false; mTrackBatteryPastUptime = 0; mTrackBatteryPastRealtime = 0; @@ -1407,10 +1488,6 @@ public final class BatteryStatsImpl extends BatteryStats { public void setOnBattery(boolean onBattery) { synchronized(this) { if (mOnBattery != onBattery) { - if (mScreenOn) { - updateScreenOnTimeLocked(true); - } - mOnBattery = mOnBatteryInternal = onBattery; long uptime = SystemClock.uptimeMillis() * 1000; @@ -1425,7 +1502,7 @@ public final class BatteryStatsImpl extends BatteryStats { } else { mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart; mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart; - doPlug(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime); + doPlug(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime)); } if ((mLastWriteTime + (60 * 1000)) < mSecRealtime) { if (mFile != null) { @@ -1447,10 +1524,10 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long computeUptime(long curTime, int which) { switch (which) { - case STATS_TOTAL: return mUptime + (curTime-mUptimeStart); - case STATS_LAST: return mLastUptime; - case STATS_CURRENT: return (curTime-mUptimeStart); - case STATS_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart); + case STATS_TOTAL: return mUptime + (curTime-mUptimeStart); + case STATS_LAST: return mLastUptime; + case STATS_CURRENT: return (curTime-mUptimeStart); + case STATS_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart); } return 0; } @@ -1458,26 +1535,25 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long computeRealtime(long curTime, int which) { switch (which) { - case STATS_TOTAL: return mRealtime + (curTime-mRealtimeStart); - case STATS_LAST: return mLastRealtime; - case STATS_CURRENT: return (curTime-mRealtimeStart); - case STATS_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart); + case STATS_TOTAL: return mRealtime + (curTime-mRealtimeStart); + case STATS_LAST: return mLastRealtime; + case STATS_CURRENT: return (curTime-mRealtimeStart); + case STATS_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart); } return 0; } @Override public long computeBatteryUptime(long curTime, int which) { - long uptime = getBatteryUptime(curTime); switch (which) { - case STATS_TOTAL: - return mBatteryUptime + uptime; - case STATS_LAST: - return mBatteryLastUptime; - case STATS_CURRENT: - return uptime; - case STATS_UNPLUGGED: - return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime; + case STATS_TOTAL: + return mBatteryUptime + getBatteryUptime(curTime); + case STATS_LAST: + return mBatteryLastUptime; + case STATS_CURRENT: + return getBatteryUptime(curTime); + case STATS_UNPLUGGED: + return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime; } return 0; } @@ -1485,14 +1561,14 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public long computeBatteryRealtime(long curTime, int which) { switch (which) { - case STATS_TOTAL: - return mBatteryRealtime + getBatteryRealtimeLocked(curTime); - case STATS_LAST: - return mBatteryLastRealtime; - case STATS_CURRENT: - return getBatteryRealtimeLocked(curTime); - case STATS_UNPLUGGED: - return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime; + case STATS_TOTAL: + return mBatteryRealtime + getBatteryRealtimeLocked(curTime); + case STATS_LAST: + return mBatteryLastRealtime; + case STATS_CURRENT: + return getBatteryRealtimeLocked(curTime); + case STATS_UNPLUGGED: + return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime; } return 0; } @@ -1688,9 +1764,10 @@ public final class BatteryStatsImpl extends BatteryStats { mLastRealtime = in.readLong(); mStartCount++; - mBatteryScreenOnTimeMillis = in.readLong(); - mPluggedScreenOnTimeMillis = in.readLong(); mScreenOn = false; + mScreenOnTimer.readSummaryFromParcelLocked(in); + mPhoneOn = false; + mPhoneOnTimer.readSummaryFromParcelLocked(in); final int NU = in.readInt(); for (int iu = 0; iu < NU; iu++) { @@ -1764,9 +1841,10 @@ public final class BatteryStatsImpl extends BatteryStats { * @param out the Parcel to be written to. */ public void writeSummaryToParcel(Parcel out) { - final long NOW = getBatteryUptimeLocked(); final long NOW_SYS = SystemClock.uptimeMillis() * 1000; final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000; + final long NOW = getBatteryUptimeLocked(NOW_SYS); + final long NOWREAL = getBatteryRealtimeLocked(NOWREAL_SYS); out.writeInt(VERSION); @@ -1780,8 +1858,8 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT)); - out.writeLong(mBatteryScreenOnTimeMillis); - out.writeLong(mPluggedScreenOnTimeMillis); + mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); + mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL); final int NU = mUidStats.size(); out.writeInt(NU); @@ -1798,19 +1876,19 @@ public final class BatteryStatsImpl extends BatteryStats { Uid.Wakelock wl = ent.getValue(); if (wl.mTimerFull != null) { out.writeInt(1); - wl.mTimerFull.writeSummaryFromParcelLocked(out, NOW); + wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL); } else { out.writeInt(0); } if (wl.mTimerPartial != null) { out.writeInt(1); - wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOW); + wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL); } else { out.writeInt(0); } if (wl.mTimerWindow != null) { out.writeInt(1); - wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOW); + wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL); } else { out.writeInt(0); } @@ -1826,7 +1904,7 @@ public final class BatteryStatsImpl extends BatteryStats { Uid.Sensor se = ent.getValue(); if (se.mTimer != null) { out.writeInt(1); - se.mTimer.writeSummaryFromParcelLocked(out, NOW); + se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL); } else { out.writeInt(0); } @@ -1897,9 +1975,10 @@ public final class BatteryStatsImpl extends BatteryStats { mBatteryLastUptime = in.readLong(); mBatteryRealtime = in.readLong(); mBatteryLastRealtime = in.readLong(); - mBatteryScreenOnTimeMillis = in.readLong(); - mPluggedScreenOnTimeMillis = in.readLong(); mScreenOn = false; + mScreenOnTimer = new Timer(-1, null, mUnpluggables, in); + mPhoneOn = false; + mPhoneOnTimer = new Timer(-2, null, mUnpluggables, in); mUptime = in.readLong(); mUptimeStart = in.readLong(); mLastUptime = in.readLong(); @@ -1912,6 +1991,8 @@ public final class BatteryStatsImpl extends BatteryStats { mTrackBatteryUptimeStart = in.readLong(); mTrackBatteryPastRealtime = in.readLong(); mTrackBatteryRealtimeStart = in.readLong(); + mUnpluggedBatteryUptime = in.readLong(); + mUnpluggedBatteryRealtime = in.readLong(); mLastWriteTime = in.readLong(); mPartialTimers.clear(); @@ -1945,8 +2026,8 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mBatteryLastUptime); out.writeLong(mBatteryRealtime); out.writeLong(mBatteryLastRealtime); - out.writeLong(mBatteryScreenOnTimeMillis); - out.writeLong(mPluggedScreenOnTimeMillis); + mScreenOnTimer.writeToParcel(out, batteryRealtime); + mPhoneOnTimer.writeToParcel(out, batteryRealtime); out.writeLong(mUptime); out.writeLong(mUptimeStart); out.writeLong(mLastUptime); @@ -1954,10 +2035,12 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mRealtimeStart); out.writeLong(mLastRealtime); out.writeInt(mOnBattery ? 1 : 0); - out.writeLong(mTrackBatteryPastUptime); + out.writeLong(batteryUptime); out.writeLong(mTrackBatteryUptimeStart); - out.writeLong(mTrackBatteryPastRealtime); + out.writeLong(batteryRealtime); out.writeLong(mTrackBatteryRealtimeStart); + out.writeLong(mUnpluggedBatteryUptime); + out.writeLong(mUnpluggedBatteryRealtime); out.writeLong(mLastWriteTime); int size = mUidStats.size(); @@ -1980,4 +2063,14 @@ public final class BatteryStatsImpl extends BatteryStats { return new BatteryStatsImpl[size]; } }; + + public void dumpLocked(Printer pw) { + if (DEBUG) { + Log.i(TAG, "*** Screen timer:"); + mScreenOnTimer.logState(); + Log.i(TAG, "*** Phone timer:"); + mPhoneOnTimer.logState(); + } + super.dumpLocked(pw); + } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index f21b62f..ac8b589 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -19,6 +19,7 @@ package com.android.internal.os; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.net.LocalServerSocket; import android.os.Debug; @@ -335,32 +336,18 @@ public class ZygoteInit { mResources.startPreloading(); if (PRELOAD_RESOURCES) { Log.i(TAG, "Preloading resources..."); + long startTime = SystemClock.uptimeMillis(); TypedArray ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_drawables); - int N = ar.length(); - for (int i=0; i<N; i++) { - if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) { - if (Config.LOGV) { - Log.v(TAG, " GC at " + Debug.getGlobalAllocSize()); - } - runtime.gcSoftReferences(); - runtime.runFinalizationSync(); - Debug.resetGlobalAllocSize(); - } - int id = ar.getResourceId(i, 0); - if (Config.LOGV) { - Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); - } - if (id != 0) { - Drawable dr = mResources.getDrawable(id); - if ((dr.getChangingConfigurations()&~ActivityInfo.CONFIG_FONT_SCALE) != 0) { - Log.w(TAG, "Preloaded drawable resource #0x" - + Integer.toHexString(id) - + " that varies with configuration!!"); - } - } - } + int N = preloadDrawables(runtime, ar); + Log.i(TAG, "...preloaded " + N + " resources in " + + (SystemClock.uptimeMillis()-startTime) + "ms."); + + startTime = SystemClock.uptimeMillis(); + ar = mResources.obtainTypedArray( + com.android.internal.R.array.preloaded_color_state_lists); + N = preloadColorStateLists(runtime, ar); Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); } @@ -372,6 +359,56 @@ public class ZygoteInit { } } + private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) { + int N = ar.length(); + for (int i=0; i<N; i++) { + if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) { + if (Config.LOGV) { + Log.v(TAG, " GC at " + Debug.getGlobalAllocSize()); + } + runtime.gcSoftReferences(); + runtime.runFinalizationSync(); + Debug.resetGlobalAllocSize(); + } + int id = ar.getResourceId(i, 0); + if (Config.LOGV) { + Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); + } + if (id != 0) { + mResources.getColorStateList(id); + } + } + return N; + } + + + private static int preloadDrawables(VMRuntime runtime, TypedArray ar) { + int N = ar.length(); + for (int i=0; i<N; i++) { + if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) { + if (Config.LOGV) { + Log.v(TAG, " GC at " + Debug.getGlobalAllocSize()); + } + runtime.gcSoftReferences(); + runtime.runFinalizationSync(); + Debug.resetGlobalAllocSize(); + } + int id = ar.getResourceId(i, 0); + if (Config.LOGV) { + Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); + } + if (id != 0) { + Drawable dr = mResources.getDrawable(id); + if ((dr.getChangingConfigurations()&~ActivityInfo.CONFIG_FONT_SCALE) != 0) { + Log.w(TAG, "Preloaded drawable resource #0x" + + Integer.toHexString(id) + + " (" + ar.getString(i) + ") that varies with configuration!!"); + } + } + } + return N; + } + /** * Runs several special GCs to try to clean up a few generations of * softly- and final-reachable objects, along with any other garbage. diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 4f98cee..8e65177 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -23,7 +23,8 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_COMMIT_TEXT = 50; private static final int DO_COMMIT_COMPLETION = 55; private static final int DO_SET_SELECTION = 57; - private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 58; + private static final int DO_PERFORM_EDITOR_ACTION = 58; + private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59; private static final int DO_SET_COMPOSING_TEXT = 60; private static final int DO_FINISH_COMPOSING_TEXT = 65; private static final int DO_SEND_KEY_EVENT = 70; @@ -97,6 +98,10 @@ public class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); } + public void performEditorAction(int id) { + dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0)); + } + public void performContextMenuAction(int id) { dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); } @@ -235,6 +240,15 @@ public class IInputConnectionWrapper extends IInputContext.Stub { ic.setSelection(msg.arg1, msg.arg2); return; } + case DO_PERFORM_EDITOR_ACTION: { + InputConnection ic = mInputConnection.get(); + if (ic == null || !isActive()) { + Log.w(TAG, "performEditorAction on inactive InputConnection"); + return; + } + ic.performEditorAction(msg.arg1); + return; + } case DO_PERFORM_CONTEXT_MENU_ACTION: { InputConnection ic = mInputConnection.get(); if (ic == null || !isActive()) { diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 02b6044..02cb9e4 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -50,6 +50,8 @@ import com.android.internal.view.IInputContextCallback; void setSelection(int start, int end); + void performEditorAction(int actionCode); + void performContextMenuAction(int id); void beginBatchEdit(); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 32d9f3d..b92cb45 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -250,6 +250,15 @@ public class InputConnectionWrapper implements InputConnection { } } + public boolean performEditorAction(int actionCode) { + try { + mIInputContext.performEditorAction(actionCode); + return true; + } catch (RemoteException e) { + return false; + } + } + public boolean performContextMenuAction(int id) { try { mIInputContext.performContextMenuAction(id); diff --git a/core/java/com/android/internal/view/menu/ExpandedMenuView.java b/core/java/com/android/internal/view/menu/ExpandedMenuView.java index c16c165..9e4b4ce 100644 --- a/core/java/com/android/internal/view/menu/ExpandedMenuView.java +++ b/core/java/com/android/internal/view/menu/ExpandedMenuView.java @@ -80,6 +80,11 @@ public final class ExpandedMenuView extends ListView implements ItemInvoker, Men setChildrenDrawingCacheEnabled(false); } + @Override + protected boolean recycleOnMeasure() { + return false; + } + public boolean invokeItem(MenuItemImpl item) { return mMenu.performItemAction(item, 0); } diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 44cf0ed..f2ec064 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -59,7 +59,14 @@ public class EditableInputConnection extends BaseInputConnection { final Editable content = getEditable(); if (content == null) return false; KeyListener kl = mTextView.getKeyListener(); - if (kl != null) kl.clearMetaKeyState(mTextView, content, states); + if (kl != null) { + try { + kl.clearMetaKeyState(mTextView, content, states); + } catch (AbstractMethodError e) { + // This is an old listener that doesn't implement the + // new method. + } + } return true; } @@ -71,6 +78,12 @@ public class EditableInputConnection extends BaseInputConnection { return true; } + public boolean performEditorAction(int actionCode) { + if (DEBUG) Log.v(TAG, "performEditorAction " + actionCode); + mTextView.onEditorAction(actionCode); + return true; + } + public boolean performContextMenuAction(int id) { if (DEBUG) Log.v(TAG, "performContextMenuAction " + id); mTextView.beginBatchEdit(); diff --git a/core/java/com/android/internal/widget/TextProgressBar.java b/core/java/com/android/internal/widget/TextProgressBar.java index 5bf4601..aee7b76 100644 --- a/core/java/com/android/internal/widget/TextProgressBar.java +++ b/core/java/com/android/internal/widget/TextProgressBar.java @@ -115,8 +115,10 @@ public class TextProgressBar extends RelativeLayout implements OnChronometerTick // Update the ProgressBar maximum relative to Chronometer base mDuration = (int) (durationBase - mChronometer.getBase()); + if (mDuration <= 0) { + mDuration = 1; + } mProgressBar.setMax(mDuration); - } /** |