Merge "Switch UM to internal isUserUnlockingOrUnlocked"
diff --git a/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java b/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java
index 6a49c03..80ce22e 100644
--- a/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java
+++ b/apct-tests/perftests/core/src/java/lang/perftests/SystemPerfTest.java
@@ -37,4 +37,10 @@
System.nanoTime();
}
}
+
+ @Test
+ public void testBenchmarkOverhead() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {}
+ }
}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
index cdbca63..88cb8e6 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
@@ -57,8 +57,6 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
public class UserLifecycleTest {
- private final int MIN_REPEAT_TIMES = 4;
-
private final int TIMEOUT_REMOVE_USER_MS = 4 * 1000; // 4 sec
private final int CHECK_USER_REMOVED_INTERVAL_MS = 200; // 0.2 sec
@@ -90,7 +88,6 @@
mAm = context.getSystemService(ActivityManager.class);
mIam = ActivityManagerNative.getDefault();
mState = mPerfStatusReporter.getBenchmarkState();
- mState.setMinRepeatTimes(MIN_REPEAT_TIMES);
mUsersToRemove = new ArrayList<>();
}
@@ -300,4 +297,4 @@
mUsersToRemove.add(userId);
}
}
-}
\ No newline at end of file
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index b27d71b..bf04f6d 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.concurrent.TimeUnit;
/**
* Provides a benchmark framework.
@@ -41,39 +42,47 @@
* System.out.println(state.summaryLine());
* }
*/
-public class BenchmarkState {
+public final class BenchmarkState {
private static final String TAG = "BenchmarkState";
- private static final int NOT_STARTED = 1; // The benchmark has not started yet.
+ private static final int NOT_STARTED = 0; // The benchmark has not started yet.
+ private static final int WARMUP = 1; // The benchmark is warming up.
private static final int RUNNING = 2; // The benchmark is running.
private static final int RUNNING_PAUSED = 3; // The benchmark is temporary paused.
private static final int FINISHED = 4; // The benchmark has stopped.
private int mState = NOT_STARTED; // Current benchmark state.
- private long mNanoPreviousTime = 0; // Previously captured System.nanoTime().
- private long mNanoFinishTime = 0; // Finish if System.nanoTime() returns after than this value.
- private long mNanoPausedTime = 0; // The System.nanoTime() when the pauseTiming() is called.
- private long mNanoPausedDuration = 0; // The duration of paused state in nano sec.
- private long mNanoTimeLimit = 1 * 1000 * 1000 * 1000; // 1 sec. Default time limit.
+ private static final long WARMUP_DURATION_NS = ms2ns(250); // warm-up for at least 250ms
+ private static final int WARMUP_MIN_ITERATIONS = 16; // minimum iterations to warm-up for
+
+ // TODO: Tune these values.
+ private static final long TARGET_TEST_DURATION_NS = ms2ns(500); // target testing for 500 ms
+ private static final int MAX_TEST_ITERATIONS = 1000000;
+ private static final int MIN_TEST_ITERATIONS = 100;
+ private static final int REPEAT_COUNT = 5;
+
+ private long mStartTimeNs = 0; // Previously captured System.nanoTime().
+ private long mPausedTimeNs = 0; // The System.nanoTime() when the pauseTiming() is called.
+ private long mPausedDurationNs = 0; // The duration of paused state in nano sec.
+
+ private int mIteration = 0;
+ private int mMaxIterations = 0;
+
+ private int mRepeatCount = 0;
// Statistics. These values will be filled when the benchmark has finished.
// The computation needs double precision, but long int is fine for final reporting.
private long mMedian = 0;
private double mMean = 0.0;
private double mStandardDeviation = 0.0;
-
- // Number of iterations needed for calculating the stats.
- private int mMinRepeatTimes = 16;
+ private long mMin = 0;
// Individual duration in nano seconds.
private ArrayList<Long> mResults = new ArrayList<>();
- /**
- * Sets the number of iterations needed for calculating the stats. Default is 16.
- */
- public void setMinRepeatTimes(int minRepeatTimes) {
- mMinRepeatTimes = minRepeatTimes;
+ private static final long ms2ns(long ms) {
+ return TimeUnit.MILLISECONDS.toNanos(ms);
}
/**
@@ -89,8 +98,13 @@
mMedian = size % 2 == 0 ? (mResults.get(size / 2) + mResults.get(size / 2 + 1)) / 2 :
mResults.get(size / 2);
+ mMin = mResults.get(0);
for (int i = 0; i < size; ++i) {
- mMean += mResults.get(i);
+ long result = mResults.get(i);
+ mMean += result;
+ if (result < mMin) {
+ mMin = result;
+ }
}
mMean /= (double) size;
@@ -108,7 +122,7 @@
throw new IllegalStateException(
"Unable to pause the benchmark. The benchmark has already paused.");
}
- mNanoPausedTime = System.nanoTime();
+ mPausedTimeNs = System.nanoTime();
mState = RUNNING_PAUSED;
}
@@ -119,11 +133,43 @@
throw new IllegalStateException(
"Unable to resume the benchmark. The benchmark is already running.");
}
- mNanoPausedDuration += System.nanoTime() - mNanoPausedTime;
- mNanoPausedTime = 0;
+ mPausedDurationNs += System.nanoTime() - mPausedTimeNs;
+ mPausedTimeNs = 0;
mState = RUNNING;
}
+ private void beginWarmup() {
+ mStartTimeNs = System.nanoTime();
+ mIteration = 0;
+ mState = WARMUP;
+ }
+
+ private void beginBenchmark(long warmupDuration, int iterations) {
+ mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
+ mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
+ Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
+ mPausedDurationNs = 0;
+ mIteration = 0;
+ mRepeatCount = 0;
+ mState = RUNNING;
+ mStartTimeNs = System.nanoTime();
+ }
+
+ private boolean startNextTestRun() {
+ final long currentTime = System.nanoTime();
+ mResults.add((currentTime - mStartTimeNs - mPausedDurationNs) / mMaxIterations);
+ mRepeatCount++;
+ if (mRepeatCount >= REPEAT_COUNT) {
+ calculateSatistics();
+ mState = FINISHED;
+ return false;
+ }
+ mPausedDurationNs = 0;
+ mIteration = 0;
+ mStartTimeNs = System.nanoTime();
+ return true;
+ }
+
/**
* Judges whether the benchmark needs more samples.
*
@@ -132,23 +178,22 @@
public boolean keepRunning() {
switch (mState) {
case NOT_STARTED:
- mNanoPreviousTime = System.nanoTime();
- mNanoFinishTime = mNanoPreviousTime + mNanoTimeLimit;
- mState = RUNNING;
+ beginWarmup();
+ return true;
+ case WARMUP:
+ mIteration++;
+ // Only check nanoTime on every iteration in WARMUP since we
+ // don't yet have a target iteration count.
+ final long duration = System.nanoTime() - mStartTimeNs;
+ if (mIteration >= WARMUP_MIN_ITERATIONS && duration >= WARMUP_DURATION_NS) {
+ beginBenchmark(duration, mIteration);
+ }
return true;
case RUNNING:
- final long currentTime = System.nanoTime();
- mResults.add(currentTime - mNanoPreviousTime - mNanoPausedDuration);
- mNanoPausedDuration = 0;
-
- // To calculate statistics, needs two or more samples.
- if (mResults.size() > mMinRepeatTimes && currentTime > mNanoFinishTime) {
- calculateSatistics();
- mState = FINISHED;
- return false;
+ mIteration++;
+ if (mIteration >= mMaxIterations) {
+ return startNextTestRun();
}
-
- mNanoPreviousTime = currentTime;
return true;
case RUNNING_PAUSED:
throw new IllegalStateException(
@@ -161,21 +206,28 @@
}
}
- public long mean() {
+ private long mean() {
if (mState != FINISHED) {
throw new IllegalStateException("The benchmark hasn't finished");
}
return (long) mMean;
}
- public long median() {
+ private long median() {
if (mState != FINISHED) {
throw new IllegalStateException("The benchmark hasn't finished");
}
return mMedian;
}
- public long standardDeviation() {
+ private long min() {
+ if (mState != FINISHED) {
+ throw new IllegalStateException("The benchmark hasn't finished");
+ }
+ return mMin;
+ }
+
+ private long standardDeviation() {
if (mState != FINISHED) {
throw new IllegalStateException("The benchmark hasn't finished");
}
@@ -187,10 +239,11 @@
sb.append("Summary: ");
sb.append("median=").append(median()).append("ns, ");
sb.append("mean=").append(mean()).append("ns, ");
+ sb.append("min=").append(min()).append("ns, ");
sb.append("sigma=").append(standardDeviation()).append(", ");
sb.append("iteration=").append(mResults.size()).append(", ");
// print out the first few iterations' number for double checking.
- int sampleNumber = Math.min(mResults.size(), mMinRepeatTimes);
+ int sampleNumber = Math.min(mResults.size(), 16);
for (int i = 0; i < sampleNumber; i++) {
sb.append("No ").append(i).append(" result is ").append(mResults.get(i)).append(", ");
}
@@ -202,6 +255,7 @@
Bundle status = new Bundle();
status.putLong(key + "_median", median());
status.putLong(key + "_mean", mean());
+ status.putLong(key + "_min", min());
status.putLong(key + "_standardDeviation", standardDeviation());
instrumentation.sendStatus(Activity.RESULT_OK, status);
}
diff --git a/api/current.txt b/api/current.txt
index 9f5a8e2..0508268 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31683,6 +31683,7 @@
method public static void notifyDirectoryChange(android.content.ContentResolver);
field public static final java.lang.String ACCOUNT_NAME = "accountName";
field public static final java.lang.String ACCOUNT_TYPE = "accountType";
+ field public static final java.lang.String CALLER_PACKAGE_PARAM_KEY = "callerPackage";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_directory";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/contact_directories";
field public static final android.net.Uri CONTENT_URI;
diff --git a/api/system-current.txt b/api/system-current.txt
index 4d7c4af..644bfd2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -34307,6 +34307,7 @@
method public static void notifyDirectoryChange(android.content.ContentResolver);
field public static final java.lang.String ACCOUNT_NAME = "accountName";
field public static final java.lang.String ACCOUNT_TYPE = "accountType";
+ field public static final java.lang.String CALLER_PACKAGE_PARAM_KEY = "callerPackage";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_directory";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/contact_directories";
field public static final android.net.Uri CONTENT_URI;
diff --git a/api/test-current.txt b/api/test-current.txt
index 4be2e7f..1020982 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -31761,6 +31761,7 @@
method public static void notifyDirectoryChange(android.content.ContentResolver);
field public static final java.lang.String ACCOUNT_NAME = "accountName";
field public static final java.lang.String ACCOUNT_TYPE = "accountType";
+ field public static final java.lang.String CALLER_PACKAGE_PARAM_KEY = "callerPackage";
field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_directory";
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/contact_directories";
field public static final android.net.Uri CONTENT_URI;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 02c8f0c..f487d08 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6586,7 +6586,7 @@
*/
public void removeUnsafeExtras() {
if (mExtras != null) {
- mExtras.filterValues();
+ mExtras = mExtras.filterValues();
}
}
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 7061589..d04d6c2 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -329,25 +329,49 @@
* Filter values in Bundle to only basic types.
* @hide
*/
- public void filterValues() {
+ public Bundle filterValues() {
unparcel();
+ Bundle bundle = this;
if (mMap != null) {
- for (int i = mMap.size() - 1; i >= 0; i--) {
- Object value = mMap.valueAt(i);
+ ArrayMap<String, Object> map = mMap;
+ for (int i = map.size() - 1; i >= 0; i--) {
+ Object value = map.valueAt(i);
if (PersistableBundle.isValidType(value)) {
continue;
}
if (value instanceof Bundle) {
- ((Bundle)value).filterValues();
+ Bundle newBundle = ((Bundle)value).filterValues();
+ if (newBundle != value) {
+ if (map == mMap) {
+ // The filter had to generate a new bundle, but we have not yet
+ // created a new one here. Do that now.
+ bundle = new Bundle(this);
+ // Note the ArrayMap<> constructor is guaranteed to generate
+ // a new object with items in the same order as the original.
+ map = bundle.mMap;
+ }
+ // Replace this current entry with the new child bundle.
+ map.setValueAt(i, newBundle);
+ }
+ continue;
}
if (value.getClass().getName().startsWith("android.")) {
continue;
}
- mMap.removeAt(i);
+ if (map == mMap) {
+ // This is the first time we have had to remove something, that means we
+ // need to switch to a new Bundle.
+ bundle = new Bundle(this);
+ // Note the ArrayMap<> constructor is guaranteed to generate
+ // a new object with items in the same order as the original.
+ map = bundle.mMap;
+ }
+ map.removeAt(i);
}
}
mFlags |= FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_FDS;
+ return bundle;
}
/**
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a1763c0..8d8c122 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -661,6 +661,12 @@
ContentValues contentValues = new ContentValues();
resolver.update(Directory.CONTENT_URI, contentValues, null, null);
}
+
+ /**
+ * A query parameter that's passed to directory providers which indicates the client
+ * package name that has made the query requests.
+ */
+ public static final String CALLER_PACKAGE_PARAM_KEY = "callerPackage";
}
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 3e8d577..2316b38 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -436,6 +436,14 @@
public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
/**
+ * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that
+ * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to
+ * prevent generating redundant {@link #ACTION_HOVER_ENTER} events.
+ * @hide
+ */
+ public static final int FLAG_HOVER_EXIT_PENDING = 0x4;
+
+ /**
* Private flag that indicates when the system has detected that this motion event
* may be inconsistent with respect to the sequence of previously delivered motion events,
* such as when a pointer move event is sent but the pointer is not down.
@@ -1947,6 +1955,20 @@
: flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
}
+ /** @hide */
+ public final boolean isHoverExitPending() {
+ final int flags = getFlags();
+ return (flags & FLAG_HOVER_EXIT_PENDING) != 0;
+ }
+
+ /** @hide */
+ public void setHoverExitPending(boolean hoverExitPending) {
+ final int flags = getFlags();
+ nativeSetFlags(mNativePtr, hoverExitPending
+ ? flags | FLAG_HOVER_EXIT_PENDING
+ : flags & ~FLAG_HOVER_EXIT_PENDING);
+ }
+
/**
* Returns the time (in ms) when the user originally pressed down to start
* a stream of position events.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 49d664e..e37a93a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -48,6 +48,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Interpolator;
import android.graphics.LinearGradient;
@@ -763,6 +764,9 @@
AccessibilityEventSource {
private static final boolean DBG = false;
+ /** @hide */
+ public static boolean DEBUG_DRAW = false;
+
/**
* The logging tag used by this class with android.util.Log.
*/
@@ -1190,6 +1194,8 @@
*/
static final int PARENT_SAVE_DISABLED_MASK = 0x20000000;
+ private static Paint sDebugPaint;
+
/** @hide */
@IntDef(flag = true,
value = {
@@ -1661,6 +1667,10 @@
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
| AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
+ static final int DEBUG_CORNERS_COLOR = Color.rgb(63, 127, 255);
+
+ static final int DEBUG_CORNERS_SIZE_DIP = 8;
+
/**
* Temporary Rect currently for use in setBackground(). This will probably
* be extended in the future to hold our own class with more than just
@@ -4749,6 +4759,10 @@
mRenderNode = RenderNode.create(getClass().getName(), this);
}
+ final boolean debugDraw() {
+ return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
+ }
+
private static SparseArray<String> getAttributeMap() {
if (mAttributeMap == null) {
mAttributeMap = new SparseArray<>();
@@ -16143,6 +16157,9 @@
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
+ if (debugDraw()) {
+ debugDrawFocus(canvas);
+ }
} else {
draw(canvas);
}
@@ -17121,6 +17138,41 @@
return more;
}
+ static Paint getDebugPaint() {
+ if (sDebugPaint == null) {
+ sDebugPaint = new Paint();
+ sDebugPaint.setAntiAlias(false);
+ }
+ return sDebugPaint;
+ }
+
+ final int dipsToPixels(int dips) {
+ float scale = getContext().getResources().getDisplayMetrics().density;
+ return (int) (dips * scale + 0.5f);
+ }
+
+ final private void debugDrawFocus(Canvas canvas) {
+ if (isFocused()) {
+ final int cornerSquareSize = dipsToPixels(DEBUG_CORNERS_SIZE_DIP);
+ final int w = getWidth();
+ final int h = getHeight();
+ final Paint paint = getDebugPaint();
+ paint.setColor(DEBUG_CORNERS_COLOR);
+
+ // Draw squares in corners.
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(0, 0, cornerSquareSize, cornerSquareSize, paint);
+ canvas.drawRect(w - cornerSquareSize, 0, w, cornerSquareSize, paint);
+ canvas.drawRect(0, h - cornerSquareSize, cornerSquareSize, h, paint);
+ canvas.drawRect(w - cornerSquareSize, h - cornerSquareSize, w, h, paint);
+
+ // Draw big X across the view.
+ paint.setStyle(Paint.Style.STROKE);
+ canvas.drawLine(0, 0, getWidth(), getHeight(), paint);
+ canvas.drawLine(0, getHeight(), getWidth(), 0, paint);
+ }
+ }
+
/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
@@ -17175,6 +17227,10 @@
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
+ if (debugDraw()) {
+ debugDrawFocus(canvas);
+ }
+
// we're done...
return;
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 87a85f1..e39cb96 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -116,8 +116,6 @@
private static final String TAG = "ViewGroup";
private static final boolean DBG = false;
- /** @hide */
- public static boolean DEBUG_DRAW = false;
/**
* Views which have been hidden or removed which need to be animated on
@@ -476,7 +474,6 @@
private static final int ARRAY_INITIAL_CAPACITY = 12;
private static final int ARRAY_CAPACITY_INCREMENT = 12;
- private static Paint sDebugPaint;
private static float[] sDebugLines;
// Used to draw cached views
@@ -586,10 +583,6 @@
initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
}
- private boolean debugDraw() {
- return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
- }
-
private void initViewGroup() {
// ViewGroup doesn't draw by default
if (!debugDraw()) {
@@ -1865,8 +1858,11 @@
// Synthesize an exit from a move or enter.
// Ignore the result because hover focus has moved to a different view.
if (action == MotionEvent.ACTION_HOVER_MOVE) {
+ final boolean hoverExitPending = event.isHoverExitPending();
+ event.setHoverExitPending(true);
dispatchTransformedGenericPointerEvent(
event, child); // move
+ event.setHoverExitPending(hoverExitPending);
}
eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
@@ -1880,8 +1876,10 @@
firstOldHoverTarget = nextOldHoverTarget;
}
- // Send events to the view group itself if no children have handled it.
- boolean newHoveredSelf = !handled;
+ // Send events to the view group itself if no children have handled it and the view group
+ // itself is not currently being hover-exited.
+ boolean newHoveredSelf = !handled &&
+ (action != MotionEvent.ACTION_HOVER_EXIT) && !event.isHoverExitPending();
if (newHoveredSelf == mHoveredSelf) {
if (newHoveredSelf) {
// Send event to the view group as before.
@@ -3375,11 +3373,6 @@
fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
}
- private int dipsToPixels(int dips) {
- float scale = getContext().getResources().getDisplayMetrics().density;
- return (int) (dips * scale + 0.5f);
- }
-
private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
int lineLength, int lineWidth) {
drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
@@ -3448,10 +3441,10 @@
// Draw clip bounds
{
- paint.setColor(Color.rgb(63, 127, 255));
+ paint.setColor(DEBUG_CORNERS_COLOR);
paint.setStyle(Paint.Style.FILL);
- int lineLength = dipsToPixels(8);
+ int lineLength = dipsToPixels(DEBUG_CORNERS_SIZE_DIP);
int lineWidth = dipsToPixels(1);
for (int i = 0; i < getChildCount(); i++) {
View c = getChildAt(i);
@@ -7926,14 +7919,6 @@
}
}
- private static Paint getDebugPaint() {
- if (sDebugPaint == null) {
- sDebugPaint = new Paint();
- sDebugPaint.setAntiAlias(false);
- }
- return sDebugPaint;
- }
-
private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
if (sDebugLines== null) {
// TODO: This won't work with multiple UI threads in a single process
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0942a24..f988130 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3044,7 +3044,7 @@
<!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
@hide -->
<permission android:name="android.permission.PEERS_MAC_ADDRESS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|setup" />
<!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
can use this permission to ensure incoming Nfc messages are from the Nfc stack
diff --git a/docs/html/wear/preview/downloads.jd b/docs/html/wear/preview/downloads.jd
index da6a06d..bfa384b 100644
--- a/docs/html/wear/preview/downloads.jd
+++ b/docs/html/wear/preview/downloads.jd
@@ -346,7 +346,8 @@
</p>
<p class="warning">
- <strong>Warning:</strong> Installing a system image on a watch removes all data from the
+ <strong>Warning:</strong> Installing a system image on a watch
+ removes all data from the
watch, so you should back up your data first.
</p>
@@ -355,8 +356,7 @@
</h4>
<p>
- From the phone, unpair ("Forget") the watch.
- Then on the watch, enable the Developer Options menu and ADB debugging as
+ On the watch, enable the Developer Options menu and ADB debugging as
follows:
</p>
@@ -365,14 +365,14 @@
</li>
<li>Scroll to the bottom of the menu. If no <strong>Developer
- Options</strong> item is provided, tap <strong>About</strong>.
+ Options</strong> item is provided, tap <strong>System</strong>
+ and then <strong>About</strong>.
</li>
<li>Tap the build number 7 times.
</li>
- <li>From the Settings menu, tap the <strong>Developer Options</strong>
- item.
+ <li>From the Settings menu, tap <strong>Developer Options</strong>.
</li>
<li>Enable ADB debugging.
@@ -418,7 +418,9 @@
</li>
<li>Use the following <a href="{@docRoot}tools/help/adb.html">adb
- command</a> to confirm that the watch is available for flashing:
+ command</a> to confirm that the watch is recognized.
+ You may need to turn ADB debugging off and then on for the watch to
+ be recognized:
<code>adb devices</code>
</li>
@@ -432,11 +434,11 @@
devices, <code>fastboot oem unlock</code>
</li>
- <li>On the watch, select the <strong>Unlock</strong> option.
+ <li>On the watch, select the option to unlock the bootloader.
</li>
- <li>Navigate to the directory where you unzipped the system image in Step
- 1. At the top level of that directory,
+ <li>On your computer, navigate to the directory where you unzipped the
+ system image in Step 1. At the top level of that directory,
execute the <code>flash-all</code> script by typing
<code>flash-all.sh</code> or, in the case of Windows,
<code>flash-all.bat</code>. The following may need to
@@ -449,16 +451,16 @@
Set up the watch
</h4>
- <p>
- After the <code>flash-all</code> script finishes, your watch reboots.
- Only pair the watch with a phone (so you can begin testing the preview)
- by using the instructions in <a href="#set_up_a_phone">Set Up a Phone</a>.
- Additionally, before installing an app, perform the
- following steps on the watch to re-secure the watch's bootloader:
+ <p>
+ After the <code>flash-all</code> script finishes, the watch reboots.
+ Only pair the watch with a phone (so you can begin testing the preview)
+ by using the instructions in <a href="#set_up_a_phone">Set Up a Phone</a>.
+ Additionally, before installing an app, perform the
+ following steps on the watch to re-secure the watch's bootloader:
</p>
<ol>
- <li>Open the Settings menu (on the watch).
+ <li>Open the Settings menu by long-pressing the physical button.
</li>
<li>Scroll to the bottom of the menu and tap <strong>About</strong>.
@@ -467,15 +469,16 @@
<li>Tap the build number 7 times.
</li>
- <li>From the Settings menu, tap the <strong>Developer Options</strong>
- item.
+ <li>From the Settings menu, tap <strong>Developer Options</strong>.
</li>
<li>Enable ADB debugging.
</li>
<li>Connect the watch to your computer and tap <strong>Always allow from
- this computer</strong>.
+ this computer</strong>. (You may need to turn ADB debugging off
+ and then on, to be prompted to always allow ADB debugging from
+ the connected computer.)
</li>
<li>Use the following adb command to start the device in fastboot mode:
@@ -487,8 +490,11 @@
devices, <code>fastboot oem lock</code>
</li>
- <li>On the watch, continue the boot by choosing
- <strong>Start</strong> and touching <strong>'0'</strong>.
+ <li>On the watch, continue the boot as follows:
+ On an LGE Watch Urbane 2nd Edition, choose
+ <strong>Start</strong> and touch <strong>'0'</strong>.
+ On a Huawei Watch, confirm that <strong>Reboot</strong> is chosen and
+ long-press the physical button.
</li>
</ol>
@@ -610,7 +616,8 @@
<p>
After you install the beta version of the companion app on a phone,
- you can pair the phone to the watch:
+ unpair ("Forget") any obsolete watch pairings, if necessary.
+ Then you can pair the phone to a newly-imaged watch:
</p>
<ol>
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 7182da1..d59115e 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -162,6 +162,7 @@
r = mRestoredWithoutUids.get(name);
if (r == null) {
r = new Record();
+ r.pkg = name;
mRestoredWithoutUids.put(name, r);
}
} else {
@@ -606,7 +607,7 @@
}
public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
- if (!removingPackage || pkgList == null || pkgList.length == 0
+ if (removingPackage || pkgList == null || pkgList.length == 0
|| mRestoredWithoutUids.isEmpty()) {
return; // nothing to do
}
diff --git a/services/core/java/com/android/server/utils/PriorityDump.java b/services/core/java/com/android/server/utils/PriorityDump.java
new file mode 100644
index 0000000..c05cc3f
--- /dev/null
+++ b/services/core/java/com/android/server/utils/PriorityDump.java
@@ -0,0 +1,179 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Helper for {@link android.os.Binder#dump(java.io.FileDescriptor, String[])} that supports the
+ * {@link #PRIORITY_ARG} argument.
+ * <p>
+ * Typical usage:
+ *
+ * <pre><code>
+public class SpringfieldNuclearPowerPlant extends Binder {
+
+ private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
+
+ @Override
+ public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Donuts in the box: 1");
+ }
+
+ @Override
+ public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Nuclear reactor status: DANGER - MELTDOWN IMMINENT");
+ }
+ };
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ PriorityDump.dump(mPriorityDumper, fd, pw, args);
+ }
+}
+
+ * </code></pre>
+ *
+ * <strong>Disclaimer</strong>: a real-life service should prioritize core status over donuts :-)
+ *
+ * <p>Then to invoke it:
+ *
+ * <pre><code>
+ *
+ $ adb shell dumpsys snpp
+ Donuts in the box: 1
+ Nuclear reactor status: DANGER - MELTDOWN IMMINENT
+
+ $ adb shell dumpsys snpp --dump_priority CRITICAL
+ Donuts in the box: 1
+
+ $ adb shell dumpsys snpp --dump_priority NORMAL
+ Nuclear reactor status: DANGER - MELTDOWN IMMINENT
+
+ * </code></pre>
+ *
+ *
+ *
+ * <p>To run the unit tests:
+ * <pre><code>
+ *
+ mmm -j32 frameworks/base/services/tests/servicestests/ && \
+ adb install -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
+ adb shell am instrument -e class "com.android.server.utils.PriorityDumpTest" \
+ -w "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
+
+ * </code></pre>
+ *
+ *
+ * @hide
+ */
+public final class PriorityDump {
+
+ public static final String PRIORITY_ARG = "--dump_priority";
+
+ private PriorityDump() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Parses {@code} and call the proper {@link PriorityDumper} method when the first argument is
+ * {@code --dump_priority}, stripping the priority and its type.
+ * <p>
+ * For example, if called as {@code --dump_priority HIGH arg1 arg2 arg3}, it will call
+ * <code>dumper.dumpHigh(fd, pw, {"arg1", "arg2", "arg3"}) </code>
+ * <p>
+ * If the {@code --dump_priority} is not set, it calls
+ * {@link PriorityDumper#dump(FileDescriptor, PrintWriter, String[])} passing the whole
+ * {@code args} instead.
+ */
+ public static void dump(PriorityDumper dumper, FileDescriptor fd, PrintWriter pw,
+ String[] args) {
+ if (args != null && args.length >= 2 && args[0].equals(PRIORITY_ARG)) {
+ final String priority = args[1];
+ switch (priority) {
+ case "CRITICAL": {
+ dumper.dumpCritical(fd, pw, getStrippedArgs(args));
+ return;
+ }
+ case "HIGH": {
+ dumper.dumpHigh(fd, pw, getStrippedArgs(args));
+ return;
+ }
+ case "NORMAL": {
+ dumper.dumpNormal(fd, pw, getStrippedArgs(args));
+ return;
+ }
+ }
+ }
+ dumper.dump(fd, pw, args);
+ }
+
+ /**
+ * Gets an array without the {@code --dump_priority PRIORITY} prefix.
+ */
+ private static String[] getStrippedArgs(String[] args) {
+ final String[] stripped = new String[args.length - 2];
+ System.arraycopy(args, 2, stripped, 0, stripped.length);
+ return stripped;
+ }
+
+ /**
+ * Helper for {@link android.os.Binder#dump(java.io.FileDescriptor, String[])} that supports the
+ * {@link #PRIORITY_ARG} argument.
+ *
+ * @hide
+ */
+ public static interface PriorityDumper {
+
+ /**
+ * Dumps only the critical section.
+ */
+ @SuppressWarnings("unused")
+ default void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) {
+ }
+
+ /**
+ * Dumps only the high-priority section.
+ */
+ @SuppressWarnings("unused")
+ default void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args) {
+ }
+
+ /**
+ * Dumps only the normal section.
+ */
+ @SuppressWarnings("unused")
+ default void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ }
+
+ /**
+ * Dumps all sections.
+ * <p>
+ * This method is called when
+ * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[])} is
+ * called without priority arguments. By default, it calls the 3 {@code dumpTYPE} methods,
+ * so sub-classes just need to implement the priority types they support.
+ */
+ @SuppressWarnings("unused")
+ default void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ dumpCritical(fd, pw, args);
+ dumpHigh(fd, pw, args);
+ dumpNormal(fd, pw, args);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9b22760..7439f535 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1479,7 +1479,7 @@
lockWP.cropHint.set(sysWP.cropHint);
lockWP.width = sysWP.width;
lockWP.height = sysWP.height;
- lockWP.allowBackup = false;
+ lockWP.allowBackup = sysWP.allowBackup;
// Migrate the bitmap files outright; no need to copy
try {
diff --git a/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java b/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
new file mode 100644
index 0000000..d378b7c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/PriorityDumpTest.java
@@ -0,0 +1,210 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import static com.android.server.utils.PriorityDump.dump;
+
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import com.android.server.utils.PriorityDump.PriorityDumper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class PriorityDumpTest {
+
+ private static final String[] EMPTY_ARGS = {};
+
+ @Mock
+ private PriorityDumper mDumper;
+ @Mock
+ private PrintWriter mPw;
+
+ private final FileDescriptor mFd = FileDescriptor.err;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testNullArgs() {
+ dump(mDumper, mFd, mPw, null);
+ verify(mDumper).dump(same(mFd), same(mPw), eq(null));
+ }
+
+ @Test
+ public void testNoArgs() {
+ dump(mDumper, mFd, mPw, EMPTY_ARGS);
+ verify(mDumper).dump(same(mFd), same(mPw), same(EMPTY_ARGS));
+ }
+
+ @Test
+ public void testNonPriorityArgs() {
+ final String[] args = {
+ "--dumb_priority"
+ };
+ dump(mDumper, mFd, mPw, args);
+ verify(mDumper).dump(same(mFd), same(mPw), same(args));
+ }
+
+ @Test
+ public void testMissingPriority() {
+ final String[] args = {
+ "--dump_priority"
+ };
+ dump(mDumper, mFd, mPw, args);
+ verify(mDumper).dump(same(mFd), same(mPw), same(args));
+ }
+
+ @Test
+ public void testInvalidPriorityNoExtraArgs() {
+ final String[] args = {
+ "--dump_priority", "SUPER_HIGH"
+ };
+ dump(mDumper, mFd, mPw, args);
+ verify(mDumper).dump(same(mFd), same(mPw), same(args));
+ }
+
+ @Test
+ public void testInvalidPriorityExtraArgs() {
+ final String[] args = {
+ "--dump_priority", "SUPER_HIGH", "--high", "--five"
+ };
+ dump(mDumper, mFd, mPw, args);
+ verify(mDumper).dump(same(mFd), same(mPw), same(args));
+ }
+
+ @Test
+ public void testNoPriorityCallsAllMethods() {
+ final String[] args = {
+ "1", "2", "3"
+ };
+
+ // Cannot use mDumper here because it would mock the dump() call.
+ final FakeDumper fakeDumper = new FakeDumper();
+
+ dump(fakeDumper, mFd, mPw, args);
+
+ assertSame(mFd, fakeDumper.criticalFd);
+ assertSame(mPw, fakeDumper.criticalPw);
+ assertSame(args, fakeDumper.criticalArgs);
+ assertSame(mFd, fakeDumper.highFd);
+ assertSame(mPw, fakeDumper.highPw);
+ assertSame(args, fakeDumper.highArgs);
+ assertSame(mFd, fakeDumper.normalFd);
+ assertSame(mPw, fakeDumper.normalPw);
+ assertSame(args, fakeDumper.normalArgs);
+ }
+
+ @Test
+ public void testCriticalNoExtraArgs() {
+ dump(mDumper, mFd, mPw, new String[] {
+ "--dump_priority", "CRITICAL"
+ });
+ verify(mDumper).dumpCritical(same(mFd), same(mPw), eq(EMPTY_ARGS));
+ }
+
+ @Test
+ public void testCriticalExtraArgs() {
+ dump(mDumper, mFd, mPw, new String[] {
+ "--dump_priority", "CRITICAL", "--high", "--five"
+ });
+ verify(mDumper).dumpCritical(same(mFd), same(mPw), eq(new String[] {
+ "--high", "--five"
+ }));
+ }
+
+ @Test
+ public void testHighNoExtraArgs() {
+ dump(mDumper, mFd, mPw, new String[] {
+ "--dump_priority", "HIGH"
+ });
+ verify(mDumper).dumpHigh(same(mFd), same(mPw), eq(EMPTY_ARGS));
+ }
+
+ @Test
+ public void testHighExtraArgs() {
+ dump(mDumper, mFd, mPw, new String[] {
+ "--dump_priority", "HIGH", "--high", "--five"
+ });
+ verify(mDumper).dumpHigh(same(mFd), same(mPw), eq(new String[] {
+ "--high", "--five"
+ }));
+ }
+
+ @Test
+ public void testNormalNoExtraArgs() {
+ dump(mDumper, mFd, mPw, new String[] {
+ "--dump_priority", "NORMAL"
+ });
+ verify(mDumper).dumpNormal(same(mFd), same(mPw), eq(EMPTY_ARGS));
+ }
+
+ @Test
+ public void testNormalExtraArgs() {
+ dump(mDumper, mFd, mPw, new String[] {
+ "--dump_priority", "NORMAL", "--high", "--five"
+ });
+ verify(mDumper).dumpNormal(same(mFd), same(mPw), eq(new String[] {
+ "--high", "--five"
+ }));
+ }
+
+ private final class FakeDumper implements PriorityDumper {
+
+ String[] criticalArgs, highArgs, normalArgs;
+ FileDescriptor criticalFd, highFd, normalFd;
+ PrintWriter criticalPw, highPw, normalPw;
+
+ @Override
+ public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) {
+ criticalFd = fd;
+ criticalPw = pw;
+ criticalArgs = args;
+ }
+
+ @Override
+ public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args) {
+ highFd = fd;
+ highPw = pw;
+ highArgs = args;
+ }
+
+ @Override
+ public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ normalFd = fd;
+ normalPw = pw;
+ normalArgs = args;
+ }
+ }
+}