diff options
31 files changed, 1721 insertions, 311 deletions
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index c211b476f2bc..c60045cbbe7c 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -216,11 +216,9 @@ int main(int argc, char *argv[]) { } else vibrate_fd = -1; -#if 0 /* switch to non-root user and group */ setgroups(sizeof(groups)/sizeof(groups[0]), groups); setuid(AID_SHELL); -#endif /* make it safe to use both printf and STDOUT_FILENO */ setvbuf(stdout, 0, _IONBF, 0); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 8c422a2280cf..03346fec837f 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -185,7 +185,7 @@ public abstract class AccessibilityService extends Service { private final HandlerCaller mCaller; - private AccessibilityService mTarget; + private final AccessibilityService mTarget; public IEventListenerWrapper(AccessibilityService context) { mTarget = context; @@ -211,9 +211,9 @@ public abstract class AccessibilityService extends Service { switch (message.what) { case DO_ON_ACCESSIBILITY_EVENT : AccessibilityEvent event = (AccessibilityEvent) message.obj; - if (event != null){ - mTarget.onAccessibilityEvent(event); - event.recycle(); + if (event != null) { + mTarget.onAccessibilityEvent(event); + event.recycle(); } return; case DO_ON_INTERRUPT : diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index d04abe59c680..9afeb74b02f4 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -21,11 +21,13 @@ import android.content.Intent; import android.content.Context; import android.content.IntentFilter; import android.content.BroadcastReceiver; +import android.database.SQLException; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.Parcelable; +import android.util.Log; import java.io.IOException; import java.util.concurrent.Callable; @@ -364,7 +366,13 @@ public class AccountManager { handler = (handler == null) ? mMainHandler : handler; handler.post(new Runnable() { public void run() { - listener.onAccountsUpdated(accountsCopy); + try { + listener.onAccountsUpdated(accountsCopy); + } catch (SQLException e) { + // Better luck next time. If the problem was disk-full, + // the STORAGE_OK intent will re-trigger the update. + Log.e(TAG, "Can't update accounts", e); + } } }); } @@ -824,6 +832,8 @@ public class AccountManager { // Register a broadcast receiver to monitor account changes IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Constants.LOGIN_ACCOUNTS_CHANGED_ACTION); + // To recover from disk-full. + intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter); } } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 6d79aee973f9..b8c3aa362264 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.os.Bundle; +import android.os.PerformanceCollector; import android.os.RemoteException; import android.os.Debug; import android.os.IBinder; @@ -83,10 +84,8 @@ public class Instrumentation { private List<ActivityWaiter> mWaitingActivities; private List<ActivityMonitor> mActivityMonitors; private IInstrumentationWatcher mWatcher; - private long mPreCpuTime; - private long mStart; private boolean mAutomaticPerformanceSnapshots = false; - private Bundle mPrePerfMetrics = new Bundle(); + private PerformanceCollector mPerformanceCollector; private Bundle mPerfMetrics = new Bundle(); public Instrumentation() { @@ -191,96 +190,21 @@ public class Instrumentation { public void setAutomaticPerformanceSnapshots() { mAutomaticPerformanceSnapshots = true; + mPerformanceCollector = new PerformanceCollector(); } public void startPerformanceSnapshot() { - mStart = 0; if (!isProfiling()) { - // Add initial binder counts - Bundle binderCounts = getBinderCounts(); - for (String key: binderCounts.keySet()) { - addPerfMetricLong("pre_" + key, binderCounts.getLong(key)); - } - - // Force a GC and zero out the performance counters. Do this - // before reading initial CPU/wall-clock times so we don't include - // the cost of this setup in our final metrics. - startAllocCounting(); - - // Record CPU time up to this point, and start timing. Note: this - // must happen at the end of this method, otherwise the timing will - // include noise. - mStart = SystemClock.uptimeMillis(); - mPreCpuTime = Process.getElapsedCpuTime(); + mPerformanceCollector.beginSnapshot(null); } } public void endPerformanceSnapshot() { if (!isProfiling()) { - // Stop the timing. This must be done first before any other counting is stopped. - long cpuTime = Process.getElapsedCpuTime(); - long duration = SystemClock.uptimeMillis(); - - stopAllocCounting(); - - long nativeMax = Debug.getNativeHeapSize() / 1024; - long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; - long nativeFree = Debug.getNativeHeapFreeSize() / 1024; - - Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); - Debug.getMemoryInfo(memInfo); - - Runtime runtime = Runtime.getRuntime(); - - long dalvikMax = runtime.totalMemory() / 1024; - long dalvikFree = runtime.freeMemory() / 1024; - long dalvikAllocated = dalvikMax - dalvikFree; - - // Add final binder counts - Bundle binderCounts = getBinderCounts(); - for (String key: binderCounts.keySet()) { - addPerfMetricLong(key, binderCounts.getLong(key)); - } - - // Add alloc counts - Bundle allocCounts = getAllocCounts(); - for (String key: allocCounts.keySet()) { - addPerfMetricLong(key, allocCounts.getLong(key)); - } - - addPerfMetricLong("execution_time", duration - mStart); - addPerfMetricLong("pre_cpu_time", mPreCpuTime); - addPerfMetricLong("cpu_time", cpuTime - mPreCpuTime); - - addPerfMetricLong("native_size", nativeMax); - addPerfMetricLong("native_allocated", nativeAllocated); - addPerfMetricLong("native_free", nativeFree); - addPerfMetricInt("native_pss", memInfo.nativePss); - addPerfMetricInt("native_private_dirty", memInfo.nativePrivateDirty); - addPerfMetricInt("native_shared_dirty", memInfo.nativeSharedDirty); - - addPerfMetricLong("java_size", dalvikMax); - addPerfMetricLong("java_allocated", dalvikAllocated); - addPerfMetricLong("java_free", dalvikFree); - addPerfMetricInt("java_pss", memInfo.dalvikPss); - addPerfMetricInt("java_private_dirty", memInfo.dalvikPrivateDirty); - addPerfMetricInt("java_shared_dirty", memInfo.dalvikSharedDirty); - - addPerfMetricInt("other_pss", memInfo.otherPss); - addPerfMetricInt("other_private_dirty", memInfo.otherPrivateDirty); - addPerfMetricInt("other_shared_dirty", memInfo.otherSharedDirty); - + mPerfMetrics = mPerformanceCollector.endSnapshot(); } } - private void addPerfMetricLong(String key, long value) { - mPerfMetrics.putLong("performance." + key, value); - } - - private void addPerfMetricInt(String key, int value) { - mPerfMetrics.putInt("performance." + key, value); - } - /** * Called when the instrumented application is stopping, after all of the * normal application cleanup has occurred. diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index a9cec5033b66..b1861acb708b 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -250,7 +250,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int BOND_SUCCESS = 0; /** A bond attempt failed because pins did not match, or remote device did - * not respond to pin request in time + * not respond to pin request in time * @hide */ public static final int UNBOND_REASON_AUTH_FAILED = 1; /** A bond attempt failed because the other side explicilty rejected @@ -266,9 +266,15 @@ public final class BluetoothDevice implements Parcelable { /** A bond attempt failed because a discovery is in progress * @hide */ public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; + /** A bond attempt failed because of authentication timeout + * @hide */ + public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; + /** A bond attempt failed because of repeated attempts + * @hide */ + public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** An existing bond was explicitly revoked * @hide */ - public static final int UNBOND_REASON_REMOVED = 6; + public static final int UNBOND_REASON_REMOVED = 8; /** The user will be prompted to enter a pin * @hide */ @@ -278,7 +284,13 @@ public final class BluetoothDevice implements Parcelable { public static final int PAIRING_VARIANT_PASSKEY = 1; /** The user will be prompted to confirm the passkey displayed on the screen * @hide */ - public static final int PAIRING_VARIANT_CONFIRMATION = 2; + public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; + /** The user will be prompted to accept or deny the incoming pairing request + * @hide */ + public static final int PAIRING_VARIANT_CONSENT = 3; + /** The user will be prompted to enter the passkey displayed on remote device + * @hide */ + public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; private static IBluetooth sService; /* Guarenteed constant after first object constructed */ diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 0bc8a9d2e482..0d43b2a83c72 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -24,7 +24,6 @@ import android.util.TypedValue; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.util.Locale; /** * Provides access to an application's raw asset files; see {@link Resources} diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 7d412a7b5554..ba5c9ed18a43 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -66,8 +66,6 @@ public class Resources { = new SparseArray<ColorStateList>(); private static boolean mPreloaded; - private final LongSparseArray<Drawable.ConstantState> mPreloadedDrawables; - /*package*/ final TypedValue mTmpValue = new TypedValue(); // These are protected by the mTmpValue lock. @@ -158,11 +156,6 @@ public class Resources { } updateConfiguration(config, metrics); assets.ensureStringBlocks(); - if (mCompatibilityInfo.isScalingRequired()) { - mPreloadedDrawables = emptySparseArray(); - } else { - mPreloadedDrawables = sPreloadedDrawables; - } } /** @@ -1669,7 +1662,7 @@ public class Resources { return dr; } - Drawable.ConstantState cs = mPreloadedDrawables.get(key); + Drawable.ConstantState cs = sPreloadedDrawables.get(key); if (cs != null) { dr = cs.newDrawable(); } else { @@ -1976,7 +1969,6 @@ public class Resources { mMetrics.setToDefaults(); updateConfiguration(null, null); mAssets.ensureStringBlocks(); - mPreloadedDrawables = sPreloadedDrawables; mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; } } diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java index 5bfdcc4a5578..30ecf5a81a4e 100755 --- a/core/java/android/gesture/GestureOverlayView.java +++ b/core/java/android/gesture/GestureOverlayView.java @@ -29,6 +29,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; import android.os.SystemClock; +import android.annotation.Widget; import com.android.internal.R; import java.util.ArrayList; @@ -50,6 +51,7 @@ import java.util.ArrayList; * @attr ref android.R.styleable#GestureOverlayView_orientation * @attr ref android.R.styleable#GestureOverlayView_uncertainGestureColor */ +@Widget public class GestureOverlayView extends FrameLayout { public static final int GESTURE_STROKE_TYPE_SINGLE = 0; public static final int GESTURE_STROKE_TYPE_MULTIPLE = 1; diff --git a/core/java/android/os/PerformanceCollector.java b/core/java/android/os/PerformanceCollector.java new file mode 100644 index 000000000000..4ca1f32bc0f5 --- /dev/null +++ b/core/java/android/os/PerformanceCollector.java @@ -0,0 +1,524 @@ +/* + * 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.os; + + +import java.util.ArrayList; + +/** + * Collects performance data between two function calls in Bundle objects and + * outputs the results using writer of type {@link PerformanceResultsWriter}. + * <p> + * {@link #beginSnapshot(String)} and {@link #endSnapshot()} functions collect + * memory usage information and measure runtime between calls to begin and end. + * These functions logically wrap around an entire test, and should be called + * with name of test as the label, e.g. EmailPerformanceTest. + * <p> + * {@link #startTiming(String)} and {@link #stopTiming(String)} functions + * measure runtime between calls to start and stop. These functions logically + * wrap around a single test case or a small block of code, and should be called + * with the name of test case as the label, e.g. testSimpleSendMailSequence. + * <p> + * {@link #addIteration(String)} inserts intermediate measurement point which + * can be labeled with a String, e.g. Launch email app, compose, send, etc. + * <p> + * Snapshot and timing functions do not interfere with each other, and thus can + * be called in any order. The intended structure is to wrap begin/endSnapshot + * around calls to start/stopTiming, for example: + * <p> + * <code>beginSnapshot("EmailPerformanceTest"); + * startTiming("testSimpleSendSequence"); + * addIteration("Launch email app"); + * addIteration("Compose"); + * stopTiming("Send"); + * startTiming("testComplexSendSequence"); + * stopTiming(""); + * startTiming("testAddLabel"); + * stopTiming(""); + * endSnapshot();</code> + * <p> + * Structure of results output is up to implementor of + * {@link PerformanceResultsWriter }. + * + * {@hide} Pending approval for public API. + */ +public class PerformanceCollector { + + /** + * Interface for reporting performance data. + */ + public interface PerformanceResultsWriter { + + /** + * Callback invoked as first action in + * PerformanceCollector#beginSnapshot(String) for reporting the start of + * a performance snapshot. + * + * @param label description of code block between beginSnapshot and + * PerformanceCollector#endSnapshot() + * @see PerformanceCollector#beginSnapshot(String) + */ + public void writeBeginSnapshot(String label); + + /** + * Callback invoked as last action in PerformanceCollector#endSnapshot() + * for reporting performance data collected in the snapshot. + * + * @param results memory and runtime metrics stored as key/value pairs, + * in the same structure as returned by + * PerformanceCollector#endSnapshot() + * @see PerformanceCollector#endSnapshot() + */ + public void writeEndSnapshot(Bundle results); + + /** + * Callback invoked as first action in + * PerformanceCollector#startTiming(String) for reporting the start of + * a timing measurement. + * + * @param label description of code block between startTiming and + * PerformanceCollector#stopTiming(String) + * @see PerformanceCollector#startTiming(String) + */ + public void writeStartTiming(String label); + + /** + * Callback invoked as last action in + * {@link PerformanceCollector#stopTiming(String)} for reporting the + * sequence of timings measured. + * + * @param results runtime metrics of code block between calls to + * startTiming and stopTiming, in the same structure as + * returned by PerformanceCollector#stopTiming(String) + * @see PerformanceCollector#stopTiming(String) + */ + public void writeStopTiming(Bundle results); + } + + /** + * In a results Bundle, this key references a List of iteration Bundles. + */ + public static final String METRIC_KEY_ITERATIONS = "iterations"; + /** + * In an iteration Bundle, this key describes the iteration. + */ + public static final String METRIC_KEY_LABEL = "label"; + /** + * In a results Bundle, this key reports the cpu time of the code block + * under measurement. + */ + public static final String METRIC_KEY_CPU_TIME = "cpu_time"; + /** + * In a results Bundle, this key reports the execution time of the code + * block under measurement. + */ + public static final String METRIC_KEY_EXECUTION_TIME = "execution_time"; + /** + * In a snapshot Bundle, this key reports the number of received + * transactions from the binder driver before collection started. + */ + public static final String METRIC_KEY_PRE_RECEIVED_TRANSACTIONS = "pre_received_transactions"; + /** + * In a snapshot Bundle, this key reports the number of transactions sent by + * the running program before collection started. + */ + public static final String METRIC_KEY_PRE_SENT_TRANSACTIONS = "pre_sent_transactions"; + /** + * In a snapshot Bundle, this key reports the number of received + * transactions from the binder driver. + */ + public static final String METRIC_KEY_RECEIVED_TRANSACTIONS = "received_transactions"; + /** + * In a snapshot Bundle, this key reports the number of transactions sent by + * the running program. + */ + public static final String METRIC_KEY_SENT_TRANSACTIONS = "sent_transactions"; + /** + * In a snapshot Bundle, this key reports the number of garbage collection + * invocations. + */ + public static final String METRIC_KEY_GC_INVOCATION_COUNT = "gc_invocation_count"; + /** + * In a snapshot Bundle, this key reports the amount of allocated memory + * used by the running program. + */ + public static final String METRIC_KEY_JAVA_ALLOCATED = "java_allocated"; + /** + * In a snapshot Bundle, this key reports the amount of free memory + * available to the running program. + */ + public static final String METRIC_KEY_JAVA_FREE = "java_free"; + /** + * In a snapshot Bundle, this key reports the number of private dirty pages + * used by dalvik. + */ + public static final String METRIC_KEY_JAVA_PRIVATE_DIRTY = "java_private_dirty"; + /** + * In a snapshot Bundle, this key reports the proportional set size for + * dalvik. + */ + public static final String METRIC_KEY_JAVA_PSS = "java_pss"; + /** + * In a snapshot Bundle, this key reports the number of shared dirty pages + * used by dalvik. + */ + public static final String METRIC_KEY_JAVA_SHARED_DIRTY = "java_shared_dirty"; + /** + * In a snapshot Bundle, this key reports the total amount of memory + * available to the running program. + */ + public static final String METRIC_KEY_JAVA_SIZE = "java_size"; + /** + * In a snapshot Bundle, this key reports the amount of allocated memory in + * the native heap. + */ + public static final String METRIC_KEY_NATIVE_ALLOCATED = "native_allocated"; + /** + * In a snapshot Bundle, this key reports the amount of free memory in the + * native heap. + */ + public static final String METRIC_KEY_NATIVE_FREE = "native_free"; + /** + * In a snapshot Bundle, this key reports the number of private dirty pages + * used by the native heap. + */ + public static final String METRIC_KEY_NATIVE_PRIVATE_DIRTY = "native_private_dirty"; + /** + * In a snapshot Bundle, this key reports the proportional set size for the + * native heap. + */ + public static final String METRIC_KEY_NATIVE_PSS = "native_pss"; + /** + * In a snapshot Bundle, this key reports the number of shared dirty pages + * used by the native heap. + */ + public static final String METRIC_KEY_NATIVE_SHARED_DIRTY = "native_shared_dirty"; + /** + * In a snapshot Bundle, this key reports the size of the native heap. + */ + public static final String METRIC_KEY_NATIVE_SIZE = "native_size"; + /** + * In a snapshot Bundle, this key reports the number of objects allocated + * globally. + */ + public static final String METRIC_KEY_GLOBAL_ALLOC_COUNT = "global_alloc_count"; + /** + * In a snapshot Bundle, this key reports the size of all objects allocated + * globally. + */ + public static final String METRIC_KEY_GLOBAL_ALLOC_SIZE = "global_alloc_size"; + /** + * In a snapshot Bundle, this key reports the number of objects freed + * globally. + */ + public static final String METRIC_KEY_GLOBAL_FREED_COUNT = "global_freed_count"; + /** + * In a snapshot Bundle, this key reports the size of all objects freed + * globally. + */ + public static final String METRIC_KEY_GLOBAL_FREED_SIZE = "global_freed_size"; + /** + * In a snapshot Bundle, this key reports the number of private dirty pages + * used by everything else. + */ + public static final String METRIC_KEY_OTHER_PRIVATE_DIRTY = "other_private_dirty"; + /** + * In a snapshot Bundle, this key reports the proportional set size for + * everything else. + */ + public static final String METRIC_KEY_OTHER_PSS = "other_pss"; + /** + * In a snapshot Bundle, this key reports the number of shared dirty pages + * used by everything else. + */ + public static final String METRIC_KEY_OTHER_SHARED_DIRTY = "other_shared_dirty"; + + private PerformanceResultsWriter mPerfWriter; + private Bundle mPerfSnapshot; + private Bundle mPerfMeasurement; + private long mSnapshotCpuTime; + private long mSnapshotExecTime; + private long mCpuTime; + private long mExecTime; + + public PerformanceCollector() { + } + + public PerformanceCollector(PerformanceResultsWriter writer) { + setPerformanceResultsWriter(writer); + } + + public void setPerformanceResultsWriter(PerformanceResultsWriter writer) { + mPerfWriter = writer; + } + + /** + * Begin collection of memory usage information. + * + * @param label description of code block between beginSnapshot and + * endSnapshot, used to label output + */ + public void beginSnapshot(String label) { + if (mPerfWriter != null) + mPerfWriter.writeBeginSnapshot(label); + startPerformanceSnapshot(); + } + + /** + * End collection of memory usage information. Returns collected data in a + * Bundle object. + * + * @return Memory and runtime metrics stored as key/value pairs. Values are + * of type long, and keys include: + * <ul> + * <li>{@link #METRIC_KEY_CPU_TIME cpu_time} + * <li>{@link #METRIC_KEY_EXECUTION_TIME execution_time} + * <li>{@link #METRIC_KEY_PRE_RECEIVED_TRANSACTIONS + * pre_received_transactions} + * <li>{@link #METRIC_KEY_PRE_SENT_TRANSACTIONS + * pre_sent_transactions} + * <li>{@link #METRIC_KEY_RECEIVED_TRANSACTIONS + * received_transactions} + * <li>{@link #METRIC_KEY_SENT_TRANSACTIONS sent_transactions} + * <li>{@link #METRIC_KEY_GC_INVOCATION_COUNT gc_invocation_count} + * <li>{@link #METRIC_KEY_JAVA_ALLOCATED java_allocated} + * <li>{@link #METRIC_KEY_JAVA_FREE java_free} + * <li>{@link #METRIC_KEY_JAVA_PRIVATE_DIRTY java_private_dirty} + * <li>{@link #METRIC_KEY_JAVA_PSS java_pss} + * <li>{@link #METRIC_KEY_JAVA_SHARED_DIRTY java_shared_dirty} + * <li>{@link #METRIC_KEY_JAVA_SIZE java_size} + * <li>{@link #METRIC_KEY_NATIVE_ALLOCATED native_allocated} + * <li>{@link #METRIC_KEY_NATIVE_FREE native_free} + * <li>{@link #METRIC_KEY_NATIVE_PRIVATE_DIRTY native_private_dirty} + * <li>{@link #METRIC_KEY_NATIVE_PSS native_pss} + * <li>{@link #METRIC_KEY_NATIVE_SHARED_DIRTY native_shared_dirty} + * <li>{@link #METRIC_KEY_NATIVE_SIZE native_size} + * <li>{@link #METRIC_KEY_GLOBAL_ALLOC_COUNT global_alloc_count} + * <li>{@link #METRIC_KEY_GLOBAL_ALLOC_SIZE global_alloc_size} + * <li>{@link #METRIC_KEY_GLOBAL_FREED_COUNT global_freed_count} + * <li>{@link #METRIC_KEY_GLOBAL_FREED_SIZE global_freed_size} + * <li>{@link #METRIC_KEY_OTHER_PRIVATE_DIRTY other_private_dirty} + * <li>{@link #METRIC_KEY_OTHER_PSS other_pss} + * <li>{@link #METRIC_KEY_OTHER_SHARED_DIRTY other_shared_dirty} + * </ul> + */ + public Bundle endSnapshot() { + endPerformanceSnapshot(); + if (mPerfWriter != null) + mPerfWriter.writeEndSnapshot(mPerfSnapshot); + return mPerfSnapshot; + } + + /** + * Start measurement of user and cpu time. + * + * @param label description of code block between startTiming and + * stopTiming, used to label output + */ + public void startTiming(String label) { + if (mPerfWriter != null) + mPerfWriter.writeStartTiming(label); + mPerfMeasurement = new Bundle(); + mPerfMeasurement.putParcelableArrayList( + METRIC_KEY_ITERATIONS, new ArrayList<Parcelable>()); + mExecTime = SystemClock.uptimeMillis(); + mCpuTime = Process.getElapsedCpuTime(); + } + + /** + * Add a measured segment, and start measuring the next segment. Returns + * collected data in a Bundle object. + * + * @param label description of code block between startTiming and + * addIteration, and between two calls to addIteration, used + * to label output + * @return Runtime metrics stored as key/value pairs. Values are of type + * long, and keys include: + * <ul> + * <li>{@link #METRIC_KEY_LABEL label} + * <li>{@link #METRIC_KEY_CPU_TIME cpu_time} + * <li>{@link #METRIC_KEY_EXECUTION_TIME execution_time} + * </ul> + */ + public Bundle addIteration(String label) { + mCpuTime = Process.getElapsedCpuTime() - mCpuTime; + mExecTime = SystemClock.uptimeMillis() - mExecTime; + + Bundle iteration = new Bundle(); + iteration.putString(METRIC_KEY_LABEL, label); + iteration.putLong(METRIC_KEY_EXECUTION_TIME, mExecTime); + iteration.putLong(METRIC_KEY_CPU_TIME, mCpuTime); + mPerfMeasurement.getParcelableArrayList(METRIC_KEY_ITERATIONS).add(iteration); + + mExecTime = SystemClock.uptimeMillis(); + mCpuTime = Process.getElapsedCpuTime(); + return iteration; + } + + /** + * Stop measurement of user and cpu time. + * + * @param label description of code block between addIteration or + * startTiming and stopTiming, used to label output + * @return Runtime metrics stored in a bundle, including all iterations + * between calls to startTiming and stopTiming. List of iterations + * is keyed by {@link #METRIC_KEY_ITERATIONS iterations}. + */ + public Bundle stopTiming(String label) { + addIteration(label); + if (mPerfWriter != null) + mPerfWriter.writeStopTiming(mPerfMeasurement); + return mPerfMeasurement; + } + + /* + * Starts tracking memory usage, binder transactions, and real & cpu timing. + */ + private void startPerformanceSnapshot() { + // Create new snapshot + mPerfSnapshot = new Bundle(); + + // Add initial binder counts + Bundle binderCounts = getBinderCounts(); + for (String key : binderCounts.keySet()) { + mPerfSnapshot.putLong("pre_" + key, binderCounts.getLong(key)); + } + + // Force a GC and zero out the performance counters. Do this + // before reading initial CPU/wall-clock times so we don't include + // the cost of this setup in our final metrics. + startAllocCounting(); + + // Record CPU time up to this point, and start timing. Note: this + // must happen at the end of this method, otherwise the timing will + // include noise. + mSnapshotExecTime = SystemClock.uptimeMillis(); + mSnapshotCpuTime = Process.getElapsedCpuTime(); + } + + /* + * Stops tracking memory usage, binder transactions, and real & cpu timing. + * Stores collected data as type long into Bundle object for reporting. + */ + private void endPerformanceSnapshot() { + // Stop the timing. This must be done first before any other counting is + // stopped. + mSnapshotCpuTime = Process.getElapsedCpuTime() - mSnapshotCpuTime; + mSnapshotExecTime = SystemClock.uptimeMillis() - mSnapshotExecTime; + + stopAllocCounting(); + + long nativeMax = Debug.getNativeHeapSize() / 1024; + long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; + long nativeFree = Debug.getNativeHeapFreeSize() / 1024; + + Debug.MemoryInfo memInfo = new Debug.MemoryInfo(); + Debug.getMemoryInfo(memInfo); + + Runtime runtime = Runtime.getRuntime(); + + long dalvikMax = runtime.totalMemory() / 1024; + long dalvikFree = runtime.freeMemory() / 1024; + long dalvikAllocated = dalvikMax - dalvikFree; + + // Add final binder counts + Bundle binderCounts = getBinderCounts(); + for (String key : binderCounts.keySet()) { + mPerfSnapshot.putLong(key, binderCounts.getLong(key)); + } + + // Add alloc counts + Bundle allocCounts = getAllocCounts(); + for (String key : allocCounts.keySet()) { + mPerfSnapshot.putLong(key, allocCounts.getLong(key)); + } + + mPerfSnapshot.putLong(METRIC_KEY_EXECUTION_TIME, mSnapshotExecTime); + mPerfSnapshot.putLong(METRIC_KEY_CPU_TIME, mSnapshotCpuTime); + + mPerfSnapshot.putLong(METRIC_KEY_NATIVE_SIZE, nativeMax); + mPerfSnapshot.putLong(METRIC_KEY_NATIVE_ALLOCATED, nativeAllocated); + mPerfSnapshot.putLong(METRIC_KEY_NATIVE_FREE, nativeFree); + mPerfSnapshot.putLong(METRIC_KEY_NATIVE_PSS, memInfo.nativePss); + mPerfSnapshot.putLong(METRIC_KEY_NATIVE_PRIVATE_DIRTY, memInfo.nativePrivateDirty); + mPerfSnapshot.putLong(METRIC_KEY_NATIVE_SHARED_DIRTY, memInfo.nativeSharedDirty); + + mPerfSnapshot.putLong(METRIC_KEY_JAVA_SIZE, dalvikMax); + mPerfSnapshot.putLong(METRIC_KEY_JAVA_ALLOCATED, dalvikAllocated); + mPerfSnapshot.putLong(METRIC_KEY_JAVA_FREE, dalvikFree); + mPerfSnapshot.putLong(METRIC_KEY_JAVA_PSS, memInfo.dalvikPss); + mPerfSnapshot.putLong(METRIC_KEY_JAVA_PRIVATE_DIRTY, memInfo.dalvikPrivateDirty); + mPerfSnapshot.putLong(METRIC_KEY_JAVA_SHARED_DIRTY, memInfo.dalvikSharedDirty); + + mPerfSnapshot.putLong(METRIC_KEY_OTHER_PSS, memInfo.otherPss); + mPerfSnapshot.putLong(METRIC_KEY_OTHER_PRIVATE_DIRTY, memInfo.otherPrivateDirty); + mPerfSnapshot.putLong(METRIC_KEY_OTHER_SHARED_DIRTY, memInfo.otherSharedDirty); + } + + /* + * Starts allocation counting. This triggers a gc and resets the counts. + */ + private static void startAllocCounting() { + // Before we start trigger a GC and reset the debug counts. Run the + // finalizers and another GC before starting and stopping the alloc + // counts. This will free up any objects that were just sitting around + // waiting for their finalizers to be run. + Runtime.getRuntime().gc(); + Runtime.getRuntime().runFinalization(); + Runtime.getRuntime().gc(); + + Debug.resetAllCounts(); + + // start the counts + Debug.startAllocCounting(); + } + + /* + * Stops allocation counting. + */ + private static void stopAllocCounting() { + Runtime.getRuntime().gc(); + Runtime.getRuntime().runFinalization(); + Runtime.getRuntime().gc(); + Debug.stopAllocCounting(); + } + + /* + * Returns a bundle with the current results from the allocation counting. + */ + private static Bundle getAllocCounts() { + Bundle results = new Bundle(); + results.putLong(METRIC_KEY_GLOBAL_ALLOC_COUNT, Debug.getGlobalAllocCount()); + results.putLong(METRIC_KEY_GLOBAL_ALLOC_SIZE, Debug.getGlobalAllocSize()); + results.putLong(METRIC_KEY_GLOBAL_FREED_COUNT, Debug.getGlobalFreedCount()); + results.putLong(METRIC_KEY_GLOBAL_FREED_SIZE, Debug.getGlobalFreedSize()); + results.putLong(METRIC_KEY_GC_INVOCATION_COUNT, Debug.getGlobalGcInvocationCount()); + return results; + } + + /* + * Returns a bundle with the counts for various binder counts for this + * process. Currently the only two that are reported are the number of send + * and the number of received transactions. + */ + private static Bundle getBinderCounts() { + Bundle results = new Bundle(); + results.putLong(METRIC_KEY_SENT_TRANSACTIONS, Debug.getBinderSentTransactions()); + results.putLong(METRIC_KEY_RECEIVED_TRANSACTIONS, Debug.getBinderReceivedTransactions()); + return results; + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 97955ae6a9e2..688f37720541 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2401,11 +2401,6 @@ public final class Settings { WIFI_NUM_ALLOWED_CHANNELS, WIFI_NUM_OPEN_NETWORKS_KEPT, BACKGROUND_DATA, - PREFERRED_NETWORK_MODE, - PREFERRED_TTY_MODE, - CDMA_CELL_BROADCAST_SMS, - PREFERRED_CDMA_SUBSCRIPTION, - ENHANCED_VOICE_PRIVACY_ENABLED }; /** diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 4c24c503f59e..1ed5c49e592e 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -17,8 +17,8 @@ package android.server; import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.content.Context; @@ -53,6 +53,7 @@ class BluetoothEventLoop { private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1; private static final int EVENT_RESTART_BLUETOOTH = 2; + private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 3; // The time (in millisecs) to delay the pairing attempt after the first // auto pairing attempt fails. We use an exponential delay with @@ -67,9 +68,10 @@ class BluetoothEventLoop { private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { + String address = null; switch (msg.what) { case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY: - String address = (String)msg.obj; + address = (String)msg.obj; if (address != null) { mBluetoothService.createBond(address); return; @@ -78,6 +80,12 @@ class BluetoothEventLoop { case EVENT_RESTART_BLUETOOTH: mBluetoothService.restart(); break; + case EVENT_PAIRING_CONSENT_DELAYED_ACCEPT: + address = (String)msg.obj; + if (address != null) { + mBluetoothService.setPairingConfirmation(address, true); + } + break; } } }; @@ -166,6 +174,17 @@ class BluetoothEventLoop { mContext.sendBroadcast(intent, BLUETOOTH_PERM); } + private void onDeviceDisconnectRequested(String deviceObjectPath) { + String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath); + if (address == null) { + Log.e(TAG, "onDeviceDisconnectRequested: Address of the remote device in null"); + return; + } + Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + } + private void onCreatePairedDeviceResult(String address, int result) { address = address.toUpperCase(); if (result == BluetoothDevice.BOND_SUCCESS) { @@ -228,6 +247,7 @@ class BluetoothEventLoop { addDevice(address, properties); } } + mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING); return; } @@ -379,7 +399,32 @@ class BluetoothEventLoop { return address; } - private void onRequestConfirmation(String objectPath, int passkey, int nativeData) { + private void onRequestPairingConsent(String objectPath, int nativeData) { + String address = checkPairingRequestAndGetAddress(objectPath, nativeData); + if (address == null) return; + + /* The link key will not be stored if the incoming request has MITM + * protection switched on. Unfortunately, some devices have MITM + * switched on even though their capabilities are NoInputNoOutput, + * so we may get this request many times. Also if we respond immediately, + * the other end is unable to handle it. Delay sending the message. + */ + if (mBluetoothService.getBondState().getBondState(address) == BluetoothDevice.BOND_BONDED) { + Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT); + message.obj = address; + mHandler.sendMessageDelayed(message, 1500); + return; + } + + Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); + intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, + BluetoothDevice.PAIRING_VARIANT_CONSENT); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + return; + } + + private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) { String address = checkPairingRequestAndGetAddress(objectPath, nativeData); if (address == null) return; @@ -387,7 +432,7 @@ class BluetoothEventLoop { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey); intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, - BluetoothDevice.PAIRING_VARIANT_CONFIRMATION); + BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); return; } @@ -436,6 +481,18 @@ class BluetoothEventLoop { return; } + private void onDisplayPasskey(String objectPath, int passkey, int nativeData) { + String address = checkPairingRequestAndGetAddress(objectPath, nativeData); + if (address == null) return; + + Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); + intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey); + intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, + BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY); + mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); + } + private boolean onAgentAuthorize(String objectPath, String deviceUuid) { String address = mBluetoothService.getAddressFromObjectPath(objectPath); if (address == null) { diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 53fb03cfdae0..6ce0f5f070ba 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1266,5 +1266,4 @@ public class BluetoothService extends IBluetooth.Stub { private native boolean setPairingConfirmationNative(String address, boolean confirm, int nativeData); private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value); - } diff --git a/core/java/android/test/TimedTest.java b/core/java/android/test/TimedTest.java new file mode 100644 index 000000000000..3a60a256823b --- /dev/null +++ b/core/java/android/test/TimedTest.java @@ -0,0 +1,32 @@ +/* + * 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.test; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This annotation can be used on an {@link junit.framework.TestCase}'s test + * methods. When the annotation is present, the test method is timed and the + * results written through instrumentation output. It can also be used on the + * class itself, which is equivalent to tagging all test methods with this + * annotation. + * + * {@hide} Pending approval for public API. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface TimedTest { }
\ No newline at end of file diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2f17bbc71ced..f63c2f1162e1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6038,16 +6038,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * some form of this public, but should think about the API. */ Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor) { - final int width = mRight - mLeft; - final int height = mBottom - mTop; + int width = mRight - mLeft; + int height = mBottom - mTop; - Bitmap bitmap = Bitmap.createBitmap(width, height, quality); + final AttachInfo attachInfo = mAttachInfo; + final float scale = attachInfo.mApplicationScale; + width = (int) ((width * scale) + 0.5f); + height = (int) ((height * scale) + 0.5f); + + Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, + height > 0 ? height : 1, quality); if (bitmap == null) { throw new OutOfMemoryError(); } + bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); + Canvas canvas; - final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { canvas = attachInfo.mCanvas; if (canvas == null) { @@ -6070,6 +6077,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility computeScroll(); final int restoreCount = canvas.save(); + canvas.scale(scale, scale); canvas.translate(-mScrollX, -mScrollY); // Temporarily remove the dirty mask diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a611d5a9168a..e98fd137954a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -732,7 +732,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } else if (digits != null) { mInput = DigitsKeyListener.getInstance(digits.toString()); - mInputType = inputType; + // If no input type was specified, we will default to generic + // text, since we can't tell the IME about the set of digits + // that was selected. + mInputType = inputType != EditorInfo.TYPE_NULL + ? inputType : EditorInfo.TYPE_CLASS_TEXT; } else if (inputType != EditorInfo.TYPE_NULL) { setInputType(inputType, true); singleLine = (inputType&(EditorInfo.TYPE_MASK_CLASS diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index fd5271e4c18b..50df0f72a545 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -1,4 +1,25 @@ +/* +** +** Copyright 2006, 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. +*/ + +#define LOG_TAG "9patch" +#define LOG_NDEBUG 1 + #include <utils/ResourceTypes.h> +#include <utils/Log.h> #include "SkCanvas.h" #include "SkRegion.h" @@ -62,6 +83,9 @@ public: if (destDensity == srcDensity || destDensity == 0 || srcDensity == 0) { + LOGV("Drawing unscaled 9-patch: (%g,%g)-(%g,%g)", + SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom)); NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); } else { canvas->save(); @@ -74,6 +98,11 @@ public: bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); bounds.fLeft = bounds.fTop = 0; + LOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", + SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), + SkScalarToFloat(bounds.fRight), SkScalarToFloat(bounds.fBottom), + srcDensity, destDensity); + NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL); canvas->restore(); diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp index 32d9b5789acd..ff24a87fd226 100644 --- a/core/jni/android/graphics/NinePatchImpl.cpp +++ b/core/jni/android/graphics/NinePatchImpl.cpp @@ -16,8 +16,10 @@ */ #define LOG_TAG "NinePatch" +#define LOG_NDEBUG 1 #include <utils/ResourceTypes.h> +#include <utils/Log.h> #include "SkBitmap.h" #include "SkCanvas.h" @@ -25,7 +27,7 @@ #include "SkPaint.h" #include "SkUnPreMultiply.h" -#define USE_TRACEx +#define USE_TRACE #ifdef USE_TRACE static bool gTrace; @@ -130,10 +132,10 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, SkASSERT(canvas || outRegion); -#if 0 +#ifdef USE_TRACE if (canvas) { const SkMatrix& m = canvas->getTotalMatrix(); - SkDebugf("ninepatch [%g %g %g] [%g %g %g]\n", + LOGV("ninepatch [%g %g %g] [%g %g %g]\n", SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]), SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5])); } @@ -141,10 +143,10 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, #ifdef USE_TRACE if (gTrace) { - SkDEBUGF(("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()))); - SkDEBUGF(("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height())); - SkDEBUGF(("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1])); - SkDEBUGF(("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1])); + LOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height())); + LOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height()); + LOGV("======== ninepatch xDivs [%d,%d]\n", chunk.xDivs[0], chunk.xDivs[1]); + LOGV("======== ninepatch yDivs [%d,%d]\n", chunk.yDivs[0], chunk.yDivs[1]); } #endif @@ -153,7 +155,7 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0)) { #ifdef USE_TRACE - if (gTrace) SkDEBUGF(("======== abort ninepatch draw\n")); + if (gTrace) LOGV("======== abort ninepatch draw\n"); #endif return; } @@ -198,8 +200,8 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, } int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining; -#if 0 - SkDebugf("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", +#ifdef USE_TRACE + LOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n", bitmap.width(), bitmap.height(), SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop), SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()), @@ -302,13 +304,13 @@ void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, goto nextDiv; } if (canvas) { -#if 0 - SkDebugf("-- src [%d %d %d %d] dst [%g %g %g %g]\n", +#ifdef USE_TRACE + LOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n", src.fLeft, src.fTop, src.width(), src.height(), SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop), SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height())); if (2 == src.width() && SkIntToScalar(5) == dst.width()) { - SkDebugf("--- skip patch\n"); + LOGV("--- skip patch\n"); } #endif drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor, diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 8fe7487a3482..e703ed85032d 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -45,13 +45,16 @@ static jmethodID method_onDeviceFound; static jmethodID method_onDeviceDisappeared; static jmethodID method_onDeviceCreated; static jmethodID method_onDeviceRemoved; +static jmethodID method_onDeviceDisconnectRequested; static jmethodID method_onCreatePairedDeviceResult; static jmethodID method_onGetDeviceServiceChannelResult; static jmethodID method_onRequestPinCode; static jmethodID method_onRequestPasskey; -static jmethodID method_onRequestConfirmation; +static jmethodID method_onRequestPasskeyConfirmation; +static jmethodID method_onRequestPairingConsent; +static jmethodID method_onDisplayPasskey; static jmethodID method_onAgentAuthorize; static jmethodID method_onAgentCancel; @@ -84,6 +87,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { "(Ljava/lang/String;)V"); method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V"); method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V"); + method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested", + "(Ljava/lang/String;)V"); method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult", "(Ljava/lang/String;I)V"); @@ -95,7 +100,11 @@ static void classInitNative(JNIEnv* env, jclass clazz) { "(Ljava/lang/String;I)V"); method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey", "(Ljava/lang/String;I)V"); - method_onRequestConfirmation = env->GetMethodID(clazz, "onRequestConfirmation", + method_onRequestPasskeyConfirmation = env->GetMethodID(clazz, "onRequestPasskeyConfirmation", + "(Ljava/lang/String;II)V"); + method_onRequestPairingConsent = env->GetMethodID(clazz, "onRequestPairingConsent", + "(Ljava/lang/String;I)V"); + method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey", "(Ljava/lang/String;II)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); @@ -227,7 +236,7 @@ static jboolean setUpEventLoop(native_data_t *nat) { const char * get_adapter_path(DBusConnection *conn) { - DBusMessage *msg, *reply = NULL; + DBusMessage *msg = NULL, *reply = NULL; DBusError err; const char *device_path = NULL; int attempt = 0; @@ -815,6 +824,14 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, str_array); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); goto success; + } else if (dbus_message_is_signal(msg, + "org.bluez.Device", + "DisconnectRequested")) { + const char *remote_device_path = dbus_message_get_path(msg); + env->CallVoidMethod(nat->me, + method_onDeviceDisconnectRequested, + env->NewStringUTF(remote_device_path)); + goto success; } ret = a2dp_event_filter(msg, env); @@ -845,9 +862,7 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Cancel")) { - env->CallVoidMethod(nat->me, method_onAgentCancel); - // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { @@ -928,6 +943,24 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, int(msg)); goto success; } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "DisplayPasskey")) { + char *object_path; + uint32_t passkey; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__); + goto failure; + } + + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onDisplayPasskey, + env->NewStringUTF(object_path), + passkey, + int(msg)); + goto success; + } else if (dbus_message_is_method_call(msg, "org.bluez.Agent", "RequestConfirmation")) { char *object_path; uint32_t passkey; @@ -940,12 +973,27 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, } dbus_message_ref(msg); // increment refcount because we pass to java - env->CallVoidMethod(nat->me, method_onRequestConfirmation, + env->CallVoidMethod(nat->me, method_onRequestPasskeyConfirmation, env->NewStringUTF(object_path), passkey, int(msg)); goto success; } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "RequestPairingConsent")) { + char *object_path; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPairingConsent() method", __FUNCTION__); + goto failure; + } + + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPairingConsent, + env->NewStringUTF(object_path), + int(msg)); + goto success; + } else if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Release")) { // reply DBusMessage *reply = dbus_message_new_method_return(msg); @@ -981,6 +1029,8 @@ success: #define BOND_RESULT_AUTH_CANCELED 3 #define BOND_RESULT_REMOTE_DEVICE_DOWN 4 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5 +#define BOND_RESULT_AUTH_TIMEOUT 6 +#define BOND_RESULT_REPEATED_ATTEMPTS 7 void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); @@ -1026,6 +1076,12 @@ void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { !strcmp(err.message, "Discover in progress")) { LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_DISCOVERY_IN_PROGRESS; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.RepeatedAttempts")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_REPEATED_ATTEMPTS; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationTimeout")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_AUTH_TIMEOUT; } else { LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); result = BOND_RESULT_ERROR; diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index de921f1e08fa..0b71acb5a85a 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -455,8 +455,8 @@ static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object, } if (!reply) { - LOGE("%s: Cannot create message reply to RequestConfirmation to " - "D-Bus\n", __FUNCTION__); + LOGE("%s: Cannot create message reply to RequestPasskeyConfirmation or" + "RequestPairingConsent to D-Bus\n", __FUNCTION__); dbus_message_unref(msg); return JNI_FALSE; } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 924e19e94f80..997efb813a6d 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -21,6 +21,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.util.TypedValue; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -170,6 +171,11 @@ public class NinePatchDrawable extends Drawable { @Override public void draw(Canvas canvas) { + if (false) { + float[] pts = new float[2]; + canvas.getMatrix().mapPoints(pts); + Log.v("9patch", "Drawing 9-patch @ " + pts[0] + "," + pts[1] + ": " + getBounds()); + } mNinePatch.draw(canvas, getBounds(), mPaint); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index 45a5e539f9e7..a86ef8f9fac9 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -415,7 +415,7 @@ public class MediaNames { null, null, "231180", "1", null}, {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", "1/8", "Suspended Animation", "John Petrucci", null, null, "20070510T125223.000Z", - "13", "Jaws Of Life", "2005", "19815424", "1", "m4a composer"}, + "13", "Jaws Of Life", "2005", "449329", "1", "m4a composer"}, {"/sdcard/media_api/metaDataTestMedias/M4V/sample_iPod.m4v", null, null, null, null, null, "20051220T202015.000Z", null, null, null, "85500", "2", null}, diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java index 5e213d704bbe..3667faeba8a1 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java @@ -97,6 +97,7 @@ public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<Medi int video_duration = MediaNames.VIDEO_H263_AAC_DURATION; int random_play_time = 0; int random_seek_time = 0; + int random_no_of_seek = 0; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); try { @@ -106,8 +107,13 @@ public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<Medi mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder()); mp.prepare(); mp.start(); + random_no_of_seek = generator.nextInt(10); + // make sure the seek at least run once. + if (random_no_of_seek == 0) { + random_no_of_seek = 1; + } // Random seek and play - for (int j = 0; j < generator.nextInt(10); j++) { + for (int j = 0; j < random_no_of_seek; j++) { random_play_time = generator.nextInt(video_duration / 2); Log.v(TAG, "Play time = " + random_play_time); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 13db005b7d50..8cfd956fd2cc 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -50,6 +50,7 @@ import android.util.Log; * List of settings that are backed up are stored in the Settings.java file */ public class SettingsBackupAgent extends BackupHelperAgent { + private static final boolean DEBUG = true; private static final String KEY_SYSTEM = "system"; private static final String KEY_SECURE = "secure"; @@ -242,6 +243,13 @@ public class SettingsBackupAgent extends BackupHelperAgent { pos += length; if (!TextUtils.isEmpty(settingName) && !TextUtils.isEmpty(settingValue)) { //Log.i(TAG, "Restore " + settingName + " = " + settingValue); + + // TODO: versioning rather than just an ad hoc blacklist to handle + // older varieties of backed-up data + if (invalidSavedSetting(contentUri, settingName, settingValue)) { + continue; + } + if (mSettingsHelper.restoreValue(settingName, settingValue)) { cv.clear(); cv.put(Settings.NameValueTable.NAME, settingName); @@ -252,6 +260,22 @@ public class SettingsBackupAgent extends BackupHelperAgent { } } + private boolean invalidSavedSetting(Uri contentUri, String settingName, String settingValue) { + // Even if these settings were stored, don't use them on restore + if (contentUri.equals(Settings.Secure.CONTENT_URI)) { + if (settingName.equals(Settings.Secure.PREFERRED_NETWORK_MODE) + || settingName.equals(Settings.Secure.PREFERRED_TTY_MODE) + || settingName.equals(Settings.Secure.CDMA_CELL_BROADCAST_SMS) + || settingName.equals(Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION) + || settingName.equals(Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED)) { + if (DEBUG) Log.v(TAG, "Ignoring restore datum: " + settingName); + return true; + } + } + + return false; + } + private String[] copyAndSort(String[] keys) { String[] sortedKeys = new String[keys.length]; System.arraycopy(keys, 0, sortedKeys, 0, keys.length); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a744486aca37..8914aceac592 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -159,7 +159,8 @@ public class TelephonyManager { /** * Returns the software version number for the device, for example, - * the IMEI/SV for GSM phones. + * the IMEI/SV for GSM phones. Return null if the software version is + * not available. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -168,13 +169,15 @@ public class TelephonyManager { try { return getSubscriberInfo().getDeviceSvn(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; } - return null; } /** - * Returns the unique device ID, for example, the IMEI for GSM and the MEID for CDMA - * phones. + * Returns the unique device ID, for example, the IMEI for GSM and the MEID + * for CDMA phones. Return null if device ID is not available. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -183,12 +186,15 @@ public class TelephonyManager { try { return getSubscriberInfo().getDeviceId(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; } - return null; } /** * Returns the current location of the device. + * Return null if current location is not available. * * <p>Requires Permission: * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or @@ -199,8 +205,10 @@ public class TelephonyManager { Bundle bundle = getITelephony().getCellLocation(); return CellLocation.newFromBundle(bundle); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; } - return null; } /** @@ -216,6 +224,7 @@ public class TelephonyManager { try { getITelephony().enableLocationUpdates(); } catch (RemoteException ex) { + } catch (NullPointerException ex) { } } @@ -232,6 +241,7 @@ public class TelephonyManager { try { getITelephony().disableLocationUpdates(); } catch (RemoteException ex) { + } catch (NullPointerException ex) { } } @@ -247,9 +257,10 @@ public class TelephonyManager { try { return getITelephony().getNeighboringCellInfo(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; } - return null; - } /** @@ -289,7 +300,11 @@ public class TelephonyManager { // This can happen when the ITelephony interface is not up yet. return getPhoneTypeFromProperty(); } - } catch(RemoteException ex){ + } catch (RemoteException ex) { + // This shouldn't happen in the normal case, as a backup we + // read from the system property. + return getPhoneTypeFromProperty(); + } catch (NullPointerException ex) { // This shouldn't happen in the normal case, as a backup we // read from the system property. return getPhoneTypeFromProperty(); @@ -418,9 +433,12 @@ public class TelephonyManager { // This can happen when the ITelephony interface is not up yet. return NETWORK_TYPE_UNKNOWN; } - } catch(RemoteException ex){ + } catch(RemoteException ex) { // This shouldn't happen in the normal case return NETWORK_TYPE_UNKNOWN; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return NETWORK_TYPE_UNKNOWN; } } @@ -489,6 +507,9 @@ public class TelephonyManager { } catch (RemoteException ex) { // Assume no ICC card if remote exception which shouldn't happen return false; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return false; } } @@ -556,7 +577,8 @@ public class TelephonyManager { } /** - * Returns the serial number of the SIM, if applicable. + * Returns the serial number of the SIM, if applicable. Return null if it is + * unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -565,8 +587,11 @@ public class TelephonyManager { try { return getSubscriberInfo().getIccSerialNumber(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; } - return null; } // @@ -577,6 +602,7 @@ public class TelephonyManager { /** * Returns the unique subscriber ID, for example, the IMSI for a GSM phone. + * Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -585,13 +611,16 @@ public class TelephonyManager { try { return getSubscriberInfo().getSubscriberId(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; } - return null; } /** * Returns the phone number string for line 1, for example, the MSISDN - * for a GSM phone. + * for a GSM phone. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -600,12 +629,16 @@ public class TelephonyManager { try { return getSubscriberInfo().getLine1Number(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; } - return null; } /** * Returns the alphabetic identifier associated with the line 1 number. + * Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -616,12 +649,15 @@ public class TelephonyManager { try { return getSubscriberInfo().getLine1AlphaTag(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; } - return null; } /** - * Returns the voice mail number. + * Returns the voice mail number. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -630,12 +666,15 @@ public class TelephonyManager { try { return getSubscriberInfo().getVoiceMailNumber(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; } - return null; } /** - * Returns the voice mail count. + * Returns the voice mail count. Return 0 if unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} @@ -645,8 +684,11 @@ public class TelephonyManager { try { return getITelephony().getVoiceMessageCount(); } catch (RemoteException ex) { + return 0; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return 0; } - return 0; } /** @@ -660,8 +702,11 @@ public class TelephonyManager { try { return getSubscriberInfo().getVoiceMailAlphaTag(); } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; } - return null; } private IPhoneSubInfo getSubscriberInfo() { @@ -759,6 +804,8 @@ public class TelephonyManager { } catch (RemoteException ex) { // the phone process is restarting. return DATA_DISCONNECTED; + } catch (NullPointerException ex) { + return DATA_DISCONNECTED; } } @@ -802,6 +849,8 @@ public class TelephonyManager { mRegistry.listen(pkgForDebug, listener.callback, events, notifyNow); } catch (RemoteException ex) { // system process dead + } catch (NullPointerException ex) { + // system process dead } } @@ -816,6 +865,8 @@ public class TelephonyManager { } catch (RemoteException ex) { // the phone process is restarting. return -1; + } catch (NullPointerException ex) { + return -1; } } @@ -832,6 +883,8 @@ public class TelephonyManager { } catch (RemoteException ex) { // the phone process is restarting. return -1; + } catch (NullPointerException ex) { + return -1; } } @@ -846,6 +899,8 @@ public class TelephonyManager { } catch (RemoteException ex) { // the phone process is restarting. return null; + } catch (NullPointerException ex) { + return null; } } } diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index ece708acf5c5..8d2785a3791c 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -100,7 +100,6 @@ public abstract class DataConnectionTracker extends Handler { public static final int EVENT_CLEAN_UP_CONNECTION = 34; protected static final int EVENT_CDMA_OTA_PROVISION = 35; protected static final int EVENT_RESTART_RADIO = 36; - private static final int EVENT_ENABLE_APN_REQUEST = 37; /***** Constants *****/ @@ -126,6 +125,10 @@ public abstract class DataConnectionTracker extends Handler { + "5000,10000,20000,40000,80000:5000,160000:5000," + "320000:5000,640000:5000,1280000:5000,1800000:5000"; + /** Retry configuration for secondary networks: 4 tries in 20 sec */ + protected static final String SECONDARY_DATA_RETRY_CONFIG = + "max_retries=3; 5000, 5000, 5000"; + /** Slow poll when attempting connection recovery. */ protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000; /** Default ping deadline, in seconds. */ @@ -171,7 +174,7 @@ public abstract class DataConnectionTracker extends Handler { protected boolean netStatPollEnabled = false; /** Manage the behavior of data retry after failure */ - protected final RetryManager mRetryMgr = new RetryManager(); + protected RetryManager mRetryMgr = new RetryManager(); // wifi connection status will be updated by sticky intent protected boolean mIsWifiConnected = false; @@ -266,33 +269,8 @@ public abstract class DataConnectionTracker extends Handler { public void handleMessage (Message msg) { switch (msg.what) { - case EVENT_ENABLE_APN_REQUEST: - int apnId = msg.arg1; - synchronized (this) { - if (DBG) { - Log.d(LOG_TAG, "got EVENT_ENABLE_APN_REQUEST with apnType = " + apnId + - " and enable = " + msg.arg2); - Log.d(LOG_TAG, "dataEnabled[apnId] = " + dataEnabled[apnId] + - ", enabledCount = " + enabledCount); - } - if (msg.arg2 == APN_ENABLED) { - // enable - if (!dataEnabled[apnId]) { - dataEnabled[apnId] = true; - enabledCount++; - } - onTrySetupData(null); - } else { - // disable - if (dataEnabled[apnId]) { - dataEnabled[apnId] = false; - enabledCount--; - if (enabledCount == 0) { - onCleanUpConnection(true, Phone.REASON_DATA_DISABLED); - } - } - } - } + case EVENT_ENABLE_NEW_APN: + onEnableApn(msg.arg1, msg.arg2); break; case EVENT_TRY_SETUP_DATA: @@ -392,6 +370,24 @@ public abstract class DataConnectionTracker extends Handler { } } + protected String apnIdToType(int id) { + switch (id) { + case APN_DEFAULT_ID: + return Phone.APN_TYPE_DEFAULT; + case APN_MMS_ID: + return Phone.APN_TYPE_MMS; + case APN_SUPL_ID: + return Phone.APN_TYPE_SUPL; + case APN_DUN_ID: + return Phone.APN_TYPE_DUN; + case APN_HIPRI_ID: + return Phone.APN_TYPE_HIPRI; + default: + Log.e(LOG_TAG, "Unknown id (" + id + ") in apnIdToType"); + return Phone.APN_TYPE_DEFAULT; + } + } + protected abstract boolean isApnTypeActive(String type); protected abstract boolean isApnTypeAvailable(String type); @@ -449,8 +445,6 @@ public abstract class DataConnectionTracker extends Handler { } setEnabled(id, true); - mRequestedApnType = type; - sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN)); return Phone.APN_REQUEST_STARTED; } @@ -471,7 +465,6 @@ public abstract class DataConnectionTracker extends Handler { if (isEnabled(id)) { setEnabled(id, false); if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { - mRequestedApnType = Phone.APN_TYPE_DEFAULT; if (dataEnabled[APN_DEFAULT_ID]) { return Phone.APN_ALREADY_ACTIVE; } else { @@ -485,16 +478,56 @@ public abstract class DataConnectionTracker extends Handler { } } - protected void setEnabled(int id, boolean enable) { + private void setEnabled(int id, boolean enable) { if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ") with old state = " + dataEnabled[id] + " and enabledCount = " + enabledCount); - Message msg = obtainMessage(EVENT_ENABLE_APN_REQUEST); + Message msg = obtainMessage(EVENT_ENABLE_NEW_APN); msg.arg1 = id; msg.arg2 = (enable ? APN_ENABLED : APN_DISABLED); sendMessage(msg); } + protected synchronized void onEnableApn(int apnId, int enabled) { + if (DBG) { + Log.d(LOG_TAG, "got EVENT_APN_ENABLE_REQUEST with apnType = " + apnId + + " and enable = " + enabled); + Log.d(LOG_TAG, "dataEnabled[apnId] = " + dataEnabled[apnId] + + ", enabledCount = " + enabledCount); + } + if (enabled == APN_ENABLED) { + if (!dataEnabled[apnId]) { + mRequestedApnType = apnIdToType(apnId); + onEnableNewApn(); + + dataEnabled[apnId] = true; + enabledCount++; + } + onTrySetupData(null); + } else { + // disable + if (dataEnabled[apnId]) { + dataEnabled[apnId] = false; + enabledCount--; + if (enabledCount == 0) { + onCleanUpConnection(true, Phone.REASON_DATA_DISABLED); + } else if (dataEnabled[APN_DEFAULT_ID] == true) { + mRequestedApnType = Phone.APN_TYPE_DEFAULT; + onEnableNewApn(); + } + } + } + } + + /** + * Called when we switch APNs. + * + * mRequestedApnType is set prior to call + * To be overridden. + */ + protected void onEnableNewApn() { + } + /** * Prevent mobile data connections from being established, * or once again allow mobile data connections. If the state diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 5203d3f5a98e..f32837fae4b6 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -154,6 +154,7 @@ public interface Phone { static final String REASON_CDMA_DATA_DETACHED = "cdmaDataDetached"; static final String REASON_APN_CHANGED = "apnChanged"; static final String REASON_APN_SWITCHED = "apnSwitched"; + static final String REASON_APN_FAILED = "apnFailed"; static final String REASON_RESTORE_DEFAULT_APN = "restoreDefaultApn"; static final String REASON_RADIO_TURNED_OFF = "radioTurnedOff"; static final String REASON_PDP_RESET = "pdpReset"; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 0215ab233c7b..b063e0a42a8d 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -150,6 +150,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { static final String APN_ID = "apn_id"; private boolean canSetPreferApn = false; + // for tracking retrys on the default APN + private RetryManager mDefaultRetryManager; + // for tracking retrys on a secondary APN + private RetryManager mSecondaryRetryManager; + BroadcastReceiver mIntentReceiver = new BroadcastReceiver () { @Override @@ -253,6 +258,19 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { mRetryMgr.configure(20, 2000, 1000); } } + + mDefaultRetryManager = mRetryMgr; + mSecondaryRetryManager = new RetryManager(); + + if (!mSecondaryRetryManager.configure(SystemProperties.get( + "ro.gsm.2nd_data_retry_config"))) { + if (!mSecondaryRetryManager.configure(SECONDARY_DATA_RETRY_CONFIG)) { + // Should never happen, log an error and default to a simple sequence. + Log.e(LOG_TAG, "Could note configure using SECONDARY_DATA_RETRY_CONFIG=" + + SECONDARY_DATA_RETRY_CONFIG); + mSecondaryRetryManager.configure("max_retries=3, 333, 333, 333"); + } + } } public void dispose() { @@ -1019,6 +1037,12 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { if (state == State.FAILED) { if (!mRetryMgr.isRetryNeeded()) { + if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { + // if no more retries on a secondary APN attempt, tell the world and revert. + phone.notifyDataConnection(Phone.REASON_APN_FAILED); + onEnableApn(apnTypeToId(mRequestedApnType), APN_DISABLED); + return; + } if (mReregisterOnReconnectFailure) { // We've re-registerd once now just retry forever. mRetryMgr.retryForeverUsingLastTimeout(); @@ -1069,7 +1093,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); } + @Override protected void onEnableNewApn() { + // change our retry manager to use the appropriate numbers for the new APN + if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { + mRetryMgr = mDefaultRetryManager; + } else { + mRetryMgr = mSecondaryRetryManager; + } + mRetryMgr.resetRetryCount(); + // TODO: To support simultaneous PDP contexts, this should really only call // cleanUpConnection if it needs to free up a PdpConnection. cleanUpConnection(true, Phone.REASON_APN_SWITCHED); @@ -1189,6 +1222,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // No try for permanent failure if (cause.isPermanentFail()) { notifyNoData(cause); + if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { + phone.notifyDataConnection(Phone.REASON_APN_FAILED); + onEnableApn(apnTypeToId(mRequestedApnType), APN_DISABLED); + } return; } @@ -1381,9 +1418,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private void startDelayedRetry(PdpConnection.FailCause cause, String reason) { notifyNoData(cause); - if (mRequestedApnType == Phone.APN_TYPE_DEFAULT) { - reconnectAfterFail(cause, reason); - } + reconnectAfterFail(cause, reason); } private void setPreferredApn(int pos) { @@ -1442,10 +1477,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { onRecordsLoaded(); break; - case EVENT_ENABLE_NEW_APN: - onEnableNewApn(); - break; - case EVENT_GPRS_DETACHED: onGprsDetached(); break; diff --git a/test-runner/android/test/AndroidTestRunner.java b/test-runner/android/test/AndroidTestRunner.java index 358b7e919200..0f1599aafd1c 100644 --- a/test-runner/android/test/AndroidTestRunner.java +++ b/test-runner/android/test/AndroidTestRunner.java @@ -18,6 +18,8 @@ package android.test; import android.app.Instrumentation; import android.content.Context; +import android.os.PerformanceCollector.PerformanceResultsWriter; + import com.google.android.collect.Lists; import junit.framework.Test; import junit.framework.TestCase; @@ -39,6 +41,7 @@ public class AndroidTestRunner extends BaseTestRunner { private List<TestListener> mTestListeners = Lists.newArrayList(); private Instrumentation mInstrumentation; + private PerformanceResultsWriter mPerfWriter; @SuppressWarnings("unchecked") public void setTestClassName(String testClassName, String testMethodName) { @@ -162,6 +165,7 @@ public class AndroidTestRunner extends BaseTestRunner { for (TestCase testCase : mTestCases) { setContextIfAndroidTestCase(testCase, mContext, testContext); setInstrumentationIfInstrumentationTestCase(testCase, mInstrumentation); + setPerformanceWriterIfPerformanceTestCase(testCase, mPerfWriter); testCase.run(mTestResult); } } @@ -184,6 +188,13 @@ public class AndroidTestRunner extends BaseTestRunner { } } + private void setPerformanceWriterIfPerformanceTestCase( + Test test, PerformanceResultsWriter writer) { + if (PerformanceTestBase.class.isAssignableFrom(test.getClass())) { + ((PerformanceTestBase) test).setPerformanceResultsWriter(writer); + } + } + public void setInstrumentation(Instrumentation instrumentation) { mInstrumentation = instrumentation; } @@ -197,6 +208,13 @@ public class AndroidTestRunner extends BaseTestRunner { setInstrumentation(instrumentation); } + /** + * {@hide} Pending approval for public API. + */ + public void setPerformanceResultsWriter(PerformanceResultsWriter writer) { + mPerfWriter = writer; + } + @Override protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException { return mContext.getClassLoader().loadClass(suiteClassName); diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java index 23f0ed40a8b3..b9978d65dd8b 100644 --- a/test-runner/android/test/InstrumentationTestRunner.java +++ b/test-runner/android/test/InstrumentationTestRunner.java @@ -17,17 +17,31 @@ package android.test; import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE; + +import com.android.internal.util.Predicate; + import android.app.Activity; import android.app.Instrumentation; import android.os.Bundle; import android.os.Debug; import android.os.Looper; +import android.os.Parcelable; +import android.os.PerformanceCollector; +import android.os.Process; +import android.os.SystemClock; +import android.os.PerformanceCollector.PerformanceResultsWriter; import android.test.suitebuilder.TestMethod; import android.test.suitebuilder.TestPredicates; import android.test.suitebuilder.TestSuiteBuilder; import android.util.Log; -import com.android.internal.util.Predicate; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import junit.framework.AssertionFailedError; import junit.framework.Test; @@ -38,22 +52,13 @@ import junit.framework.TestSuite; import junit.runner.BaseTestRunner; import junit.textui.ResultPrinter; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.PrintStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - - /** * An {@link Instrumentation} that runs various types of {@link junit.framework.TestCase}s against * an Android package (application). Typical usage: * <ol> * <li>Write {@link junit.framework.TestCase}s that perform unit, functional, or performance tests * against the classes in your package. Typically these are subclassed from: - * <ul><li>{@link android.test.ActivityInstrumentationTestCase}</li> + * <ul><li>{@link android.test.ActivityInstrumentationTestCase2}</li> * <li>{@link android.test.ActivityUnitTestCase}</li> * <li>{@link android.test.AndroidTestCase}</li> * <li>{@link android.test.ApplicationTestCase}</li> @@ -111,13 +116,13 @@ import java.util.List; * <p/> * <b>To run in 'log only' mode</b> * -e log true - * This option will load and iterate through all test classes and methods, but will bypass actual - * test execution. Useful for quickly obtaining info on the tests to be executed by an + * This option will load and iterate through all test classes and methods, but will bypass actual + * test execution. Useful for quickly obtaining info on the tests to be executed by an * instrumentation command. * <p/> * <b>To generate EMMA code coverage:</b> * -e coverage true - * Note: this requires an emma instrumented build. By default, the code coverage results file + * Note: this requires an emma instrumented build. By default, the code coverage results file * will be saved in a /data/<app>/coverage.ec file, unless overridden by coverageFile flag (see * below) * <p/> @@ -129,11 +134,10 @@ import java.util.List; /* (not JavaDoc) * Although not necessary in most case, another way to use this class is to extend it and have the - * derived class return - * the desired test suite from the {@link #getTestSuite()} method. The test suite returned from this - * method will be used if no target class is defined in the meta-data or command line argument - * parameters. If a derived class is used it needs to be added as an instrumentation to the - * AndroidManifest.xml and the command to run it would look like: + * derived class return the desired test suite from the {@link #getTestSuite()} method. The test + * suite returned from this method will be used if no target class is defined in the meta-data or + * command line argument parameters. If a derived class is used it needs to be added as an + * instrumentation to the AndroidManifest.xml and the command to run it would look like: * <p/> * adb shell am instrument -w com.android.foo/<i>com.android.FooInstrumentationTestRunner</i> * <p/> @@ -155,66 +159,65 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu public static final String ARGUMENT_DELAY_MSEC = "delay_msec"; private static final String SMALL_SUITE = "small"; - private static final String MEDIUM_SUITE = "medium"; + private static final String MEDIUM_SUITE = "medium"; private static final String LARGE_SUITE = "large"; - + private static final String ARGUMENT_LOG_ONLY = "log"; - /** - * This constant defines the maximum allowed runtime (in ms) for a test included in the "small" suite. - * It is used to make an educated guess at what suite an unlabeled test belongs. + * This constant defines the maximum allowed runtime (in ms) for a test included in the "small" + * suite. It is used to make an educated guess at what suite an unlabeled test belongs. */ private static final float SMALL_SUITE_MAX_RUNTIME = 100; - + /** - * This constant defines the maximum allowed runtime (in ms) for a test included in the "medium" suite. - * It is used to make an educated guess at what suite an unlabeled test belongs. + * This constant defines the maximum allowed runtime (in ms) for a test included in the + * "medium" suite. It is used to make an educated guess at what suite an unlabeled test belongs. */ private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000; - + /** - * The following keys are used in the status bundle to provide structured reports to - * an IInstrumentationWatcher. + * The following keys are used in the status bundle to provide structured reports to + * an IInstrumentationWatcher. */ /** - * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER}, + * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER}, * identifies InstrumentationTestRunner as the source of the report. This is sent with all * status messages. */ public static final String REPORT_VALUE_ID = "InstrumentationTestRunner"; /** - * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key * identifies the total number of tests that are being run. This is sent with all status * messages. */ public static final String REPORT_KEY_NUM_TOTAL = "numtests"; /** - * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key * identifies the sequence number of the current test. This is sent with any status message * describing a specific test being started or completed. */ public static final String REPORT_KEY_NUM_CURRENT = "current"; /** - * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key * identifies the name of the current test class. This is sent with any status message * describing a specific test being started or completed. */ public static final String REPORT_KEY_NAME_CLASS = "class"; /** - * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key * identifies the name of the current test. This is sent with any status message * describing a specific test being started or completed. */ public static final String REPORT_KEY_NAME_TEST = "test"; /** - * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key * reports the run time in seconds of the current test. */ private static final String REPORT_KEY_RUN_TIME = "runtime"; /** - * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key * reports the guessed suite assignment for the current test. */ private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment"; @@ -224,6 +227,19 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu */ private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath"; /** + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * reports the cpu time in milliseconds of the current test. + */ + private static final String REPORT_KEY_PERF_CPU_TIME = + "performance." + PerformanceCollector.METRIC_KEY_CPU_TIME; + /** + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * reports the run time in milliseconds of the current test. + */ + private static final String REPORT_KEY_PERF_EXECUTION_TIME = + "performance." + PerformanceCollector.METRIC_KEY_EXECUTION_TIME; + + /** * The test is starting. */ public static final int REPORT_VALUE_RESULT_START = 1; @@ -240,15 +256,15 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu */ public static final int REPORT_VALUE_RESULT_FAILURE = -2; /** - * If included in the status bundle sent to an IInstrumentationWatcher, this key - * identifies a stack trace describing an error or failure. This is sent with any status + * If included in the status bundle sent to an IInstrumentationWatcher, this key + * identifies a stack trace describing an error or failure. This is sent with any status * message describing a specific test being completed. */ public static final String REPORT_KEY_STACK = "stack"; // Default file name for code coverage private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec"; - + private static final String LOG_TAG = "InstrumentationTestRunner"; private final Bundle mResults = new Bundle(); @@ -316,7 +332,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu if (testSuite != null) { testSuiteBuilder.addTestSuite(testSuite); } else { - // no package or class bundle arguments were supplied, and no test suite + // no package or class bundle arguments were supplied, and no test suite // provided so add all tests in application testSuiteBuilder.includePackages(""); } @@ -324,7 +340,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu } else { parseTestClasses(testClassesArg, testSuiteBuilder); } - + testSuiteBuilder.addRequirements(getBuilderRequirements()); mTestRunner = getAndroidTestRunner(); @@ -336,8 +352,10 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu if (mSuiteAssignmentMode) { mTestRunner.addTestListener(new SuiteAssignmentPrinter()); } else { + WatcherResultPrinter resultPrinter = new WatcherResultPrinter(mTestCount); mTestRunner.addTestListener(new TestPrinter("TestRunner", false)); - mTestRunner.addTestListener(new WatcherResultPrinter(mTestCount)); + mTestRunner.addTestListener(resultPrinter); + mTestRunner.setPerformanceResultsWriter(resultPrinter); } start(); } @@ -347,7 +365,8 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu } /** - * Parses and loads the specified set of test classes + * Parses and loads the specified set of test classes + * * @param testClassArg - comma-separated list of test classes and methods * @param testSuiteBuilder - builder to add tests to */ @@ -360,8 +379,9 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu /** * Parse and load the given test class and, optionally, method - * @param testClassName - full package name of test class and optionally method to add. Expected - * format: com.android.TestClass#testMethod + * + * @param testClassName - full package name of test class and optionally method to add. + * Expected format: com.android.TestClass#testMethod * @param testSuiteBuilder - builder to add tests to */ private void parseTestClass(String testClassName, TestSuiteBuilder testSuiteBuilder) { @@ -372,8 +392,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu testMethodName = testClassName.substring(methodSeparatorIndex + 1); testClassName = testClassName.substring(0, methodSeparatorIndex); } - testSuiteBuilder.addTestClassByName(testClassName, testMethodName, - getTargetContext()); + testSuiteBuilder.addTestClassByName(testClassName, testMethodName, getTargetContext()); } protected AndroidTestRunner getAndroidTestRunner() { @@ -384,12 +403,12 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu String tagString = arguments.getString(tag); return tagString != null && Boolean.parseBoolean(tagString); } - + /* * Returns the size predicate object, corresponding to the "size" argument value. */ private Predicate<TestMethod> getSizePredicateFromArg(String sizeArg) { - + if (SMALL_SUITE.equals(sizeArg)) { return TestPredicates.SELECT_SMALL; } else if (MEDIUM_SUITE.equals(sizeArg)) { @@ -400,11 +419,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu return null; } } - + @Override public void onStart() { Looper.prepare(); - + if (mJustCount) { mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID); mResults.putInt(REPORT_KEY_NUM_TOTAL, mTestCount); @@ -413,30 +432,30 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu if (mDebug) { Debug.waitForDebugger(); } - + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); PrintStream writer = new PrintStream(byteArrayOutputStream); try { StringResultPrinter resultPrinter = new StringResultPrinter(writer); - + mTestRunner.addTestListener(resultPrinter); - + long startTime = System.currentTimeMillis(); mTestRunner.runTest(); long runTime = System.currentTimeMillis() - startTime; - + resultPrinter.print(mTestRunner.getTestResult(), runTime); } finally { - mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, - String.format("\nTest results for %s=%s", - mTestRunner.getTestClassName(), + mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + String.format("\nTest results for %s=%s", + mTestRunner.getTestClassName(), byteArrayOutputStream.toString())); if (mCoverage) { generateCoverageReport(); } writer.close(); - + finish(Activity.RESULT_OK, mResults); } } @@ -459,7 +478,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu public ClassLoader getLoader() { return null; } - + private void generateCoverageReport() { // use reflection to call emma dump coverage method, to avoid // always statically compiling against emma jar @@ -467,9 +486,9 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu java.io.File coverageFile = new java.io.File(coverageFilePath); try { Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); - Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", + Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", coverageFile.getClass(), boolean.class, boolean.class); - + dumpCoverageMethod.invoke(null, coverageFile, false, false); // output path to generated coverage file so it can be parsed by a test harness if // needed @@ -495,15 +514,14 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu private String getCoverageFilePath() { if (mCoverageFilePath == null) { return getTargetContext().getFilesDir().getAbsolutePath() + File.separator + - DEFAULT_COVERAGE_FILE_NAME; - } - else { + DEFAULT_COVERAGE_FILE_NAME; + } else { return mCoverageFilePath; } } private void reportEmmaError(Exception e) { - reportEmmaError("", e); + reportEmmaError("", e); } private void reportEmmaError(String hint, Exception e) { @@ -524,30 +542,29 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu printFooter(result); } } - + /** - * This class sends status reports back to the IInstrumentationWatcher about + * This class sends status reports back to the IInstrumentationWatcher about * which suite each test belongs. */ - private class SuiteAssignmentPrinter implements TestListener - { - + private class SuiteAssignmentPrinter implements TestListener { + private Bundle mTestResult; private long mStartTime; private long mEndTime; private boolean mTimingValid; - + public SuiteAssignmentPrinter() { } - + /** * send a status for the start of a each test, so long tests can be seen as "running" */ public void startTest(Test test) { mTimingValid = true; - mStartTime = System.currentTimeMillis(); + mStartTime = System.currentTimeMillis(); } - + /** * @see junit.framework.TestListener#addError(Test, Throwable) */ @@ -576,7 +593,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu runTime = -1; } else { runTime = mEndTime - mStartTime; - if (runTime < SMALL_SUITE_MAX_RUNTIME + if (runTime < SMALL_SUITE_MAX_RUNTIME && !InstrumentationTestCase.class.isAssignableFrom(test.getClass())) { assignmentSuite = SMALL_SUITE; } else if (runTime < MEDIUM_SUITE_MAX_RUNTIME) { @@ -588,8 +605,8 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu // Clear mStartTime so that we can verify that it gets set next time. mStartTime = -1; - mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, - test.getClass().getName() + "#" + ((TestCase) test).getName() + mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + test.getClass().getName() + "#" + ((TestCase) test).getName() + "\nin " + assignmentSuite + " suite\nrunTime: " + String.valueOf(runTime) + "\n"); mTestResult.putFloat(REPORT_KEY_RUN_TIME, runTime); @@ -598,36 +615,40 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu sendStatus(0, mTestResult); } } - + /** * This class sends status reports back to the IInstrumentationWatcher */ - private class WatcherResultPrinter implements TestListener - { + private class WatcherResultPrinter implements TestListener, PerformanceResultsWriter { private final Bundle mResultTemplate; Bundle mTestResult; int mTestNum = 0; int mTestResultCode = 0; String mTestClass = null; - + boolean mIsTimedTest = false; + long mCpuTime = 0; + long mExecTime = 0; + public WatcherResultPrinter(int numTests) { mResultTemplate = new Bundle(); mResultTemplate.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID); mResultTemplate.putInt(REPORT_KEY_NUM_TOTAL, numTests); } - + /** - * send a status for the start of a each test, so long tests can be seen as "running" + * send a status for the start of a each test, so long tests can be seen + * as "running" */ public void startTest(Test test) { String testClass = test.getClass().getName(); + String testName = ((TestCase)test).getName(); mTestResult = new Bundle(mResultTemplate); mTestResult.putString(REPORT_KEY_NAME_CLASS, testClass); - mTestResult.putString(REPORT_KEY_NAME_TEST, ((TestCase) test).getName()); + mTestResult.putString(REPORT_KEY_NAME_TEST, testName); mTestResult.putInt(REPORT_KEY_NUM_CURRENT, ++mTestNum); // pretty printing if (testClass != null && !testClass.equals(mTestClass)) { - mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, String.format("\n%s:", testClass)); mTestClass = testClass; } else { @@ -635,9 +656,9 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu } // The delay_msec parameter is normally used to provide buffers of idle time - // for power measurement purposes. To make sure there is a delay before and after + // for power measurement purposes. To make sure there is a delay before and after // every test in a suite, we delay *after* every test (see endTest below) and also - // delay *before* the first test. So, delay test1 delay test2 delay. + // delay *before* the first test. So, delay test1 delay test2 delay. try { if (mTestNum == 1) Thread.sleep(mDelayMsec); @@ -647,8 +668,25 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu sendStatus(REPORT_VALUE_RESULT_START, mTestResult); mTestResultCode = 0; + + mIsTimedTest = false; + try { + // Look for TimedTest annotation on both test class and test + // method + mIsTimedTest = test.getClass().isAnnotationPresent(TimedTest.class) || + test.getClass().getMethod(testName).isAnnotationPresent(TimedTest.class); + } catch (SecurityException e) { + throw new IllegalStateException(e); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(e); + } + + if (mIsTimedTest) { + mExecTime = SystemClock.uptimeMillis(); + mCpuTime = Process.getElapsedCpuTime(); + } } - + /** * @see junit.framework.TestListener#addError(Test, Throwable) */ @@ -656,9 +694,9 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t)); mTestResultCode = REPORT_VALUE_RESULT_ERROR; // pretty printing - mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, - String.format("\nError in %s:\n%s", - ((TestCase) test).getName(), BaseTestRunner.getFilteredTrace(t))); + mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + String.format("\nError in %s:\n%s", + ((TestCase)test).getName(), BaseTestRunner.getFilteredTrace(t))); } /** @@ -668,28 +706,68 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu mTestResult.putString(REPORT_KEY_STACK, BaseTestRunner.getFilteredTrace(t)); mTestResultCode = REPORT_VALUE_RESULT_FAILURE; // pretty printing - mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, - String.format("\nFailure in %s:\n%s", - ((TestCase) test).getName(), BaseTestRunner.getFilteredTrace(t))); + mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + String.format("\nFailure in %s:\n%s", + ((TestCase)test).getName(), BaseTestRunner.getFilteredTrace(t))); } /** * @see junit.framework.TestListener#endTest(Test) */ public void endTest(Test test) { + if (mIsTimedTest) { + mCpuTime = Process.getElapsedCpuTime() - mCpuTime; + mExecTime = SystemClock.uptimeMillis() - mExecTime; + mTestResult.putLong(REPORT_KEY_PERF_CPU_TIME, mCpuTime); + mTestResult.putLong(REPORT_KEY_PERF_EXECUTION_TIME, mExecTime); + } + if (mTestResultCode == 0) { mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "."); } sendStatus(mTestResultCode, mTestResult); - try { // Sleep after every test, if specified + try { // Sleep after every test, if specified Thread.sleep(mDelayMsec); } catch (InterruptedException e) { throw new IllegalStateException(e); } } + public void writeBeginSnapshot(String label) { + // Do nothing + } + + public void writeEndSnapshot(Bundle results) { + // Copy all snapshot data fields as type long into mResults, which + // is outputted via Instrumentation.finish + for (String key : results.keySet()) { + mResults.putLong(key, results.getLong(key)); + } + } + + public void writeStartTiming(String label) { + // Do nothing + } + + public void writeStopTiming(Bundle results) { + // Copy results into mTestResult by flattening list of iterations, + // which is outputted via WatcherResultPrinter.endTest + int i = 0; + for (Parcelable p : + results.getParcelableArrayList(PerformanceCollector.METRIC_KEY_ITERATIONS)) { + Bundle iteration = (Bundle)p; + String index = "performance.iteration" + i + "."; + mTestResult.putString(index + PerformanceCollector.METRIC_KEY_LABEL, + iteration.getString(PerformanceCollector.METRIC_KEY_LABEL)); + mTestResult.putLong(index + PerformanceCollector.METRIC_KEY_CPU_TIME, + iteration.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME)); + mTestResult.putLong(index + PerformanceCollector.METRIC_KEY_EXECUTION_TIME, + iteration.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME)); + i++; + } + } + // TODO report the end of the cycle - // TODO report runtime for each test } } diff --git a/test-runner/android/test/PerformanceTestBase.java b/test-runner/android/test/PerformanceTestBase.java index 93ac90ca8e20..572a9b8137a9 100644 --- a/test-runner/android/test/PerformanceTestBase.java +++ b/test-runner/android/test/PerformanceTestBase.java @@ -16,13 +16,95 @@ package android.test; -import android.test.PerformanceTestCase; -import junit.framework.TestCase; +import android.os.Bundle; +import android.os.PerformanceCollector; +import android.os.PerformanceCollector.PerformanceResultsWriter; + +import java.lang.reflect.Method; /** - * {@hide} Not needed for SDK. + * Provides hooks and wrappers to automatically and manually collect and report + * performance data in tests. + * + * {@hide} Pending approval for public API. */ -public abstract class PerformanceTestBase extends TestCase implements PerformanceTestCase { +public class PerformanceTestBase extends InstrumentationTestCase implements PerformanceTestCase { + + private static PerformanceCollector sPerfCollector = new PerformanceCollector(); + private static int sNumTestMethods = 0; + private static int sNumTestMethodsLeft = 0; + + // Count number of tests, used to emulate beforeClass and afterClass from JUnit4 + public PerformanceTestBase() { + if (sNumTestMethods == 0) { + Method methods[] = getClass().getMethods(); + for (Method m : methods) { + if (m.getName().startsWith("test")) { + sNumTestMethods ++; + sNumTestMethodsLeft ++; + } + } + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // @beforeClass + // Will skew timing measured by TestRunner, but not by PerformanceCollector + if (sNumTestMethodsLeft == sNumTestMethods) { + sPerfCollector.beginSnapshot(this.getClass().getName()); + } + } + + @Override + protected void tearDown() throws Exception { + // @afterClass + // Will skew timing measured by TestRunner, but not by PerformanceCollector + if (--sNumTestMethodsLeft == 0) { + sPerfCollector.endSnapshot(); + } + super.tearDown(); + } + + public void setPerformanceResultsWriter(PerformanceResultsWriter writer) { + sPerfCollector.setPerformanceResultsWriter(writer); + } + + /** + * @see PerformanceCollector#beginSnapshot(String) + */ + protected void beginSnapshot(String label) { + sPerfCollector.beginSnapshot(label); + } + + /** + * @see PerformanceCollector#endSnapshot() + */ + protected Bundle endSnapshot() { + return sPerfCollector.endSnapshot(); + } + + /** + * @see PerformanceCollector#startTiming(String) + */ + protected void startTiming(String label) { + sPerfCollector.startTiming(label); + } + + /** + * @see PerformanceCollector#addIteration(String) + */ + protected Bundle addIteration(String label) { + return sPerfCollector.addIteration(label); + } + + /** + * @see PerformanceCollector#stopTiming(String) + */ + protected Bundle stopTiming(String label) { + return sPerfCollector.stopTiming(label); + } public int startPerformance(PerformanceTestCase.Intermediates intermediates) { return 0; @@ -31,12 +113,4 @@ public abstract class PerformanceTestBase extends TestCase implements Performanc public boolean isPerformanceOnly() { return true; } - - /* - * Temporary hack to get some things working again. - */ - public void testRun() { - throw new RuntimeException("test implementation not provided"); - } } - diff --git a/tests/AndroidTests/src/com/android/unit_tests/os/PerformanceCollectorTest.java b/tests/AndroidTests/src/com/android/unit_tests/os/PerformanceCollectorTest.java new file mode 100644 index 000000000000..1a0c2d18698f --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/os/PerformanceCollectorTest.java @@ -0,0 +1,441 @@ +/* + * 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 com.android.unit_tests.os; + +import android.os.Bundle; +import android.os.Parcelable; +import android.os.PerformanceCollector; +import android.os.PerformanceCollector.PerformanceResultsWriter; +import android.test.suitebuilder.annotation.LargeTest; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Random; + +import junit.framework.TestCase; + +public class PerformanceCollectorTest extends TestCase { + + private PerformanceCollector mPerfCollector; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mPerfCollector = new PerformanceCollector(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + mPerfCollector = null; + } + + public void testBeginSnapshotNoWriter() throws Exception { + mPerfCollector.beginSnapshot("testBeginSnapshotNoWriter"); + + assertTrue((Long)readPrivateField("mSnapshotCpuTime", mPerfCollector) > 0); + assertTrue((Long)readPrivateField("mSnapshotExecTime", mPerfCollector) > 0); + Bundle snapshot = (Bundle)readPrivateField("mPerfSnapshot", mPerfCollector); + assertNotNull(snapshot); + assertEquals(2, snapshot.size()); + } + + @LargeTest + public void testEndSnapshotNoWriter() throws Exception { + mPerfCollector.beginSnapshot("testEndSnapshotNoWriter"); + sleepForRandomLongPeriod(); + Bundle snapshot = mPerfCollector.endSnapshot(); + + verifySnapshotBundle(snapshot); + } + + public void testStartTimingNoWriter() throws Exception { + mPerfCollector.startTiming("testStartTimingNoWriter"); + + assertTrue((Long)readPrivateField("mCpuTime", mPerfCollector) > 0); + assertTrue((Long)readPrivateField("mExecTime", mPerfCollector) > 0); + Bundle measurement = (Bundle)readPrivateField("mPerfMeasurement", mPerfCollector); + assertNotNull(measurement); + verifyTimingBundle(measurement, new ArrayList<String>()); + } + + public void testAddIterationNoWriter() throws Exception { + mPerfCollector.startTiming("testAddIterationNoWriter"); + sleepForRandomTinyPeriod(); + Bundle iteration = mPerfCollector.addIteration("timing1"); + + verifyIterationBundle(iteration, "timing1"); + } + + public void testStopTimingNoWriter() throws Exception { + mPerfCollector.startTiming("testStopTimingNoWriter"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("timing2"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("timing3"); + sleepForRandomShortPeriod(); + Bundle timing = mPerfCollector.stopTiming("timing4"); + + ArrayList<String> labels = new ArrayList<String>(); + labels.add("timing2"); + labels.add("timing3"); + labels.add("timing4"); + verifyTimingBundle(timing, labels); + } + + public void testBeginSnapshot() throws Exception { + MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); + mPerfCollector.setPerformanceResultsWriter(writer); + mPerfCollector.beginSnapshot("testBeginSnapshot"); + + assertEquals("testBeginSnapshot", writer.snapshotLabel); + assertTrue((Long)readPrivateField("mSnapshotCpuTime", mPerfCollector) > 0); + assertTrue((Long)readPrivateField("mSnapshotExecTime", mPerfCollector) > 0); + Bundle snapshot = (Bundle)readPrivateField("mPerfSnapshot", mPerfCollector); + assertNotNull(snapshot); + assertEquals(2, snapshot.size()); + } + + @LargeTest + public void testEndSnapshot() throws Exception { + MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); + mPerfCollector.setPerformanceResultsWriter(writer); + mPerfCollector.beginSnapshot("testEndSnapshot"); + sleepForRandomLongPeriod(); + Bundle snapshot1 = mPerfCollector.endSnapshot(); + Bundle snapshot2 = writer.snapshotResults; + + assertTrue(snapshot1.equals(snapshot2)); + verifySnapshotBundle(snapshot1); + } + + public void testStartTiming() throws Exception { + MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); + mPerfCollector.setPerformanceResultsWriter(writer); + mPerfCollector.startTiming("testStartTiming"); + + assertEquals("testStartTiming", writer.timingLabel); + assertTrue((Long)readPrivateField("mCpuTime", mPerfCollector) > 0); + assertTrue((Long)readPrivateField("mExecTime", mPerfCollector) > 0); + Bundle measurement = (Bundle)readPrivateField("mPerfMeasurement", mPerfCollector); + assertNotNull(measurement); + verifyTimingBundle(measurement, new ArrayList<String>()); + } + + public void testAddIteration() throws Exception { + mPerfCollector.startTiming("testAddIteration"); + sleepForRandomTinyPeriod(); + Bundle iteration = mPerfCollector.addIteration("timing5"); + + verifyIterationBundle(iteration, "timing5"); + } + + public void testStopTiming() throws Exception { + mPerfCollector.startTiming("testStopTiming"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("timing6"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("timing7"); + sleepForRandomShortPeriod(); + Bundle timing = mPerfCollector.stopTiming("timing8"); + + ArrayList<String> labels = new ArrayList<String>(); + labels.add("timing6"); + labels.add("timing7"); + labels.add("timing8"); + verifyTimingBundle(timing, labels); + } + + @LargeTest + public void testSimpleSequence() throws Exception { + MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); + mPerfCollector.setPerformanceResultsWriter(writer); + mPerfCollector.beginSnapshot("testSimpleSequence"); + mPerfCollector.startTiming("testSimpleSequenceTiming"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration1"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration2"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration3"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration4"); + sleepForRandomShortPeriod(); + Bundle timing = mPerfCollector.stopTiming("iteration5"); + sleepForRandomLongPeriod(); + Bundle snapshot1 = mPerfCollector.endSnapshot(); + Bundle snapshot2 = writer.snapshotResults; + + assertTrue(snapshot1.equals(snapshot2)); + verifySnapshotBundle(snapshot1); + + ArrayList<String> labels = new ArrayList<String>(); + labels.add("iteration1"); + labels.add("iteration2"); + labels.add("iteration3"); + labels.add("iteration4"); + labels.add("iteration5"); + verifyTimingBundle(timing, labels); + } + + @LargeTest + public void testLongSequence() throws Exception { + MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); + mPerfCollector.setPerformanceResultsWriter(writer); + mPerfCollector.beginSnapshot("testLongSequence"); + mPerfCollector.startTiming("testLongSequenceTiming1"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration1"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration2"); + sleepForRandomShortPeriod(); + Bundle timing1 = mPerfCollector.stopTiming("iteration3"); + sleepForRandomLongPeriod(); + + mPerfCollector.startTiming("testLongSequenceTiming2"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration4"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration5"); + sleepForRandomShortPeriod(); + Bundle timing2 = mPerfCollector.stopTiming("iteration6"); + sleepForRandomLongPeriod(); + + mPerfCollector.startTiming("testLongSequenceTiming3"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration7"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration8"); + sleepForRandomShortPeriod(); + Bundle timing3 = mPerfCollector.stopTiming("iteration9"); + sleepForRandomLongPeriod(); + + mPerfCollector.startTiming("testLongSequenceTiming4"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration10"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration11"); + sleepForRandomShortPeriod(); + Bundle timing4 = mPerfCollector.stopTiming("iteration12"); + sleepForRandomLongPeriod(); + + mPerfCollector.startTiming("testLongSequenceTiming5"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration13"); + sleepForRandomTinyPeriod(); + mPerfCollector.addIteration("iteration14"); + sleepForRandomShortPeriod(); + Bundle timing5 = mPerfCollector.stopTiming("iteration15"); + sleepForRandomLongPeriod(); + Bundle snapshot1 = mPerfCollector.endSnapshot(); + Bundle snapshot2 = writer.snapshotResults; + + assertTrue(snapshot1.equals(snapshot2)); + verifySnapshotBundle(snapshot1); + + ArrayList<String> labels1 = new ArrayList<String>(); + labels1.add("iteration1"); + labels1.add("iteration2"); + labels1.add("iteration3"); + verifyTimingBundle(timing1, labels1); + ArrayList<String> labels2 = new ArrayList<String>(); + labels2.add("iteration4"); + labels2.add("iteration5"); + labels2.add("iteration6"); + verifyTimingBundle(timing2, labels2); + ArrayList<String> labels3 = new ArrayList<String>(); + labels3.add("iteration7"); + labels3.add("iteration8"); + labels3.add("iteration9"); + verifyTimingBundle(timing3, labels3); + ArrayList<String> labels4 = new ArrayList<String>(); + labels4.add("iteration10"); + labels4.add("iteration11"); + labels4.add("iteration12"); + verifyTimingBundle(timing4, labels4); + ArrayList<String> labels5 = new ArrayList<String>(); + labels5.add("iteration13"); + labels5.add("iteration14"); + labels5.add("iteration15"); + verifyTimingBundle(timing5, labels5); + } + + /* + * Verify that snapshotting and timing do not interfere w/ each other, + * by staggering calls to snapshot and timing functions. + */ + @LargeTest + public void testOutOfOrderSequence() { + MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter(); + mPerfCollector.setPerformanceResultsWriter(writer); + mPerfCollector.startTiming("testOutOfOrderSequenceTiming"); + sleepForRandomShortPeriod(); + mPerfCollector.beginSnapshot("testOutOfOrderSequenceSnapshot"); + sleepForRandomShortPeriod(); + Bundle timing1 = mPerfCollector.stopTiming("timing1"); + sleepForRandomShortPeriod(); + Bundle snapshot1 = mPerfCollector.endSnapshot(); + + Bundle timing2 = writer.timingResults; + Bundle snapshot2 = writer.snapshotResults; + + assertTrue(snapshot1.equals(snapshot2)); + verifySnapshotBundle(snapshot1); + + assertTrue(timing1.equals(timing2)); + ArrayList<String> labels = new ArrayList<String>(); + labels.add("timing1"); + verifyTimingBundle(timing1, labels); + } + + private void sleepForRandomPeriod(int minDuration, int maxDuration) { + Random random = new Random(); + int period = minDuration + random.nextInt(maxDuration - minDuration); + int slept = 0; + // Generate random positive amount of work, so cpu time is measurable in + // milliseconds + while (slept < period) { + int step = random.nextInt(minDuration/5); + try { + Thread.sleep(step); + } catch (InterruptedException e ) { + // eat the exception + } + slept += step; + } + } + + private void sleepForRandomTinyPeriod() { + sleepForRandomPeriod(25, 50); + } + + private void sleepForRandomShortPeriod() { + sleepForRandomPeriod(100, 250); + } + + private void sleepForRandomLongPeriod() { + sleepForRandomPeriod(500, 1000); + } + + private void verifySnapshotBundle(Bundle snapshot) { + assertTrue("At least 26 metrics collected", 26 <= snapshot.size()); + + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_CPU_TIME)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_EXECUTION_TIME)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME) > 0); + + assertTrue(snapshot.containsKey( + PerformanceCollector.METRIC_KEY_PRE_RECEIVED_TRANSACTIONS)); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_PRE_SENT_TRANSACTIONS)); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_RECEIVED_TRANSACTIONS)); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_SENT_TRANSACTIONS)); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GC_INVOCATION_COUNT)); + + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_ALLOCATED)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_ALLOCATED) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_FREE)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_FREE) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_PRIVATE_DIRTY)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_PRIVATE_DIRTY) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_PSS)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_PSS) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_SHARED_DIRTY)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_SHARED_DIRTY) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_SIZE)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_SIZE) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_ALLOCATED)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_ALLOCATED) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_FREE)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_FREE) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_PRIVATE_DIRTY)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_PRIVATE_DIRTY) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_PSS)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_PSS) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_SHARED_DIRTY)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_SHARED_DIRTY) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_SIZE)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_SIZE) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_COUNT)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_COUNT) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_SIZE)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_SIZE) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_COUNT)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_COUNT) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_SIZE)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_SIZE) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_PRIVATE_DIRTY)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_PRIVATE_DIRTY) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_PSS)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_PSS) > 0); + assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_SHARED_DIRTY)); + assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_SHARED_DIRTY) > 0); + } + + private void verifyIterationBundle(Bundle iteration, String label) { + assertEquals(3, iteration.size()); + assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_LABEL)); + assertEquals(label, iteration.getString(PerformanceCollector.METRIC_KEY_LABEL)); + assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_CPU_TIME)); + assertTrue(iteration.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME) > 0); + assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_EXECUTION_TIME)); + assertTrue(iteration.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME) > 0); + } + + private void verifyTimingBundle(Bundle timing, ArrayList<String> labels) { + assertEquals(1, timing.size()); + assertTrue(timing.containsKey(PerformanceCollector.METRIC_KEY_ITERATIONS)); + ArrayList<Parcelable> iterations = timing.getParcelableArrayList( + PerformanceCollector.METRIC_KEY_ITERATIONS); + assertNotNull(iterations); + assertEquals(labels.size(), iterations.size()); + for (int i = 0; i < labels.size(); i ++) { + Bundle iteration = (Bundle)iterations.get(i); + verifyIterationBundle(iteration, labels.get(i)); + } + } + + private Object readPrivateField(String fieldName, Object object) throws Exception { + Field f = object.getClass().getDeclaredField(fieldName); + f.setAccessible(true); + return f.get(object); + } + + private class MockPerformanceResultsWriter implements PerformanceResultsWriter { + + public String snapshotLabel; + public Bundle snapshotResults = new Bundle(); + public String timingLabel; + public Bundle timingResults = new Bundle(); + + public void writeBeginSnapshot(String label) { + snapshotLabel = label; + } + + public void writeEndSnapshot(Bundle results) { + snapshotResults = results; + } + + public void writeStartTiming(String label) { + timingLabel = label; + } + + public void writeStopTiming(Bundle results) { + timingResults = results; + } + } +} |