Merge "WorkSource can now track package names."
diff --git a/api/current.txt b/api/current.txt
index 72e7601..1cf14aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29106,7 +29106,7 @@
method public void setRelativeScrollPosition(int, int);
method public deprecated void setRemoteAdapter(int, int, android.content.Intent);
method public void setRemoteAdapter(int, android.content.Intent);
- method public void setRemoteAdapter(int, java.util.ArrayList<android.widget.RemoteViews>);
+ method public void setRemoteAdapter(int, java.util.ArrayList<android.widget.RemoteViews>, int);
method public void setScrollPosition(int, int);
method public void setShort(int, java.lang.String, short);
method public void setString(int, java.lang.String, java.lang.String);
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 2f300de..a3f70da 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -18,6 +18,7 @@
import android.os.Parcelable;
import android.os.Parcel;
+import android.text.TextUtils;
import android.util.Log;
import java.net.InetAddress;
@@ -211,11 +212,13 @@
}
public boolean addDns(String addrString) {
- try {
- linkProperties.addDns(NetworkUtils.numericToInetAddress(addrString));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "addDns failed with addrString " + addrString);
- return true;
+ if (TextUtils.isEmpty(addrString) == false) {
+ try {
+ linkProperties.addDns(NetworkUtils.numericToInetAddress(addrString));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "addDns failed with addrString " + addrString);
+ return true;
+ }
}
return false;
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 788ab74..d69fef0 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2087,6 +2087,10 @@
+ "Parcelable.Creator object called "
+ " CREATOR on class " + name);
}
+ catch (NullPointerException e) {
+ throw new BadParcelableException("Parcelable protocol requires "
+ + "the CREATOR object to be static on class " + name);
+ }
if (creator == null) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "Parcelable.Creator object called "
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4dbc4b4..441214c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2610,6 +2610,7 @@
MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED);
MOVED_TO_GLOBAL.add(Settings.Global.ASSISTED_GPS_ENABLED);
MOVED_TO_GLOBAL.add(Settings.Global.BLUETOOTH_ON);
+ MOVED_TO_GLOBAL.add(Settings.Global.BUGREPORT_IN_POWER_MENU);
MOVED_TO_GLOBAL.add(Settings.Global.CDMA_CELL_BROADCAST_SMS);
MOVED_TO_GLOBAL.add(Settings.Global.CDMA_ROAMING_MODE);
MOVED_TO_GLOBAL.add(Settings.Global.CDMA_SUBSCRIPTION_MODE);
@@ -3081,8 +3082,10 @@
/**
* When the user has enable the option to have a "bug report" command
* in the power menu.
+ * @deprecated Use {@link android.provider.Settings.Global#BUGREPORT_IN_POWER_MENU} instead
* @hide
*/
+ @Deprecated
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
@@ -4037,7 +4040,7 @@
* @hide
*/
public static final String[] SETTINGS_TO_BACKUP = {
- BUGREPORT_IN_POWER_MENU,
+ BUGREPORT_IN_POWER_MENU, // moved to global
ALLOW_MOCK_LOCATION,
PARENTAL_CONTROL_ENABLED,
PARENTAL_CONTROL_REDIRECT_URL,
@@ -4316,6 +4319,13 @@
public static final String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
/**
+ * When the user has enable the option to have a "bug report" command
+ * in the power menu.
+ * @hide
+ */
+ public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
+
+ /**
* Whether ADB is enabled.
*/
public static final String ADB_ENABLED = "adb_enabled";
@@ -5359,6 +5369,7 @@
* @hide
*/
public static final String[] SETTINGS_TO_BACKUP = {
+ BUGREPORT_IN_POWER_MENU,
STAY_ON_WHILE_PLUGGED_IN,
MODE_RINGER,
AUTO_TIME,
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 3c984b5..352cb22 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -452,6 +452,16 @@
* @hide
*/
public static boolean hasSeconds(CharSequence inFormat) {
+ return hasDesignator(inFormat, SECONDS);
+ }
+
+ /**
+ * Test if a format string contains the given designator. Always returns
+ * {@code false} if the input format is {@code null}.
+ *
+ * @hide
+ */
+ public static boolean hasDesignator(CharSequence inFormat, char designator) {
if (inFormat == null) return false;
final int length = inFormat.length();
@@ -465,7 +475,7 @@
if (c == QUOTE) {
count = skipQuotedText(inFormat, i, length);
- } else if (c == SECONDS) {
+ } else if (c == designator) {
return true;
}
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 86d04b6..7ecdcbe 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -43,6 +43,7 @@
import java.io.File;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
import static javax.microedition.khronos.egl.EGL10.*;
@@ -88,10 +89,12 @@
*
* Possible values:
* "true", to enable profiling
- * "visual", to enable profiling and visualize the results on screen
+ * "visual_bars", to enable profiling and visualize the results on screen
+ * "visual_lines", to enable profiling and visualize the results on screen
* "false", to disable profiling
*
- * @see #PROFILE_PROPERTY_VISUALIZE
+ * @see #PROFILE_PROPERTY_VISUALIZE_BARS
+ * @see #PROFILE_PROPERTY_VISUALIZE_LINES
*
* @hide
*/
@@ -99,11 +102,19 @@
/**
* Value for {@link #PROFILE_PROPERTY}. When the property is set to this
- * value, profiling data will be visualized on screen.
+ * value, profiling data will be visualized on screen as a bar chart.
*
* @hide
*/
- public static final String PROFILE_PROPERTY_VISUALIZE = "visual";
+ public static final String PROFILE_PROPERTY_VISUALIZE_BARS = "visual_bars";
+
+ /**
+ * Value for {@link #PROFILE_PROPERTY}. When the property is set to this
+ * value, profiling data will be visualized on screen as a line chart.
+ *
+ * @hide
+ */
+ public static final String PROFILE_PROPERTY_VISUALIZE_LINES = "visual_lines";
/**
* System property used to specify the number of frames to be used
@@ -619,6 +630,100 @@
mRequested = requested;
}
+ /**
+ * Describes a series of frames that should be drawn on screen as a graph.
+ * Each frame is composed of 1 or more elements.
+ */
+ abstract class GraphDataProvider {
+ /**
+ * Draws the graph as bars. Frame elements are stacked on top of
+ * each other.
+ */
+ public static final int GRAPH_TYPE_BARS = 0;
+ /**
+ * Draws the graph as lines. The number of series drawn corresponds
+ * to the number of elements.
+ */
+ public static final int GRAPH_TYPE_LINES = 1;
+
+ /**
+ * Returns the type of graph to render.
+ *
+ * @return {@link #GRAPH_TYPE_BARS} or {@link #GRAPH_TYPE_LINES}
+ */
+ abstract int getGraphType();
+
+ /**
+ * This method is invoked before the graph is drawn. This method
+ * can be used to compute sizes, etc.
+ *
+ * @param metrics The display metrics
+ */
+ abstract void prepare(DisplayMetrics metrics);
+
+ /**
+ * @return The size in pixels of a vertical unit.
+ */
+ abstract int getVerticalUnitSize();
+
+ /**
+ * @return The size in pixels of a horizontal unit.
+ */
+ abstract int getHorizontalUnitSize();
+
+ /**
+ * @return The size in pixels of the margin between horizontal units.
+ */
+ abstract int getHorizontaUnitMargin();
+
+ /**
+ * An optional threshold value.
+ *
+ * @return A value >= 0 to draw the threshold, a negative value
+ * to ignore it.
+ */
+ abstract float getThreshold();
+
+ /**
+ * The data to draw in the graph. The number of elements in the
+ * array must be at least {@link #getFrameCount()} * {@link #getElementCount()}.
+ * If a value is negative the following values will be ignored.
+ */
+ abstract float[] getData();
+
+ /**
+ * Returns the number of frames to render in the graph.
+ */
+ abstract int getFrameCount();
+
+ /**
+ * Returns the number of elements in each frame. This directly affects
+ * the number of series drawn in the graph.
+ */
+ abstract int getElementCount();
+
+ /**
+ * Returns the current frame, if any. If the returned value is negative
+ * the current frame is ignored.
+ */
+ abstract int getCurrentFrame();
+
+ /**
+ * Prepares the paint to draw the specified element (or series.)
+ */
+ abstract void setupGraphPaint(Paint paint, int elementIndex);
+
+ /**
+ * Prepares the paint to draw the threshold.
+ */
+ abstract void setupThresholdPaint(Paint paint);
+
+ /**
+ * Prepares the paint to draw the current frame indicator.
+ */
+ abstract void setupCurrentFramePaint(Paint paint);
+ }
+
@SuppressWarnings({"deprecation"})
static abstract class GlRenderer extends HardwareRenderer {
static final int SURFACE_STATE_ERROR = 0;
@@ -627,6 +732,14 @@
static final int FUNCTOR_PROCESS_DELAY = 4;
+ private static final int PROFILE_DRAW_MARGIN = 0;
+ private static final int PROFILE_DRAW_WIDTH = 3;
+ private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
+ private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
+ private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
+ private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
+ private static final int PROFILE_DRAW_DP_PER_MS = 7;
+
static EGL10 sEgl;
static EGLDisplay sEglDisplay;
static EGLConfig sEglConfig;
@@ -660,12 +773,13 @@
boolean mUpdateDirtyRegions;
boolean mProfileEnabled;
- boolean mProfileVisualizerEnabled;
+ int mProfileVisualizerType = -1;
float[] mProfileData;
ReentrantLock mProfileLock;
int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
- float[][] mProfileRects;
+ GraphDataProvider mDebugDataProvider;
+ float[][] mProfileShapes;
Paint mProfilePaint;
boolean mDebugDirtyRegions;
@@ -688,20 +802,32 @@
loadSystemProperties(null);
}
+ private static final String[] VISUALIZERS = {
+ PROFILE_PROPERTY_VISUALIZE_BARS,
+ PROFILE_PROPERTY_VISUALIZE_LINES
+ };
+
@Override
boolean loadSystemProperties(Surface surface) {
boolean value;
boolean changed = false;
String profiling = SystemProperties.get(PROFILE_PROPERTY);
- value = PROFILE_PROPERTY_VISUALIZE.equalsIgnoreCase(profiling);
+ int graphType = Arrays.binarySearch(VISUALIZERS, profiling);
+ value = graphType >= 0;
- if (value != mProfileVisualizerEnabled) {
+ if (graphType != mProfileVisualizerType) {
changed = true;
- mProfileVisualizerEnabled = value;
+ mProfileVisualizerType = graphType;
- mProfileRects = null;
+ mProfileShapes = null;
mProfilePaint = null;
+
+ if (value) {
+ mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
+ } else {
+ mDebugDataProvider = null;
+ }
}
// If on-screen profiling is not enabled, we need to check whether
@@ -728,6 +854,7 @@
} else {
mProfileData = null;
mProfileLock = null;
+ mProfileVisualizerType = -1;
}
mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
@@ -1283,7 +1410,7 @@
}
}
- if (mProfileEnabled && mProfileVisualizerEnabled) dirty = null;
+ if (mDebugDataProvider != null) dirty = null;
return dirty;
}
@@ -1459,20 +1586,102 @@
}
return SURFACE_STATE_SUCCESS;
}
+
+ private static int dpToPx(int dp, float density) {
+ return (int) (dp * density + 0.5f);
+ }
+
+ class DrawPerformanceDataProvider extends GraphDataProvider {
+ private final int mGraphType;
+
+ private int mVerticalUnit;
+ private int mHorizontalUnit;
+ private int mHorizontalMargin;
+ private int mThresholdStroke;
+
+ DrawPerformanceDataProvider(int graphType) {
+ mGraphType = graphType;
+ }
+
+ @Override
+ void prepare(DisplayMetrics metrics) {
+ final float density = metrics.density;
+
+ mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
+ mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
+ mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
+ mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
+ }
+
+ @Override
+ int getGraphType() {
+ return mGraphType;
+ }
+
+ @Override
+ int getVerticalUnitSize() {
+ return mVerticalUnit;
+ }
+
+ @Override
+ int getHorizontalUnitSize() {
+ return mHorizontalUnit;
+ }
+
+ @Override
+ int getHorizontaUnitMargin() {
+ return mHorizontalMargin;
+ }
+
+ @Override
+ float[] getData() {
+ return mProfileData;
+ }
+
+ @Override
+ float getThreshold() {
+ return 16;
+ }
+
+ @Override
+ int getFrameCount() {
+ return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ int getElementCount() {
+ return PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ int getCurrentFrame() {
+ return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ void setupGraphPaint(Paint paint, int elementIndex) {
+ paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
+ if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
+ }
+
+ @Override
+ void setupThresholdPaint(Paint paint) {
+ paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
+ paint.setStrokeWidth(mThresholdStroke);
+ }
+
+ @Override
+ void setupCurrentFramePaint(Paint paint) {
+ paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
+ if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
+ }
+ }
}
/**
* Hardware renderer using OpenGL ES 2.0.
*/
static class Gl20Renderer extends GlRenderer {
- private static final int PROFILE_DRAW_MARGIN = 0;
- private static final int PROFILE_DRAW_WIDTH = 3;
- private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
- private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
- private static final int PROFILE_DRAW_DP_PER_MS = 7;
-
private GLES20Canvas mGlCanvas;
private DisplayMetrics mDisplayMetrics;
@@ -1581,101 +1790,149 @@
@Override
void drawProfileData(View.AttachInfo attachInfo) {
- if (mProfileEnabled && mProfileVisualizerEnabled) {
- initProfileDrawData(attachInfo);
+ if (mDebugDataProvider != null) {
+ final GraphDataProvider provider = mDebugDataProvider;
+ initProfileDrawData(attachInfo, provider);
- final int pxPerMs = (int) (PROFILE_DRAW_DP_PER_MS * mDisplayMetrics.density + 0.5f);
- final int margin = (int) (PROFILE_DRAW_MARGIN * mDisplayMetrics.density + 0.5f);
- final int width = (int) (PROFILE_DRAW_WIDTH * mDisplayMetrics.density + 0.5f);
+ final int height = provider.getVerticalUnitSize();
+ final int margin = provider.getHorizontaUnitMargin();
+ final int width = provider.getHorizontalUnitSize();
int x = 0;
int count = 0;
int current = 0;
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- if (mProfileData[i] < 0.0f) break;
+ final float[] data = provider.getData();
+ final int elementCount = provider.getElementCount();
+ final int graphType = provider.getGraphType();
+
+ int totalCount = provider.getFrameCount() * elementCount;
+ if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
+ totalCount -= elementCount;
+ }
+
+ for (int i = 0; i < totalCount; i += elementCount) {
+ if (data[i] < 0.0f) break;
int index = count * 4;
- if (i == mProfileCurrentFrame) current = index;
+ if (i == provider.getCurrentFrame() * elementCount) current = index;
x += margin;
int x2 = x + width;
int y2 = mHeight;
- int y1 = (int) (y2 - mProfileData[i] * pxPerMs);
+ int y1 = (int) (y2 - data[i] * height);
- float[] r = mProfileRects[0];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS: {
+ for (int j = 0; j < elementCount; j++) {
+ //noinspection MismatchedReadAndWriteOfArray
+ final float[] r = mProfileShapes[j];
+ r[index] = x;
+ r[index + 1] = y1;
+ r[index + 2] = x2;
+ r[index + 3] = y2;
- y2 = y1;
- y1 = (int) (y2 - mProfileData[i + 1] * pxPerMs);
+ y2 = y1;
+ if (j < elementCount - 1) {
+ y1 = (int) (y2 - data[i + j + 1] * height);
+ }
+ }
+ } break;
+ case GraphDataProvider.GRAPH_TYPE_LINES: {
+ for (int j = 0; j < elementCount; j++) {
+ //noinspection MismatchedReadAndWriteOfArray
+ final float[] r = mProfileShapes[j];
+ r[index] = (x + x2) * 0.5f;
+ r[index + 1] = index == 0 ? y1 : r[index - 1];
+ r[index + 2] = r[index] + width;
+ r[index + 3] = y1;
- r = mProfileRects[1];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
+ y2 = y1;
+ if (j < elementCount - 1) {
+ y1 = (int) (y2 - data[i + j + 1] * height);
+ }
+ }
+ } break;
+ }
- y2 = y1;
- y1 = (int) (y2 - mProfileData[i + 2] * pxPerMs);
-
- r = mProfileRects[2];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
x += width;
-
count++;
}
+
x += margin;
- drawGraph(count);
- drawCurrentFrame(current);
- drawThreshold(x, pxPerMs);
+ drawGraph(graphType, count);
+ drawCurrentFrame(graphType, current);
+ drawThreshold(x, height);
}
}
- private void drawGraph(int count) {
- for (int i = 0; i < mProfileRects.length; i++) {
- mProfilePaint.setColor(PROFILE_DRAW_COLORS[i]);
- mGlCanvas.drawRects(mProfileRects[i], count, mProfilePaint);
- }
- }
-
- private void drawCurrentFrame(int index) {
- mProfilePaint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
- mGlCanvas.drawRect(mProfileRects[2][index], mProfileRects[2][index + 1],
- mProfileRects[2][index + 2], mProfileRects[0][index + 3], mProfilePaint);
- }
-
- private void drawThreshold(int x, int pxPerMs) {
- mProfilePaint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
- mProfilePaint.setStrokeWidth((int)
- (PROFILE_DRAW_THRESHOLD_STROKE_WIDTH * mDisplayMetrics.density + 0.5f));
- int y = mHeight - 16 * pxPerMs;
- mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
- mProfilePaint.setStrokeWidth(1.0f);
- }
-
- private void initProfileDrawData(View.AttachInfo attachInfo) {
- if (mProfileRects == null) {
- mProfileRects = new float[PROFILE_FRAME_DATA_COUNT][];
- for (int i = 0; i < mProfileRects.length; i++) {
- int count = mProfileData.length / PROFILE_FRAME_DATA_COUNT;
- mProfileRects[i] = new float[count * 4];
+ private void drawGraph(int graphType, int count) {
+ for (int i = 0; i < mProfileShapes.length; i++) {
+ mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS:
+ mGlCanvas.drawRects(mProfileShapes[i], count, mProfilePaint);
+ break;
+ case GraphDataProvider.GRAPH_TYPE_LINES:
+ mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
+ break;
}
+ }
+ }
+
+ private void drawCurrentFrame(int graphType, int index) {
+ if (index >= 0) {
+ mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS:
+ mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
+ mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
+ mProfilePaint);
+ break;
+ case GraphDataProvider.GRAPH_TYPE_LINES:
+ mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
+ mProfileShapes[2][index], mHeight, mProfilePaint);
+ break;
+ }
+ }
+ }
+
+ private void drawThreshold(int x, int height) {
+ float threshold = mDebugDataProvider.getThreshold();
+ if (threshold > 0.0f) {
+ mDebugDataProvider.setupThresholdPaint(mProfilePaint);
+ int y = (int) (mHeight - threshold * height);
+ mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
+ }
+ }
+
+ private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
+ if (mProfileShapes == null) {
+ final int elementCount = provider.getElementCount();
+ final int frameCount = provider.getFrameCount();
+
+ mProfileShapes = new float[elementCount][];
+ for (int i = 0; i < elementCount; i++) {
+ mProfileShapes[i] = new float[frameCount * 4];
+ }
+
mProfilePaint = new Paint();
}
+ mProfilePaint.reset();
+ if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
+ mProfilePaint.setAntiAlias(true);
+ }
+
if (mDisplayMetrics == null) {
mDisplayMetrics = new DisplayMetrics();
}
+
attachInfo.mDisplay.getMetrics(mDisplayMetrics);
+ provider.prepare(mDisplayMetrics);
}
@Override
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index d4e27bc..e711b94 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -337,6 +337,11 @@
listenToRingerMode();
}
+ public void setLayoutDirection(int layoutDirection) {
+ mPanel.setLayoutDirection(layoutDirection);
+ updateStates();
+ }
+
private void listenToRingerMode() {
final IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
@@ -460,6 +465,8 @@
private void updateSlider(StreamControl sc) {
sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
final boolean muted = isMuted(sc.streamType);
+ // Force reloading the image resource
+ sc.icon.setImageDrawable(null);
sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
if (sc.streamType == AudioManager.STREAM_RING &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 441b7b0..1abea2b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1865,9 +1865,6 @@
}
private static synchronized WebViewFactoryProvider getFactory() {
- // For now the main purpose of this function (and the factory abstration) is to keep
- // us honest and minimize usage of WebViewClassic internals when binding the proxy.
- checkThread();
return WebViewFactory.getProvider();
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 57e3c8b..79fc51e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -487,13 +487,15 @@
}
private class SetRemoteViewsAdapterList extends Action {
- public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list) {
+ public SetRemoteViewsAdapterList(int id, ArrayList<RemoteViews> list, int viewTypeCount) {
this.viewId = id;
this.list = list;
+ this.viewTypeCount = viewTypeCount;
}
public SetRemoteViewsAdapterList(Parcel parcel) {
viewId = parcel.readInt();
+ viewTypeCount = parcel.readInt();
int count = parcel.readInt();
list = new ArrayList<RemoteViews>();
@@ -506,6 +508,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(TAG);
dest.writeInt(viewId);
+ dest.writeInt(viewTypeCount);
if (list == null || list.size() == 0) {
dest.writeInt(0);
@@ -540,18 +543,18 @@
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
Adapter a = v.getAdapter();
- if (a instanceof RemoteViewsListAdapter) {
+ if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
- v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list));
+ v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
}
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
Adapter a = v.getAdapter();
- if (a instanceof RemoteViewsListAdapter) {
+ if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) {
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
- v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list));
+ v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount));
}
}
}
@@ -560,6 +563,7 @@
return "SetRemoteViewsAdapterList";
}
+ int viewTypeCount;
ArrayList<RemoteViews> list;
public final static int TAG = 15;
}
@@ -2099,9 +2103,13 @@
*
* @param viewId The id of the {@link AdapterView}
* @param list The list of RemoteViews which will populate the view specified by viewId.
+ * @param viewTypeCount The maximum number of unique layout id's used to construct the list of
+ * RemoteViews. This count cannot change during the life-cycle of a given widget, so this
+ * parameter should account for the maximum possible number of types that may appear in the
+ * See {@link Adapter#getViewTypeCount()}.
*/
- public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list) {
- addAction(new SetRemoteViewsAdapterList(viewId, list));
+ public void setRemoteAdapter(int viewId, ArrayList<RemoteViews> list, int viewTypeCount) {
+ addAction(new SetRemoteViewsAdapterList(viewId, list, viewTypeCount));
}
/**
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index 9598771..e490458 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -30,10 +30,13 @@
private Context mContext;
private ArrayList<RemoteViews> mRemoteViewsList;
private ArrayList<Integer> mViewTypes = new ArrayList<Integer>();
+ private int mViewTypeCount;
- public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews) {
+ public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews,
+ int viewTypeCount) {
mContext = context;
mRemoteViewsList = remoteViews;
+ mViewTypeCount = viewTypeCount;
init();
}
@@ -52,6 +55,11 @@
mViewTypes.add(rv.getLayoutId());
}
}
+
+ if (mViewTypes.size() > mViewTypeCount || mViewTypeCount < 1) {
+ throw new RuntimeException("Invalid view type count -- view type count must be >= 1" +
+ "and must be as large as the total number of distinct view types");
+ }
}
@Override
@@ -77,6 +85,7 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (position < getCount()) {
RemoteViews rv = mRemoteViewsList.get(position);
+ rv.setIsWidgetCollectionChild(true);
View v;
if (convertView != null && rv != null &&
convertView.getId() == rv.getLayoutId()) {
@@ -102,7 +111,7 @@
}
public int getViewTypeCount() {
- return mViewTypes.size();
+ return mViewTypeCount;
}
@Override
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 290d9b5..5bf21c0 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -386,6 +386,16 @@
}
/**
+ * Returns the current format string. Always valid after constructor has
+ * finished, and will never be {@code null}.
+ *
+ * @hide
+ */
+ public CharSequence getFormat() {
+ return mFormat;
+ }
+
+ /**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
* depending on whether the user has selected 24-hour format.
*
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 296add3..f5f22b2 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -28,23 +28,21 @@
int ifc_disable(const char *ifname);
int ifc_reset_connections(const char *ifname, int reset_mask);
-int dhcp_do_request(const char *ifname,
+int dhcp_do_request(const char * const ifname,
const char *ipaddr,
const char *gateway,
uint32_t *prefixLength,
- const char *dns1,
- const char *dns2,
+ const char *dns[],
const char *server,
uint32_t *lease,
const char *vendorInfo,
const char *domains);
-int dhcp_do_request_renew(const char *ifname,
+int dhcp_do_request_renew(const char * const ifname,
const char *ipaddr,
const char *gateway,
uint32_t *prefixLength,
- const char *dns1,
- const char *dns2,
+ const char *dns[],
const char *server,
uint32_t *lease,
const char *vendorInfo,
@@ -120,6 +118,9 @@
char gateway[PROPERTY_VALUE_MAX];
char dns1[PROPERTY_VALUE_MAX];
char dns2[PROPERTY_VALUE_MAX];
+ char dns3[PROPERTY_VALUE_MAX];
+ char dns4[PROPERTY_VALUE_MAX];
+ const char *dns[5] = {dns1, dns2, dns3, dns4, NULL};
char server[PROPERTY_VALUE_MAX];
uint32_t lease;
char vendorInfo[PROPERTY_VALUE_MAX];
@@ -130,10 +131,10 @@
if (renew) {
result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
- dns1, dns2, server, &lease, vendorInfo, domains);
+ dns, server, &lease, vendorInfo, domains);
} else {
result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
- dns1, dns2, server, &lease, vendorInfo, domains);
+ dns, server, &lease, vendorInfo, domains);
}
env->ReleaseStringUTFChars(ifname, nameStr);
if (result == 0) {
@@ -168,6 +169,15 @@
result = env->CallBooleanMethod(dhcpResults,
dhcpResultsFieldIds.addDns, env->NewStringUTF(dns2));
+
+ if (result == 0) {
+ result = env->CallBooleanMethod(dhcpResults,
+ dhcpResultsFieldIds.addDns, env->NewStringUTF(dns3));
+ if (result == 0) {
+ result = env->CallBooleanMethod(dhcpResults,
+ dhcpResultsFieldIds.addDns, env->NewStringUTF(dns4));
+ }
+ }
}
if (result == 0) {
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification.png
new file mode 100644
index 0000000..62ef692
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification_mute.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification_mute.png
new file mode 100644
index 0000000..40123e3
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_notification_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_phone.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_phone.png
new file mode 100644
index 0000000..968f5ee
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_phone.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif.png
new file mode 100644
index 0000000..e5f0dcf
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_mute.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_mute.png
new file mode 100644
index 0000000..371e32f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_vibrate.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_vibrate.png
new file mode 100644
index 0000000..e05e8f5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_ring_notif_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol.png
new file mode 100644
index 0000000..81a8422
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol_mute.png b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol_mute.png
new file mode 100644
index 0000000..371e32f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_audio_vol_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..7b1eee6
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png
new file mode 100644
index 0000000..aedf9b1
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png
new file mode 100644
index 0000000..a7dc29d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..c093420
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png
new file mode 100644
index 0000000..1f21884
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png
new file mode 100644
index 0000000..d726b7a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification.png
new file mode 100644
index 0000000..d9843e0
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification_mute.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification_mute.png
new file mode 100644
index 0000000..2159cab
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_notification_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_phone.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_phone.png
new file mode 100644
index 0000000..b5af351
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_phone.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif.png
new file mode 100644
index 0000000..6341be6
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_mute.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_mute.png
new file mode 100644
index 0000000..b4c3a54
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_vibrate.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_vibrate.png
new file mode 100644
index 0000000..835773e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_ring_notif_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol.png
new file mode 100644
index 0000000..947d1f9
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol_mute.png b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol_mute.png
new file mode 100644
index 0000000..b4c3a54
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_audio_vol_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..cba5500
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png
new file mode 100644
index 0000000..0335dec
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png
new file mode 100644
index 0000000..265c421
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification.png
new file mode 100644
index 0000000..43aedea
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification_mute.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification_mute.png
new file mode 100644
index 0000000..4e87f77
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_notification_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_phone.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_phone.png
new file mode 100644
index 0000000..1066d03
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_phone.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif.png
new file mode 100644
index 0000000..daf9213
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_mute.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_mute.png
new file mode 100644
index 0000000..83d3bdd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_vibrate.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_vibrate.png
new file mode 100644
index 0000000..4de95aa
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_ring_notif_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol.png
new file mode 100644
index 0000000..8132926
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol_mute.png b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol_mute.png
new file mode 100644
index 0000000..83d3bdd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_audio_vol_mute.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..6820a23
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png b/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png
new file mode 100644
index 0000000..c8690bd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png b/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png
new file mode 100644
index 0000000..e342556
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
new file mode 100644
index 0000000..5f36cfd
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class DateFormatTest extends TestCase {
+ @SmallTest
+ public void testHasDesignator() throws Exception {
+ assertTrue(DateFormat.hasDesignator("hh:mm:ss", DateFormat.MINUTE));
+ assertTrue(DateFormat.hasDesignator("myyyy", DateFormat.MINUTE));
+ assertTrue(DateFormat.hasDesignator("mmm", DateFormat.MINUTE));
+
+ assertFalse(DateFormat.hasDesignator("hh:MM:ss", DateFormat.MINUTE));
+ }
+
+ @SmallTest
+ public void testHasDesignatorEscaped() throws Exception {
+ assertTrue(DateFormat.hasDesignator("hh:mm 'LOL'", DateFormat.MINUTE));
+
+ assertFalse(DateFormat.hasDesignator("hh:mm 'yyyy'", DateFormat.YEAR));
+ }
+}
diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip
index e48bfd3..882ce03 100644
--- a/docs/downloads/training/BitmapFun.zip
+++ b/docs/downloads/training/BitmapFun.zip
Binary files differ
diff --git a/docs/html/distribute/googleplay/promote/badge-files.jd b/docs/html/distribute/googleplay/promote/badge-files.jd
index 92001ed..ede1e21 100644
--- a/docs/html/distribute/googleplay/promote/badge-files.jd
+++ b/docs/html/distribute/googleplay/promote/badge-files.jd
@@ -21,9 +21,9 @@
<a href="{@docRoot}downloads/brand/en_generic_rgb_wo.ai">English (English)</a><br/>
<a href="{@docRoot}downloads/brand/af_generic_rgb_wo.ai">Afrikaans (Afrikaans)</a><br/>
-
+<!--
<a href="{@docRoot}downloads/brand/ar_generic_rgb_wo.ai">العربية (Arabic)</a><br/>
-
+-->
<a href="{@docRoot}downloads/brand/be_generic_rgb_wo.ai">Беларуская (Belarusian)</a><br/>
<a href="{@docRoot}downloads/brand/bg_generic_rgb_wo.ai">български (Bulgarian)</a><br/>
@@ -61,9 +61,9 @@
<a href="{@docRoot}downloads/brand/de_generic_rgb_wo.ai">Deutsch (German)</a><br/>
<a href="{@docRoot}downloads/brand/el_generic_rgb_wo.ai">Ελληνικά (Greek)</a><br/>
-
+<!--
<a href="{@docRoot}downloads/brand/iw-he_generic_rgb_wo.ai">עברית (Hebrew)</a><br/>
-
+-->
<a href="{@docRoot}downloads/brand/hu_generic_rgb_wo.ai">Magyar (Hungarian)</a><br/>
<a href="{@docRoot}downloads/brand/id-in_generic_rgb_wo.ai">Bahasa Indonesia (Indonesian)</a><br/>
diff --git a/docs/html/distribute/googleplay/promote/badges.jd b/docs/html/distribute/googleplay/promote/badges.jd
index 0027f59..081c8f7 100644
--- a/docs/html/distribute/googleplay/promote/badges.jd
+++ b/docs/html/distribute/googleplay/promote/badges.jd
@@ -199,8 +199,10 @@
onchange="changeBadgeLang()">
<option title="Afrikaans"
value="af">Afrikaans</option>
+<!--
<option title="Arabic"
value="ar">العربية</option>
+-->
<option title="Belarusian"
value="be">Беларуская</option>
<option title="Bulgarian"
@@ -237,8 +239,10 @@
value="el">Ελληνικά</option>
<option title="English"
value="en" selected="true">English</option>
+<!--
<option title="Hebrew"
value="iw-he">עברית</option>
+-->
<option title="Hungarian"
value="hu">Magyar</option>
<option title="Indonesian"
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
index 2a333cc..417ec5b 100644
--- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -101,19 +101,20 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
...
- // Get memory class of this device, exceeding this amount will throw an
- // OutOfMemory exception.
- final int memClass = ((ActivityManager) context.getSystemService(
- Context.ACTIVITY_SERVICE)).getMemoryClass();
+ // Get max available VM memory, exceeding this amount will throw an
+ // OutOfMemory exception. Stored in kilobytes as LruCache takes an
+ // int in its constructor.
+ final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
- final int cacheSize = 1024 * 1024 * memClass / 8;
+ final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
- // The cache size will be measured in bytes rather than number of items.
- return bitmap.getByteCount();
+ // The cache size will be measured in kilobytes rather than
+ // number of items.
+ return bitmap.getByteCount() / 1024;
}
};
...
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
index c0a5709..283f272 100644
--- a/docs/html/training/displaying-bitmaps/load-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -110,12 +110,17 @@
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
- if (width > height) {
- inSampleSize = Math.round((float)height / (float)reqHeight);
- } else {
- inSampleSize = Math.round((float)width / (float)reqWidth);
- }
+
+ // Calculate ratios of height and width to requested height and width
+ final int heightRatio = Math.round((float) height / (float) reqHeight);
+ final int widthRatio = Math.round((float) width / (float) reqWidth);
+
+ // Choose the smallest ratio as inSampleSize value, this will guarantee
+ // a final image with both dimensions larger than or equal to the
+ // requested height and width.
+ inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
+
return inSampleSize;
}
</pre>
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 9aac0e6..ed2a8da 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -5708,17 +5708,19 @@
private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
synchronized(mRCStack) {
// The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, and we can stop iterating over the stack entries once the matching
- // ID has been found.
- // FIXME optimize by traversing stack from top to bottom, the matching ID is more likely
- // at the top of the stack
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = stackIterator.next();
- if (rcse.mRccId == rccId) {
- rcse.mRemoteVolumeObs = rvo;
- break;
+ // with this RCC ID, but the matching ID is more likely at the top of the stack, so
+ // start iterating from the top.
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ if (rcse.mRccId == rccId) {
+ rcse.mRemoteVolumeObs = rvo;
+ break;
+ }
}
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
}
}
}
@@ -5813,18 +5815,20 @@
IRemoteVolumeObserver rvo = null;
synchronized (mRCStack) {
// The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, and we can stop iterating over the stack entries once the matching
- // ID has been found.
- // FIXME optimize by traversing stack from top to bottom, the matching ID is more likely
- // at the top of the stack
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = stackIterator.next();
- //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- if (rcse.mRccId == rccId) {
- rvo = rcse.mRemoteVolumeObs;
- break;
+ // with this RCC ID, but the matching ID is more likely at the top of the stack, so
+ // start iterating from the top.
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+ //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
+ if (rcse.mRccId == rccId) {
+ rvo = rcse.mRemoteVolumeObs;
+ break;
+ }
}
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
}
}
if (rvo != null) {
@@ -5866,18 +5870,20 @@
IRemoteVolumeObserver rvo = null;
synchronized (mRCStack) {
// The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, and we can stop iterating over the stack entries once the matching
- // ID has been found.
- // FIXME optimize by traversing stack from top to bottom, the matching ID is more likely
- // at the top of the stack
- Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
- while(stackIterator.hasNext()) {
- RemoteControlStackEntry rcse = stackIterator.next();
- if (rcse.mRccId == rccId) {
+ // with this RCC ID, but the matching ID is more likely at the top of the stack, so
+ // start iterating from the top.
+ try {
+ for (int index = mRCStack.size()-1; index >= 0; index--) {
+ final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
//FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- rvo = rcse.mRemoteVolumeObs;
- break;
+ if (rcse.mRccId == rccId) {
+ rvo = rcse.mRemoteVolumeObs;
+ break;
+ }
}
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // not expected to happen, indicates improper concurrent modification
+ Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
}
}
if (rvo != null) {
@@ -5991,6 +5997,7 @@
}
}
}
+ mVolumePanel.setLayoutDirection(config.getLayoutDirection());
} catch (Exception e) {
Log.e(TAG, "Error retrieving device orientation: " + e);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index cccce9d..fc0ff55 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -71,7 +71,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 95;
+ private static final int DATABASE_VERSION = 96;
private Context mContext;
private int mUserHandle;
@@ -1488,7 +1488,6 @@
// Redo this step, since somehow it didn't work the first time for some users
if (mUserHandle == UserHandle.USER_OWNER) {
db.beginTransaction();
- SQLiteStatement stmt = null;
try {
// Migrate now-global settings
String[] settingsToMove = hashsetToStringArray(SettingsProvider.sSystemGlobalKeys);
@@ -1499,7 +1498,6 @@
db.setTransactionSuccessful();
} finally {
db.endTransaction();
- if (stmt != null) stmt.close();
}
}
upgradeVersion = 94;
@@ -1524,6 +1522,20 @@
upgradeVersion = 95;
}
+ if (upgradeVersion == 95) {
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ db.beginTransaction();
+ try {
+ String[] settingsToMove = { Settings.Global.BUGREPORT_IN_POWER_MENU };
+ moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+ upgradeVersion = 96;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 3227a34..01596dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -838,9 +838,14 @@
}
public void refreshAllStatusBarIcons() {
- final int count = mStatusIcons.getChildCount();
+ refreshAllIconsForLayout(mStatusIcons);
+ refreshAllIconsForLayout(mNotificationIcons);
+ }
+
+ private void refreshAllIconsForLayout(LinearLayout ll) {
+ final int count = ll.getChildCount();
for (int n = 0; n < count; n++) {
- View child = mStatusIcons.getChildAt(n);
+ View child = ll.getChildAt(n);
if (child instanceof StatusBarIconView) {
((StatusBarIconView) child).updateDrawable();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index a6500a2..4ff3862 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -471,6 +471,8 @@
ImageView iv = (ImageView) view.findViewById(R.id.rssi_image);
ImageView iov = (ImageView) view.findViewById(R.id.rssi_overlay_image);
TextView tv = (TextView) view.findViewById(R.id.rssi_textview);
+ // Force refresh
+ iv.setImageDrawable(null);
iv.setImageResource(rssiState.signalIconId);
if (rssiState.dataTypeIconId > 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 00991c1..1037137 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -139,7 +139,7 @@
public void startObserving() {
final ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.BUGREPORT_IN_POWER_MENU), false, this);
+ Settings.Global.getUriFor(Settings.Global.BUGREPORT_IN_POWER_MENU), false, this);
}
}
@@ -544,7 +544,7 @@
final ContentResolver cr = mContext.getContentResolver();
boolean enabled = false;
try {
- enabled = (Settings.Secure.getInt(cr, Settings.Secure.BUGREPORT_IN_POWER_MENU) != 0);
+ enabled = (Settings.Global.getInt(cr, Settings.Global.BUGREPORT_IN_POWER_MENU) != 0);
} catch (SettingNotFoundException e) {
}
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 3dc77d4..761eb2d 100755
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -260,8 +260,8 @@
mItems.add(mAirplaneModeOn);
// next: bug report, if enabled
- if (Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.BUGREPORT_IN_POWER_MENU, 0) != 0) {
+ if (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0) {
mItems.add(
new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb,
R.string.global_action_bug_report) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index b4fe0c7..ad5e257 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
+import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Slog;
import android.view.Gravity;
@@ -38,10 +39,12 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
+import android.widget.TextClock;
import com.android.internal.widget.LockPatternUtils;
import java.util.ArrayList;
+import java.util.TimeZone;
public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener,
OnLongClickListener, ChallengeLayout.OnBouncerStateChangedListener {
@@ -51,6 +54,9 @@
protected static float OVERSCROLL_MAX_ROTATION = 30;
private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
+ private static final int FLAG_HAS_LOCAL_HOUR = 0x1;
+ private static final int FLAG_HAS_LOCAL_MINUTE = 0x2;
+
protected KeyguardViewStateManager mViewStateManager;
private LockPatternUtils mLockPatternUtils;
@@ -131,16 +137,21 @@
@Override
public void onPageSwitched(View newPage, int newPageIndex) {
- boolean showingStatusWidget = false;
+ boolean showingClock = false;
if (newPage instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) newPage;
if (vg.getChildAt(0) instanceof KeyguardStatusView) {
- showingStatusWidget = true;
+ showingClock = true;
}
}
+ if (newPage != null &&
+ findClockInHierarchy(newPage) == (FLAG_HAS_LOCAL_HOUR | FLAG_HAS_LOCAL_MINUTE)) {
+ showingClock = true;
+ }
+
// Disable the status bar clock if we're showing the default status widget
- if (showingStatusWidget) {
+ if (showingClock) {
setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK);
} else {
setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK);
@@ -857,4 +868,53 @@
protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex);
}
+
+ /**
+ * Search given {@link View} hierarchy for {@link TextClock} instances that
+ * show various time components. Returns combination of
+ * {@link #FLAG_HAS_LOCAL_HOUR} and {@link #FLAG_HAS_LOCAL_MINUTE}.
+ */
+ private static int findClockInHierarchy(View view) {
+ if (view instanceof TextClock) {
+ return getClockFlags((TextClock) view);
+ } else if (view instanceof ViewGroup) {
+ int flags = 0;
+ final ViewGroup group = (ViewGroup) view;
+ final int size = group.getChildCount();
+ for (int i = 0; i < size; i++) {
+ flags |= findClockInHierarchy(group.getChildAt(i));
+ }
+ return flags;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Return combination of {@link #FLAG_HAS_LOCAL_HOUR} and
+ * {@link #FLAG_HAS_LOCAL_MINUTE} describing the time represented described
+ * by the given {@link TextClock}.
+ */
+ private static int getClockFlags(TextClock clock) {
+ int flags = 0;
+
+ final String timeZone = clock.getTimeZone();
+ if (timeZone != null && !TimeZone.getDefault().equals(TimeZone.getTimeZone(timeZone))) {
+ // Ignore clocks showing another timezone
+ return 0;
+ }
+
+ final CharSequence format = clock.getFormat();
+ final char hour = clock.is24HourModeEnabled() ? DateFormat.HOUR_OF_DAY
+ : DateFormat.HOUR;
+
+ if (DateFormat.hasDesignator(format, hour)) {
+ flags |= FLAG_HAS_LOCAL_HOUR;
+ }
+ if (DateFormat.hasDesignator(format, DateFormat.MINUTE)) {
+ flags |= FLAG_HAS_LOCAL_MINUTE;
+ }
+
+ return flags;
+ }
}
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index cbd00f3..fa758a8 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -956,16 +956,12 @@
}
public void scheduleTimeTickEvent() {
- Calendar calendar = Calendar.getInstance();
final long currentTime = System.currentTimeMillis();
- calendar.setTimeInMillis(currentTime);
- calendar.add(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
+ final long nextTime = 60000 * ((currentTime / 60000) + 1);
// Schedule this event for the amount of time that it would take to get to
// the top of the next minute.
- final long tickEventDelay = calendar.getTimeInMillis() - currentTime;
+ final long tickEventDelay = nextTime - currentTime;
set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay,
mTimeTickSender);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 81ae101..91ac1de 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -782,9 +782,6 @@
if (!isSystemIme(imi)) {
return false;
}
- if (imi.isAuxiliaryIme()) {
- return false;
- }
if (imi.getIsDefaultResourceId() != 0) {
try {
Resources res = context.createPackageContext(
@@ -808,9 +805,6 @@
if (!isSystemIme(imi)) {
return false;
}
- if (imi.isAuxiliaryIme()) {
- return false;
- }
return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage());
}
@@ -2864,9 +2858,6 @@
List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
.getEnabledInputMethodsAndSubtypeListLocked();
- if (DEBUG) {
- Slog.d(TAG, (enabled ? "Enable " : "Disable ") + id);
- }
if (enabled) {
for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
if (pair.first.equals(id)) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b08fc28..8297988e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3914,7 +3914,7 @@
}
boolean didSomething = killPackageProcessesLocked(name, appId, userId,
- -100, callerWillRestart, false, doit, evenPersistent,
+ -100, callerWillRestart, true, doit, evenPersistent,
name == null ? ("force stop user " + userId) : ("force stop " + name));
TaskRecord lastTask = null;
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 724e126..b5010f2 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -344,7 +344,7 @@
* Creates the display power controller.
*/
public DisplayPowerController(Looper looper, Context context, Notifier notifier,
- LightsService lights, TwilightService twilight,
+ LightsService lights, TwilightService twilight, SensorManager sensorManager,
DisplayManagerService displayManager,
DisplayBlanker displayBlanker,
Callbacks callbacks, Handler callbackHandler) {
@@ -356,7 +356,7 @@
mLights = lights;
mTwilight = twilight;
- mSensorManager = new SystemSensorManager(mHandler.getLooper());
+ mSensorManager = sensorManager;
mDisplayManager = displayManager;
final Resources resources = context.getResources();
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index ec82290..2652739 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -35,6 +35,8 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.hardware.SensorManager;
+import android.hardware.SystemSensorManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
@@ -153,11 +155,6 @@
// Otherwise the user won't get much screen on time before dimming occurs.
private static final float MAXIMUM_SCREEN_DIM_RATIO = 0.2f;
- // Upper bound on the battery charge percentage in order to consider turning
- // the screen on when the device starts charging wirelessly.
- // See point of use for more details.
- private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
-
// The name of the boot animation service in init.rc.
private static final String BOOT_ANIMATION_SERVICE = "bootanim";
@@ -179,6 +176,7 @@
private WindowManagerPolicy mPolicy;
private Notifier mNotifier;
private DisplayPowerController mDisplayPowerController;
+ private WirelessChargerDetector mWirelessChargerDetector;
private SettingsObserver mSettingsObserver;
private DreamManagerService mDreamManager;
private LightsService.Light mAttentionLight;
@@ -434,6 +432,8 @@
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
+ SensorManager sensorManager = new SystemSensorManager(mHandler.getLooper());
+
// The notifier runs on the system server's main looper so as not to interfere
// with the animations and other critical functions of the power manager.
mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
@@ -441,11 +441,14 @@
mScreenOnBlocker, mPolicy);
// The display power controller runs on the power manager service's
- // own handler thread.
+ // own handler thread to ensure timely operation.
mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
- mContext, mNotifier, mLightsService, twilight, mDisplayManagerService,
- mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler);
+ mContext, mNotifier, mLightsService, twilight, sensorManager,
+ mDisplayManagerService, mDisplayBlanker,
+ mDisplayPowerControllerCallbacks, mHandler);
+ mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
+ createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"));
mSettingsObserver = new SettingsObserver(mHandler);
mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION);
@@ -1164,65 +1167,51 @@
if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
mDirty |= DIRTY_IS_POWERED;
+ // Update wireless dock detection state.
+ final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
+ mIsPowered, mPlugType, mBatteryLevel);
+
// Treat plugging and unplugging the devices as a user activity.
// Users find it disconcerting when they plug or unplug the device
// and it shuts off right away.
// Some devices also wake the device when plugged or unplugged because
// they don't have a charging LED.
final long now = SystemClock.uptimeMillis();
- if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType)) {
+ if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
+ dockedOnWirelessCharger)) {
wakeUpNoUpdateLocked(now);
}
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
// Tell the notifier whether wireless charging has started so that
- // it can provide feedback to the user. Refer to
- // shouldWakeUpWhenPluggedOrUnpluggedLocked for justification of the
- // heuristics used here.
- if (!wasPowered && mIsPowered
- && mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
- && mBatteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) {
+ // it can provide feedback to the user.
+ if (dockedOnWirelessCharger) {
mNotifier.onWirelessChargingStarted();
}
}
}
}
- private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(boolean wasPowered, int oldPlugType) {
+ private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(
+ boolean wasPowered, int oldPlugType, boolean dockedOnWirelessCharger) {
// Don't wake when powered unless configured to do so.
if (!mWakeUpWhenPluggedOrUnpluggedConfig) {
return false;
}
- // FIXME: Need more accurate detection of wireless chargers.
- //
- // We are unable to accurately detect whether the device is resting on the
- // charger unless it is actually receiving power. This causes us some grief
- // because the device might not appear to be plugged into the wireless charger
- // unless it actually charging.
- //
- // To avoid spuriously waking the screen, we apply a special policy to
- // wireless chargers.
- //
- // 1. Don't wake the device when unplugged from wireless charger because
- // it might be that the device is still resting on the wireless charger
- // but is not receiving power anymore because the battery is full.
- //
- // 2. Don't wake the device when plugged into a wireless charger if the
- // battery already appears to be mostly full. This situation may indicate
- // that the device was resting on the charger the whole time and simply
- // wasn't receiving power because the battery was full. We can't tell
- // whether the device was just placed on the charger or whether it has
- // been there for half of the night slowly discharging until it hit
- // the point where it needed to start charging again.
+ // Don't wake when undocked from wireless charger.
+ // See WirelessChargerDetector for justification.
if (wasPowered && !mIsPowered
&& oldPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
return false;
}
+
+ // Don't wake when docked on wireless charger unless we are certain of it.
+ // See WirelessChargerDetector for justification.
if (!wasPowered && mIsPowered
&& mPlugType == BatteryManager.BATTERY_PLUGGED_WIRELESS
- && mBatteryLevel >= WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT) {
+ && !dockedOnWirelessCharger) {
return false;
}
@@ -2209,6 +2198,7 @@
pw.println("POWER MANAGER (dumpsys power)\n");
final DisplayPowerController dpc;
+ final WirelessChargerDetector wcd;
synchronized (mLock) {
pw.println("Power Manager State:");
pw.println(" mDirty=0x" + Integer.toHexString(mDirty));
@@ -2291,11 +2281,16 @@
pw.println("Display Blanker: " + mDisplayBlanker);
dpc = mDisplayPowerController;
+ wcd = mWirelessChargerDetector;
}
if (dpc != null) {
dpc.dump(pw);
}
+
+ if (wcd != null) {
+ wcd.dump(pw);
+ }
}
private SuspendBlocker createSuspendBlockerLocked(String name) {
diff --git a/services/java/com/android/server/power/WirelessChargerDetector.java b/services/java/com/android/server/power/WirelessChargerDetector.java
new file mode 100644
index 0000000..ac6dc3e
--- /dev/null
+++ b/services/java/com/android/server/power/WirelessChargerDetector.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2013 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.power;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.BatteryManager;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements heuristics to detect docking or undocking from a wireless charger.
+ * <p>
+ * Some devices have wireless charging circuits that are unable to detect when the
+ * device is resting on a wireless charger except when the device is actually
+ * receiving power from the charger. The device may stop receiving power
+ * if the battery is already nearly full or if it is too hot. As a result, we cannot
+ * always rely on the battery service wireless plug signal to accurately indicate
+ * whether the device has been docked or undocked from a wireless charger.
+ * </p><p>
+ * This is a problem because the power manager typically wakes up the screen and
+ * plays a tone when the device is docked in a wireless charger. It is important
+ * for the system to suppress spurious docking and undocking signals because they
+ * can be intrusive for the user (especially if they cause a tone to be played
+ * late at night for no apparent reason).
+ * </p><p>
+ * To avoid spurious signals, we apply some special policies to wireless chargers.
+ * </p><p>
+ * 1. Don't wake the device when undocked from the wireless charger because
+ * it might be that the device is still resting on the wireless charger
+ * but is not receiving power anymore because the battery is full.
+ * Ideally we would wake the device if we could be certain that the user had
+ * picked it up from the wireless charger but due to hardware limitations we
+ * must be more conservative.
+ * </p><p>
+ * 2. Don't wake the device when docked on a wireless charger if the
+ * battery already appears to be mostly full. This situation may indicate
+ * that the device was resting on the charger the whole time and simply
+ * wasn't receiving power because the battery was already full. We can't tell
+ * whether the device was just placed on the charger or whether it has
+ * been there for half of the night slowly discharging until it reached
+ * the point where it needed to start charging again. So we suppress docking
+ * signals that occur when the battery level is above a given threshold.
+ * </p><p>
+ * 3. Don't wake the device when docked on a wireless charger if it does
+ * not appear to have moved since it was last undocked because it may
+ * be that the prior undocking signal was spurious. We use the gravity
+ * sensor to detect this case.
+ * </p>
+ */
+final class WirelessChargerDetector {
+ private static final String TAG = "WirelessChargerDetector";
+ private static final boolean DEBUG = false;
+
+ // Number of nanoseconds per millisecond.
+ private static final long NANOS_PER_MS = 1000000;
+
+ // The minimum amount of time to spend watching the sensor before making
+ // a determination of whether movement occurred.
+ private static final long SETTLE_TIME_NANOS = 500 * NANOS_PER_MS;
+
+ // The minimum number of samples that must be collected.
+ private static final int MIN_SAMPLES = 3;
+
+ // Upper bound on the battery charge percentage in order to consider turning
+ // the screen on when the device starts charging wirelessly.
+ private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
+
+ // To detect movement, we compute the angle between the gravity vector
+ // at rest and the current gravity vector. This field specifies the
+ // cosine of the maximum angle variance that we tolerate while at rest.
+ private static final double MOVEMENT_ANGLE_COS_THRESHOLD = Math.cos(5 * Math.PI / 180);
+
+ // Sanity thresholds for the gravity vector.
+ private static final double MIN_GRAVITY = SensorManager.GRAVITY_EARTH - 1.0f;
+ private static final double MAX_GRAVITY = SensorManager.GRAVITY_EARTH + 1.0f;
+
+ private final Object mLock = new Object();
+
+ private final SensorManager mSensorManager;
+ private final SuspendBlocker mSuspendBlocker;
+
+ // The gravity sensor, or null if none.
+ private Sensor mGravitySensor;
+
+ // Previously observed wireless power state.
+ private boolean mPoweredWirelessly;
+
+ // True if the device is thought to be at rest on a wireless charger.
+ private boolean mAtRest;
+
+ // The gravity vector most recently observed while at rest.
+ private float mRestX, mRestY, mRestZ;
+
+ /* These properties are only meaningful while detection is in progress. */
+
+ // True if detection is in progress.
+ // The suspend blocker is held while this is the case.
+ private boolean mDetectionInProgress;
+
+ // True if the rest position should be updated if at rest.
+ // Otherwise, the current rest position is simply checked and cleared if movement
+ // is detected but no new rest position is stored.
+ private boolean mMustUpdateRestPosition;
+
+ // The total number of samples collected.
+ private int mTotalSamples;
+
+ // The number of samples collected that showed evidence of not being at rest.
+ private int mMovingSamples;
+
+ // The time and value of the first sample that was collected.
+ private long mFirstSampleTime;
+ private float mFirstSampleX, mFirstSampleY, mFirstSampleZ;
+
+ public WirelessChargerDetector(SensorManager sensorManager,
+ SuspendBlocker suspendBlocker) {
+ mSensorManager = sensorManager;
+ mSuspendBlocker = suspendBlocker;
+
+ mGravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
+ }
+
+ public void dump(PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("Wireless Charger Detector State:");
+ pw.println(" mGravitySensor=" + mGravitySensor);
+ pw.println(" mPoweredWirelessly=" + mPoweredWirelessly);
+ pw.println(" mAtRest=" + mAtRest);
+ pw.println(" mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ);
+ pw.println(" mDetectionInProgress=" + mDetectionInProgress);
+ pw.println(" mMustUpdateRestPosition=" + mMustUpdateRestPosition);
+ pw.println(" mTotalSamples=" + mTotalSamples);
+ pw.println(" mMovingSamples=" + mMovingSamples);
+ pw.println(" mFirstSampleTime=" + mFirstSampleTime);
+ pw.println(" mFirstSampleX=" + mFirstSampleX
+ + ", mFirstSampleY=" + mFirstSampleY + ", mFirstSampleZ=" + mFirstSampleZ);
+ }
+ }
+
+ /**
+ * Updates the charging state and returns true if docking was detected.
+ *
+ * @param isPowered True if the device is powered.
+ * @param plugType The current plug type.
+ * @param batteryLevel The current battery level.
+ * @return True if the device is determined to have just been docked on a wireless
+ * charger, after suppressing spurious docking or undocking signals.
+ */
+ public boolean update(boolean isPowered, int plugType, int batteryLevel) {
+ synchronized (mLock) {
+ final boolean wasPoweredWirelessly = mPoweredWirelessly;
+
+ if (isPowered && plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+ // The device is receiving power from the wireless charger.
+ // Update the rest position asynchronously.
+ mPoweredWirelessly = true;
+ mMustUpdateRestPosition = true;
+ startDetectionLocked();
+ } else {
+ // The device may or may not be on the wireless charger depending on whether
+ // the unplug signal that we received was spurious.
+ mPoweredWirelessly = false;
+ if (mAtRest) {
+ if (plugType != 0 && plugType != BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+ // The device was plugged into a new non-wireless power source.
+ // It's safe to assume that it is no longer on the wireless charger.
+ mMustUpdateRestPosition = false;
+ clearAtRestLocked();
+ } else {
+ // The device may still be on the wireless charger but we don't know.
+ // Check whether the device has remained at rest on the charger
+ // so that we will know to ignore the next wireless plug event
+ // if needed.
+ startDetectionLocked();
+ }
+ }
+ }
+
+ // Report that the device has been docked only if the device just started
+ // receiving power wirelessly, has a high enough battery level that we
+ // can be assured that charging was not delayed due to the battery previously
+ // having been full, and the device is not known to already be at rest
+ // on the wireless charger from earlier.
+ return mPoweredWirelessly && !wasPoweredWirelessly
+ && batteryLevel < WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT
+ && !mAtRest;
+ }
+ }
+
+ private void startDetectionLocked() {
+ if (!mDetectionInProgress && mGravitySensor != null) {
+ if (mSensorManager.registerListener(mListener, mGravitySensor,
+ SensorManager.SENSOR_DELAY_UI)) {
+ mSuspendBlocker.acquire();
+ mDetectionInProgress = true;
+ mTotalSamples = 0;
+ mMovingSamples = 0;
+ }
+ }
+ }
+
+ private void processSample(long timeNanos, float x, float y, float z) {
+ synchronized (mLock) {
+ if (!mDetectionInProgress) {
+ return;
+ }
+
+ mTotalSamples += 1;
+ if (mTotalSamples == 1) {
+ // Save information about the first sample collected.
+ mFirstSampleTime = timeNanos;
+ mFirstSampleX = x;
+ mFirstSampleY = y;
+ mFirstSampleZ = z;
+ } else {
+ // Determine whether movement has occurred relative to the first sample.
+ if (hasMoved(mFirstSampleX, mFirstSampleY, mFirstSampleZ, x, y, z)) {
+ mMovingSamples += 1;
+ }
+ }
+
+ // Clear the at rest flag if movement has occurred relative to the rest sample.
+ if (mAtRest && hasMoved(mRestX, mRestY, mRestZ, x, y, z)) {
+ if (DEBUG) {
+ Slog.d(TAG, "No longer at rest: "
+ + "mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ
+ + ", x=" + x + ", y=" + y + ", z=" + z);
+ }
+ clearAtRestLocked();
+ }
+
+ // Save the result when done.
+ if (timeNanos - mFirstSampleTime >= SETTLE_TIME_NANOS
+ && mTotalSamples >= MIN_SAMPLES) {
+ mSensorManager.unregisterListener(mListener);
+ if (mMustUpdateRestPosition) {
+ if (mMovingSamples == 0) {
+ mAtRest = true;
+ mRestX = x;
+ mRestY = y;
+ mRestZ = z;
+ } else {
+ clearAtRestLocked();
+ }
+ mMustUpdateRestPosition = false;
+ }
+ mDetectionInProgress = false;
+ mSuspendBlocker.release();
+
+ if (DEBUG) {
+ Slog.d(TAG, "New state: mAtRest=" + mAtRest
+ + ", mRestX=" + mRestX + ", mRestY=" + mRestY + ", mRestZ=" + mRestZ
+ + ", mTotalSamples=" + mTotalSamples
+ + ", mMovingSamples=" + mMovingSamples);
+ }
+ }
+ }
+ }
+
+ private void clearAtRestLocked() {
+ mAtRest = false;
+ mRestX = 0;
+ mRestY = 0;
+ mRestZ = 0;
+ }
+
+ private static boolean hasMoved(float x1, float y1, float z1,
+ float x2, float y2, float z2) {
+ final double dotProduct = (x1 * x2) + (y1 * y2) + (z1 * z2);
+ final double mag1 = Math.sqrt((x1 * x1) + (y1 * y1) + (z1 * z1));
+ final double mag2 = Math.sqrt((x2 * x2) + (y2 * y2) + (z2 * z2));
+ if (mag1 < MIN_GRAVITY || mag1 > MAX_GRAVITY
+ || mag2 < MIN_GRAVITY || mag2 > MAX_GRAVITY) {
+ if (DEBUG) {
+ Slog.d(TAG, "Weird gravity vector: mag1=" + mag1 + ", mag2=" + mag2);
+ }
+ return true;
+ }
+ final boolean moved = (dotProduct < mag1 * mag2 * MOVEMENT_ANGLE_COS_THRESHOLD);
+ if (DEBUG) {
+ Slog.d(TAG, "Check: moved=" + moved
+ + ", x1=" + x1 + ", y1=" + y1 + ", z1=" + z1
+ + ", x2=" + x2 + ", y2=" + y2 + ", z2=" + z2
+ + ", angle=" + (Math.acos(dotProduct / mag1 / mag2) * 180 / Math.PI)
+ + ", dotProduct=" + dotProduct
+ + ", mag1=" + mag1 + ", mag2=" + mag2);
+ }
+ return moved;
+ }
+
+ private final SensorEventListener mListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ processSample(event.timestamp, event.values[0], event.values[1], event.values[2]);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+}
diff --git a/services/java/com/android/server/wm/DimLayer.java b/services/java/com/android/server/wm/DimLayer.java
index 4c7add9..88efe2e 100644
--- a/services/java/com/android/server/wm/DimLayer.java
+++ b/services/java/com/android/server/wm/DimLayer.java
@@ -84,6 +84,17 @@
return mTargetAlpha;
}
+ void setLayer(int layer) {
+ if (mLayer != layer) {
+ mLayer = layer;
+ mDimSurface.setLayer(layer);
+ }
+ }
+
+ int getLayer() {
+ return mLayer;
+ }
+
private void setAlpha(float alpha) {
if (mAlpha != alpha) {
if (DEBUG) Slog.v(TAG, "setAlpha alpha=" + alpha);
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index d5144fb..d42221e 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -573,10 +573,13 @@
final DimLayer dimAnimator = displayAnimator.mDimAnimator;
final WindowStateAnimator winAnimator = displayAnimator.mDimWinAnimator;
+ final int dimLayer;
final float dimAmount;
if (winAnimator == null) {
+ dimLayer = dimAnimator.getLayer();
dimAmount = 0;
} else {
+ dimLayer = winAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
dimAmount = winAnimator.mWin.mAttrs.dimAmount;
}
final float targetAlpha = dimAnimator.getTargetAlpha();
@@ -590,9 +593,10 @@
if (targetAlpha > dimAmount) {
duration = getDimBehindFadeDuration(duration);
}
- dimAnimator.show(winAnimator.mAnimLayer -
- WindowManagerService.LAYER_OFFSET_DIM, dimAmount, duration);
+ dimAnimator.show(dimLayer, dimAmount, duration);
}
+ } else if (dimAnimator.getLayer() != dimLayer) {
+ dimAnimator.setLayer(dimLayer);
}
if (dimAnimator.isAnimating()) {
if (!mService.okToDisplay()) {