Merge "Allow optional specification of a PTS timestamp when signalling a discontinuity."
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 01bc2df..2170d72 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1678,7 +1678,7 @@
public static final int STATUS_BAR_VISIBLE = 0;
/**
- * View has requested the status bar to be visible (the default).
+ * View has requested the status bar to be hidden.
*
* @see #setSystemUiVisibility(int)
*/
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 43f8790..af20ddb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3666,9 +3666,11 @@
* @param interfaceName The name of the interface to remove.
*/
public void removeJavascriptInterface(String interfaceName) {
- WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
- arg.mInterfaceName = interfaceName;
- mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
+ if (mWebViewCore != null) {
+ WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
+ arg.mInterfaceName = interfaceName;
+ mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
+ }
}
/**
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 88c8b2a..28f64a9 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -227,10 +227,13 @@
assert density > 0;
if (Math.abs(density - mDefaultScale) > MINIMUM_SCALE_INCREMENT) {
+ // Remember the current zoom density before it gets changed.
+ final float originalDefault = mDefaultScale;
// set the new default density
setDefaultZoomScale(density);
+ float scaleChange = (originalDefault > 0.0) ? density / originalDefault: 1.0f;
// adjust the scale if it falls outside the new zoom bounds
- setZoomScale(mActualScale, true);
+ setZoomScale(mActualScale * scaleChange, true);
}
}
@@ -629,7 +632,7 @@
}
/* package */ float getZoomOverviewScale() {
- return computeScaleWithLimits(mWebView.getViewWidth() * mInvZoomOverviewWidth);
+ return mWebView.getViewWidth() * mInvZoomOverviewWidth;
}
public boolean isInZoomOverview() {
@@ -882,6 +885,7 @@
if (!mMinZoomScaleFixed) {
mMinZoomScale = newZoomOverviewScale;
+ mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale);
}
// fit the content width to the current view for the first new picture
// after first layout.
@@ -956,6 +960,10 @@
final Point viewSize = drawData.mViewSize;
updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
setupZoomOverviewWidth(drawData, mWebView.getViewWidth());
+ if (!mMinZoomScaleFixed) {
+ mMinZoomScale = getZoomOverviewScale();
+ mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale);
+ }
if (!mWebView.drawHistory()) {
float scale;
@@ -971,7 +979,7 @@
scale = overviewScale;
if (!settings.getUseWideViewPort()
|| !settings.getLoadWithOverviewMode()) {
- scale = Math.max(viewState.mTextWrapScale, scale);
+ scale = Math.max(mDefaultScale, scale);
}
if (settings.isNarrowColumnLayout() &&
settings.getUseFixedViewport()) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 2925632..eca39fc 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -624,6 +624,9 @@
*/
private boolean mForceTranscriptScroll;
+ private int mGlowPaddingLeft;
+ private int mGlowPaddingRight;
+
/**
* Interface definition for a callback to be invoked when the list or grid
* has been scrolled.
@@ -3314,9 +3317,11 @@
final int scrollY = mScrollY;
if (!mEdgeGlowTop.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth() - mListPadding.left - mListPadding.right;
+ final int leftPadding = mListPadding.left + mGlowPaddingLeft;
+ final int rightPadding = mListPadding.right + mGlowPaddingRight;
+ final int width = getWidth() - leftPadding - rightPadding;
- canvas.translate(mListPadding.left,
+ canvas.translate(leftPadding,
Math.min(0, scrollY + mFirstPositionDistanceGuess));
mEdgeGlowTop.setSize(width, getHeight());
if (mEdgeGlowTop.draw(canvas)) {
@@ -3326,10 +3331,12 @@
}
if (!mEdgeGlowBottom.isFinished()) {
final int restoreCount = canvas.save();
- final int width = getWidth() - mListPadding.left - mListPadding.right;
+ final int leftPadding = mListPadding.left + mGlowPaddingLeft;
+ final int rightPadding = mListPadding.right + mGlowPaddingRight;
+ final int width = getWidth() - leftPadding - rightPadding;
final int height = getHeight();
- canvas.translate(-width + mListPadding.left,
+ canvas.translate(-width + leftPadding,
Math.max(height, scrollY + mLastPositionDistanceGuess));
canvas.rotate(180, width, 0);
mEdgeGlowBottom.setSize(width, height);
@@ -3353,6 +3360,14 @@
}
}
+ /**
+ * @hide
+ */
+ public void setOverScrollEffectPadding(int leftPadding, int rightPadding) {
+ mGlowPaddingLeft = leftPadding;
+ mGlowPaddingRight = rightPadding;
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
diff --git a/data/etc/android.hardware.usb.accessory.xml b/data/etc/android.hardware.usb.accessory.xml
index 29df966..80a0904 100644
--- a/data/etc/android.hardware.usb.accessory.xml
+++ b/data/etc/android.hardware.usb.accessory.xml
@@ -17,4 +17,6 @@
<!-- This is the standard feature indicating that the device supports USB accessories. -->
<permissions>
<feature name="android.hardware.usb.accessory" />
+ <library name="com.android.future.usb.accessory"
+ file="/system/framework/com.android.future.usb.accessory.jar" />
</permissions>
diff --git a/data/etc/com.google.android.usb.xml b/data/etc/com.google.android.usb.xml
deleted file mode 100644
index fae3d23..0000000
--- a/data/etc/com.google.android.usb.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<permissions>
- <library name="com.google.android.usb"
- file="/system/framework/com.google.android.usb.jar" />
-</permissions>
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 3f9f961..b8e9384 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -17,83 +17,93 @@
package android.graphics;
public class ImageFormat {
- /*
- * these constants are chosen to be binary compatible with their previous
- * location in PixelFormat.java
- */
+ /*
+ * these constants are chosen to be binary compatible with their previous
+ * location in PixelFormat.java
+ */
- public static final int UNKNOWN = 0;
+ public static final int UNKNOWN = 0;
- /**
- * RGB format used for pictures encoded as RGB_565 see
- * {@link android.hardware.Camera.Parameters#setPictureFormat(int)}.
- */
- public static final int RGB_565 = 4;
+ /**
+ * RGB format used for pictures encoded as RGB_565 see
+ * {@link android.hardware.Camera.Parameters#setPictureFormat(int)}.
+ */
+ public static final int RGB_565 = 4;
- /**
- * Planar 4:2:0 YCrCb format. This format assumes an horizontal stride of 16
- * pixels for all planes and an implicit vertical stride of the image
- * height's next multiple of two.
- * y_size = stride * ALIGN(height, 2)
- * c_size = ALIGN(stride/2, 16) * height
- * size = y_size + c_size * 2
- * cr_offset = y_size
- * cb_offset = y_size + c_size
- *
- * Whether this format is supported by the camera hardware can be determined
- * by
- * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
- */
- public static final int YV12 = 0x32315659;
+ /**
+ * Android YUV format:
+ *
+ * This format is exposed to software decoders and applications.
+ *
+ * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed
+ * by (W/2) x (H/2) Cr and Cb planes.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ *
+ * y_size = stride * height
+ * c_size = ALIGN(stride/2, 16) * height/2
+ * size = y_size + c_size * 2
+ * cr_offset = y_size
+ * cb_offset = y_size + c_size
+ *
+ * Whether this format is supported by the camera hardware can be determined
+ * by
+ * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+ */
+ public static final int YV12 = 0x32315659;
- /**
- * YCbCr format, used for video. Whether this format is supported by the
- * camera hardware can be determined by
- * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
- */
- public static final int NV16 = 0x10;
+ /**
+ * YCbCr format, used for video. Whether this format is supported by the
+ * camera hardware can be determined by
+ * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+ */
+ public static final int NV16 = 0x10;
- /**
- * YCrCb format used for images, which uses the NV21 encoding format. This
- * is the default format for camera preview images, when not otherwise set
- * with {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}.
- */
- public static final int NV21 = 0x11;
+ /**
+ * YCrCb format used for images, which uses the NV21 encoding format. This
+ * is the default format for camera preview images, when not otherwise set
+ * with {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}.
+ */
+ public static final int NV21 = 0x11;
- /**
- * YCbCr format used for images, which uses YUYV (YUY2) encoding format.
- * This is an alternative format for camera preview images. Whether this
- * format is supported by the camera hardware can be determined by
- * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
- */
- public static final int YUY2 = 0x14;
+ /**
+ * YCbCr format used for images, which uses YUYV (YUY2) encoding format.
+ * This is an alternative format for camera preview images. Whether this
+ * format is supported by the camera hardware can be determined by
+ * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+ */
+ public static final int YUY2 = 0x14;
- /**
- * Encoded formats. These are not necessarily supported by the hardware.
- */
- public static final int JPEG = 0x100;
+ /**
+ * Encoded formats. These are not necessarily supported by the hardware.
+ */
+ public static final int JPEG = 0x100;
- /**
- * Use this function to retrieve the number of bits per pixel of an
- * ImageFormat.
- *
- * @param format
- * @return the number of bits per pixel of the given format or -1 if the
- * format doesn't exist or is not supported.
- */
- public static int getBitsPerPixel(int format) {
- switch (format) {
- case RGB_565:
- return 16;
- case NV16:
- return 16;
- case YUY2:
- return 16;
- case YV12:
- return 12;
- case NV21:
- return 12;
- }
- return -1;
- }
+ /**
+ * Use this function to retrieve the number of bits per pixel of an
+ * ImageFormat.
+ *
+ * @param format
+ * @return the number of bits per pixel of the given format or -1 if the
+ * format doesn't exist or is not supported.
+ */
+ public static int getBitsPerPixel(int format) {
+ switch (format) {
+ case RGB_565:
+ return 16;
+ case NV16:
+ return 16;
+ case YUY2:
+ return 16;
+ case YV12:
+ return 12;
+ case NV21:
+ return 12;
+ }
+ return -1;
+ }
}
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 9979e2a..b39d2e4 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -142,7 +142,8 @@
}
int count = x * y * z * faces;
- if (hasLod && (x > 1) && (y > 1) && (z > 1)) {
+
+ while (hasLod && ((x > 1) || (y > 1) || (z > 1))) {
if(x > 1) {
x >>= 1;
}
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 86ce098..082f11c 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -52,6 +52,14 @@
};
/*
+ * SystemUiVisibility constants from View.
+ */
+enum {
+ ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
+ ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
+};
+
+/*
* Maximum number of pointers supported per motion event.
* Smallest number of pointers is 1.
* (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
index 54bc968..609f319 100644
--- a/include/ui/Keyboard.h
+++ b/include/ui/Keyboard.h
@@ -127,6 +127,11 @@
*/
extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
+/**
+ * Returns true if a key is a meta key like ALT or CAPS_LOCK.
+ */
+extern bool isMetaKey(int32_t keyCode);
+
} // namespace android
#endif // _UI_KEYBOARD_H
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index eefff31..3c2905d 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -45,6 +45,51 @@
namespace android {
/**
+ * A message that can be posted to a Looper.
+ */
+struct Message {
+ Message() : what(0) { }
+ Message(int what) : what(what) { }
+
+ /* The message type. (interpretation is left up to the handler) */
+ int what;
+};
+
+
+/**
+ * Interface for a Looper message handler.
+ *
+ * The Looper holds a strong reference to the message handler whenever it has
+ * a message to deliver to it. Make sure to call Looper::removeMessages
+ * to remove any pending messages destined for the handler so that the handler
+ * can be destroyed.
+ */
+class MessageHandler : public virtual RefBase {
+protected:
+ virtual ~MessageHandler() { }
+
+public:
+ /**
+ * Handles a message.
+ */
+ virtual void handleMessage(const Message& message) = 0;
+};
+
+
+/**
+ * A simple proxy that holds a weak reference to a message handler.
+ */
+class WeakMessageHandler : public MessageHandler {
+public:
+ WeakMessageHandler(const wp<MessageHandler>& handler);
+ virtual void handleMessage(const Message& message);
+
+private:
+ wp<MessageHandler> mHandler;
+};
+
+
+/**
* A polling loop that supports monitoring file descriptor events, optionally
* using callbacks. The implementation uses epoll() internally.
*
@@ -166,6 +211,52 @@
int removeFd(int fd);
/**
+ * Enqueues a message to be processed by the specified handler.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessage(const sp<MessageHandler>& handler, const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * after the specified delay.
+ *
+ * The time delay is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Enqueues a message to be processed by the specified handler after all pending messages
+ * at the specified time.
+ *
+ * The time is specified in uptime nanoseconds.
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+ const Message& message);
+
+ /**
+ * Removes all messages for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler);
+
+ /**
+ * Removes all messages of a particular type for the specified handler from the queue.
+ *
+ * The handler must not be null.
+ * This method can be called on any thread.
+ */
+ void removeMessages(const sp<MessageHandler>& handler, int what);
+
+ /**
* Prepares a looper associated with the calling thread, and returns it.
* If the thread already has a looper, it is returned. Otherwise, a new
* one is created, associated with the thread, and returned.
@@ -201,12 +292,27 @@
Request request;
};
+ struct MessageEnvelope {
+ MessageEnvelope() : uptime(0) { }
+
+ MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
+ const Message& message) : uptime(uptime), handler(handler), message(message) {
+ }
+
+ nsecs_t uptime;
+ sp<MessageHandler> handler;
+ Message message;
+ };
+
const bool mAllowNonCallbacks; // immutable
int mWakeReadPipeFd; // immutable
int mWakeWritePipeFd; // immutable
Mutex mLock;
+ Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
+ bool mSendingMessage; // guarded by mLock
+
#ifdef LOOPER_USES_EPOLL
int mEpollFd; // immutable
@@ -256,6 +362,7 @@
// it runs on a single thread.
Vector<Response> mResponses;
size_t mResponseIndex;
+ nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
int pollInner(int timeoutMillis);
void awoken();
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 8bae684..aa9b40e 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -40,9 +40,9 @@
///////////////////////////////////////////////////////////////////////////////
Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle) :
+ int flags, uint32_t italicStyle, uint32_t scaleX) :
mState(state), mFontId(fontId), mFontSize(fontSize),
- mFlags(flags), mItalicStyle(italicStyle) {
+ mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
}
@@ -279,18 +279,19 @@
}
Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle) {
+ int flags, uint32_t italicStyle, uint32_t scaleX) {
Vector<Font*> &activeFonts = state->mActiveFonts;
for (uint32_t i = 0; i < activeFonts.size(); i++) {
Font* font = activeFonts[i];
if (font->mFontId == fontId && font->mFontSize == fontSize &&
- font->mFlags == flags && font->mItalicStyle == italicStyle) {
+ font->mFlags == flags && font->mItalicStyle == italicStyle &&
+ font->mScaleX == scaleX) {
return font;
}
}
- Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle);
+ Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
activeFonts.push(newFont);
return newFont;
}
@@ -657,7 +658,9 @@
const float skewX = paint->getTextSkewX();
uint32_t italicStyle = *(uint32_t*) &skewX;
- mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
+ const float scaleXFloat = paint->getTextScaleX();
+ uint32_t scaleX = *(uint32_t*) &scaleXFloat;
+ mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
const float maxPrecacheFontSize = 40.0f;
bool isNewFont = currentNumFonts != mActiveFonts.size();
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 46f332e..3a7aa96 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -58,7 +58,7 @@
* Creates a new font associated with the specified font state.
*/
static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle);
+ int flags, uint32_t italicStyle, uint32_t scaleX);
protected:
friend class FontRenderer;
@@ -104,7 +104,8 @@
SkFixed mRsbDelta;
};
- Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle);
+ Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
+ uint32_t scaleX);
DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs;
@@ -124,6 +125,7 @@
float mFontSize;
int mFlags;
uint32_t mItalicStyle;
+ uint32_t mScaleX;
};
class FontRenderer {
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 24f9739..b1eb164 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -214,6 +214,11 @@
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->texture, 0);
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glEnable(GL_SCISSOR_TEST);
+
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
return layer;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 361815a..dfca7eb 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -144,6 +144,8 @@
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ mSnapshot->fbo = getTargetFbo();
+
mSaveCount = 1;
glViewport(0, 0, mWidth, mHeight);
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
index 8b6300a..600a951 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/ui/Keyboard.cpp
@@ -322,5 +322,26 @@
}
}
+bool isMetaKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_ALT_LEFT:
+ case AKEYCODE_ALT_RIGHT:
+ case AKEYCODE_SHIFT_LEFT:
+ case AKEYCODE_SHIFT_RIGHT:
+ case AKEYCODE_SYM:
+ case AKEYCODE_FUNCTION:
+ case AKEYCODE_CTRL_LEFT:
+ case AKEYCODE_CTRL_RIGHT:
+ case AKEYCODE_META_LEFT:
+ case AKEYCODE_META_RIGHT:
+ case AKEYCODE_CAPS_LOCK:
+ case AKEYCODE_NUM_LOCK:
+ case AKEYCODE_SCROLL_LOCK:
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace android
diff --git a/libs/usb/Android.mk b/libs/usb/Android.mk
index d0ef6f0..b4e1fbf 100644
--- a/libs/usb/Android.mk
+++ b/libs/usb/Android.mk
@@ -22,6 +22,6 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE:= com.google.android.usb
+LOCAL_MODULE:= com.android.future.usb.accessory
include $(BUILD_JAVA_LIBRARY)
diff --git a/libs/usb/src/com/google/android/usb/UsbAccessory.java b/libs/usb/src/com/android/future/usb/UsbAccessory.java
similarity index 98%
rename from libs/usb/src/com/google/android/usb/UsbAccessory.java
rename to libs/usb/src/com/android/future/usb/UsbAccessory.java
index 931f42e..cdd2b73 100644
--- a/libs/usb/src/com/google/android/usb/UsbAccessory.java
+++ b/libs/usb/src/com/android/future/usb/UsbAccessory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.usb;
+package com.android.future.usb;
/**
* A class representing a USB accessory.
diff --git a/libs/usb/src/com/google/android/usb/UsbManager.java b/libs/usb/src/com/android/future/usb/UsbManager.java
similarity index 98%
rename from libs/usb/src/com/google/android/usb/UsbManager.java
rename to libs/usb/src/com/android/future/usb/UsbManager.java
index d7afb95..f74b291 100644
--- a/libs/usb/src/com/google/android/usb/UsbManager.java
+++ b/libs/usb/src/com/android/future/usb/UsbManager.java
@@ -15,7 +15,7 @@
*/
-package com.google.android.usb;
+package com.android.future.usb;
import android.content.Context;
import android.content.Intent;
diff --git a/libs/usb/tests/AccessoryChat/Android.mk b/libs/usb/tests/AccessoryChat/Android.mk
index b854569..98b6090 100644
--- a/libs/usb/tests/AccessoryChat/Android.mk
+++ b/libs/usb/tests/AccessoryChat/Android.mk
@@ -7,7 +7,7 @@
LOCAL_PACKAGE_NAME := AccessoryChatGB
-LOCAL_JAVA_LIBRARIES := com.google.android.usb
+LOCAL_JAVA_LIBRARIES := com.android.future.usb.accessory
# Force an old SDK version to make sure we aren't using newer UsbManager APIs
LOCAL_SDK_VERSION := 8
diff --git a/libs/usb/tests/AccessoryChat/AndroidManifest.xml b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
index 5922421..97e2ade 100644
--- a/libs/usb/tests/AccessoryChat/AndroidManifest.xml
+++ b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
@@ -1,8 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.accessorychat">
+ package="com.android.accessorychat">
<application>
- <uses-library android:name="com.google.android.usb" />
+ <uses-library android:name="com.android.future.usb.accessory" />
<activity android:name="AccessoryChat" android:label="Accessory Chat GB">
<intent-filter>
@@ -19,5 +19,5 @@
android:resource="@xml/accessory_filter" />
</activity>
</application>
- <uses-sdk android:minSdkVersion="8" />
+ <uses-sdk android:minSdkVersion="10" />
</manifest>
diff --git a/libs/usb/tests/AccessoryChat/src/com/google/android/accessorychat/AccessoryChat.java b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
similarity index 97%
rename from libs/usb/tests/AccessoryChat/src/com/google/android/accessorychat/AccessoryChat.java
rename to libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
index 0a5701c..5cf02c7 100644
--- a/libs/usb/tests/AccessoryChat/src/com/google/android/accessorychat/AccessoryChat.java
+++ b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.android.accessorychat;
+package com.android.accessorychat;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -31,8 +31,8 @@
import android.widget.EditText;
import android.widget.TextView;
-import com.google.android.usb.UsbAccessory;
-import com.google.android.usb.UsbManager;
+import com.android.future.usb.UsbAccessory;
+import com.android.future.usb.UsbManager;
import java.io.FileDescriptor;
import java.io.FileInputStream;
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index a5363d6..18f858b 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -19,10 +19,27 @@
#include <unistd.h>
#include <fcntl.h>
+#include <limits.h>
namespace android {
+// --- WeakMessageHandler ---
+
+WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
+ mHandler(handler) {
+}
+
+void WeakMessageHandler::handleMessage(const Message& message) {
+ sp<MessageHandler> handler = mHandler.promote();
+ if (handler != NULL) {
+ handler->handleMessage(message);
+ }
+}
+
+
+// --- Looper ---
+
#ifdef LOOPER_USES_EPOLL
// Hint for number of file descriptors to be associated with the epoll instance.
static const int EPOLL_SIZE_HINT = 8;
@@ -35,8 +52,8 @@
static pthread_key_t gTLSKey = 0;
Looper::Looper(bool allowNonCallbacks) :
- mAllowNonCallbacks(allowNonCallbacks),
- mResponseIndex(0) {
+ mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
+ mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
int result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
@@ -161,17 +178,21 @@
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
- if (! response.request.callback) {
+ ALooper_callbackFunc callback = response.request.callback;
+ if (!callback) {
+ int ident = response.request.ident;
+ int fd = response.request.fd;
+ int events = response.events;
+ void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - returning signalled identifier %d: "
- "fd=%d, events=0x%x, data=%p", this,
- response.request.ident, response.request.fd,
- response.events, response.request.data);
+ "fd=%d, events=0x%x, data=%p",
+ this, ident, fd, events, data);
#endif
- if (outFd != NULL) *outFd = response.request.fd;
- if (outEvents != NULL) *outEvents = response.events;
- if (outData != NULL) *outData = response.request.data;
- return response.request.ident;
+ if (outFd != NULL) *outFd = fd;
+ if (outEvents != NULL) *outEvents = events;
+ if (outData != NULL) *outData = data;
+ return ident;
}
}
@@ -194,6 +215,25 @@
LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
+ // Adjust the timeout based on when the next message is due.
+ if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (mNextMessageUptime <= now) {
+ timeoutMillis = 0;
+ } else {
+ uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL;
+ if (delay < INT_MAX
+ && (timeoutMillis < 0 || int(delay) < timeoutMillis)) {
+ timeoutMillis = int(delay);
+ }
+ }
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
+ this, mNextMessageUptime - now, timeoutMillis);
+#endif
+ }
+
+ // Poll.
int result = ALOOPER_POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
@@ -205,7 +245,6 @@
#ifdef LOOPER_USES_EPOLL
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
- bool acquiredLock = false;
#else
// Wait for wakeAndLock() waiters to run then set mPolling to true.
mLock.lock();
@@ -219,16 +258,20 @@
int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
#endif
+ // Acquire lock.
+ mLock.lock();
+
+ // Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
-
LOGW("Poll failed with an unexpected error, errno=%d", errno);
result = ALOOPER_POLL_ERROR;
goto Done;
}
+ // Check for poll timeout.
if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - timeout", this);
@@ -237,6 +280,7 @@
goto Done;
}
+ // Handle all events.
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
@@ -252,11 +296,6 @@
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
} else {
- if (! acquiredLock) {
- mLock.lock();
- acquiredLock = true;
- }
-
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
@@ -271,9 +310,6 @@
}
}
}
- if (acquiredLock) {
- mLock.unlock();
- }
Done: ;
#else
for (size_t i = 0; i < requestedCount; i++) {
@@ -301,15 +337,12 @@
}
}
}
-
Done:
// Set mPolling to false and wake up the wakeAndLock() waiters.
- mLock.lock();
mPolling = false;
if (mWaiters != 0) {
mAwake.broadcast();
}
- mLock.unlock();
#endif
#ifdef LOOPER_STATISTICS
@@ -335,19 +368,59 @@
}
#endif
+ // Invoke pending message callbacks.
+ mNextMessageUptime = LLONG_MAX;
+ while (mMessageEnvelopes.size() != 0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
+ if (messageEnvelope.uptime <= now) {
+ // Remove the envelope from the list.
+ // We keep a strong reference to the handler until the call to handleMessage
+ // finishes. Then we drop it so that the handler can be deleted *before*
+ // we reacquire our lock.
+ { // obtain handler
+ sp<MessageHandler> handler = messageEnvelope.handler;
+ Message message = messageEnvelope.message;
+ mMessageEnvelopes.removeAt(0);
+ mSendingMessage = true;
+ mLock.unlock();
+
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+ LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
+ this, handler.get(), message.what);
+#endif
+ handler->handleMessage(message);
+ } // release handler
+
+ mLock.lock();
+ mSendingMessage = false;
+ result = ALOOPER_POLL_CALLBACK;
+ } else {
+ // The last message left at the head of the queue determines the next wakeup time.
+ mNextMessageUptime = messageEnvelope.uptime;
+ break;
+ }
+ }
+
+ // Release lock.
+ mLock.unlock();
+
+ // Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) {
const Response& response = mResponses.itemAt(i);
- if (response.request.callback) {
+ ALooper_callbackFunc callback = response.request.callback;
+ if (callback) {
+ int fd = response.request.fd;
+ int events = response.events;
+ void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
- LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this,
- response.request.fd, response.events, response.request.data);
+ LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
+ this, callback, fd, events, data);
#endif
- int callbackResult = response.request.callback(
- response.request.fd, response.events, response.request.data);
+ int callbackResult = callback(fd, events, data);
if (callbackResult == 0) {
- removeFd(response.request.fd);
+ removeFd(fd);
}
-
result = ALOOPER_POLL_CALLBACK;
}
}
@@ -593,4 +666,83 @@
}
#endif
+void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
+ sendMessageAtTime(LLONG_MIN, handler, message);
+}
+
+void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+ const Message& message) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sendMessageAtTime(now + uptimeDelay, handler, message);
+}
+
+void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+ const Message& message) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d",
+ this, uptime, handler.get(), message.what);
+#endif
+
+ size_t i = 0;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ size_t messageCount = mMessageEnvelopes.size();
+ while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
+ i += 1;
+ }
+
+ MessageEnvelope messageEnvelope(uptime, handler, message);
+ mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
+
+ // Optimization: If the Looper is currently sending a message, then we can skip
+ // the call to wake() because the next thing the Looper will do after processing
+ // messages is to decide when the next wakeup time should be. In fact, it does
+ // not even matter whether this code is running on the Looper thread.
+ if (mSendingMessage) {
+ return;
+ }
+ } // release lock
+
+ // Wake the poll loop only when we enqueue a new message at the head.
+ if (i == 0) {
+ wake();
+ }
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ removeMessages - handler=%p", this, handler.get());
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+ if (messageEnvelope.handler == handler) {
+ mMessageEnvelopes.removeAt(i);
+ }
+ }
+ } // release lock
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler, int what) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+ const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+ if (messageEnvelope.handler == handler
+ && messageEnvelope.message.what == what) {
+ mMessageEnvelopes.removeAt(i);
+ }
+ }
+ } // release lock
+}
+
} // namespace android
diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp
index cea1313..8bf2ba2 100644
--- a/libs/utils/tests/Looper_test.cpp
+++ b/libs/utils/tests/Looper_test.cpp
@@ -16,6 +16,13 @@
namespace android {
+enum {
+ MSG_TEST1 = 1,
+ MSG_TEST2 = 2,
+ MSG_TEST3 = 3,
+ MSG_TEST4 = 4,
+};
+
class DelayedWake : public DelayedTask {
sp<Looper> mLooper;
@@ -82,6 +89,15 @@
}
};
+class StubMessageHandler : public MessageHandler {
+public:
+ Vector<Message> messages;
+
+ virtual void handleMessage(const Message& message) {
+ messages.push(message);
+ }
+};
+
class LooperTest : public testing::Test {
protected:
sp<Looper> mLooper;
@@ -421,5 +437,257 @@
<< "replacement handler callback should be invoked";
}
+TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) {
+ sp<StubMessageHandler> handler1 = new StubMessageHandler();
+ sp<StubMessageHandler> handler2 = new StubMessageHandler();
+ mLooper->sendMessage(handler1, Message(MSG_TEST1));
+ mLooper->sendMessage(handler2, Message(MSG_TEST2));
+ mLooper->sendMessage(handler1, Message(MSG_TEST3));
+ mLooper->sendMessage(handler1, Message(MSG_TEST4));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(3), handler1->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST3, handler1->messages[1].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST4, handler1->messages[2].what)
+ << "handled message";
+ EXPECT_EQ(size_t(1), handler2->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST2, handler2->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "first poll should end quickly because next message timeout was computed";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no message handled yet";
+
+ result = mLooper->pollOnce(1000);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "second poll should end around the time of the delayed message dispatch";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+
+ result = mLooper->pollOnce(100);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "third poll should timeout";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "first poll should end quickly because next message timeout was computed";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no message handled yet";
+
+ result = mLooper->pollOnce(1000);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "second poll should end around the time of the delayed message dispatch";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+
+ result = mLooper->pollOnce(100);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "third poll should timeout";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1));
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was already sent";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+ EXPECT_EQ(size_t(1), handler->messages.size())
+ << "handled message";
+ EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+ << "handled message";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+ mLooper->sendMessage(handler, Message(MSG_TEST2));
+ mLooper->sendMessage(handler, Message(MSG_TEST3));
+ mLooper->removeMessages(handler);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was sent so looper was awoken";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no messages to handle";
+
+ result = mLooper->pollOnce(0);
+
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+ EXPECT_EQ(size_t(0), handler->messages.size())
+ << "no messages to handle";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) {
+ sp<StubMessageHandler> handler = new StubMessageHandler();
+ mLooper->sendMessage(handler, Message(MSG_TEST1));
+ mLooper->sendMessage(handler, Message(MSG_TEST2));
+ mLooper->sendMessage(handler, Message(MSG_TEST3));
+ mLooper->sendMessage(handler, Message(MSG_TEST4));
+ mLooper->removeMessages(handler, MSG_TEST3);
+ mLooper->removeMessages(handler, MSG_TEST1);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because message was sent so looper was awoken";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent";
+ EXPECT_EQ(size_t(2), handler->messages.size())
+ << "no messages to handle";
+ EXPECT_EQ(MSG_TEST2, handler->messages[0].what)
+ << "handled message";
+ EXPECT_EQ(MSG_TEST4, handler->messages[1].what)
+ << "handled message";
+
+ result = mLooper->pollOnce(0);
+
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+ EXPECT_EQ(size_t(2), handler->messages.size())
+ << "no more messages to handle";
+}
} // namespace android
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
index 8b0250a..d443b7c 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
@@ -121,9 +121,11 @@
uint32 temp;
/*
- * Verify that at least the header is complete
+ * Verify that at least the header is complete
+ * Note that SYNC_WORD_LNGTH is in unit of bits, but inputBufferCurrentLength
+ * is in unit of bytes.
*/
- if (inputStream->inputBufferCurrentLength < (SYNC_WORD_LNGTH + 21))
+ if (inputStream->inputBufferCurrentLength < ((SYNC_WORD_LNGTH + 21) >> 3))
{
return NO_ENOUGH_MAIN_DATA_ERROR;
}
diff --git a/native/include/android/looper.h b/native/include/android/looper.h
index a9d8426..24e3967 100644
--- a/native/include/android/looper.h
+++ b/native/include/android/looper.h
@@ -148,7 +148,8 @@
/**
* For callback-based event loops, this is the prototype of the function
- * that is called. It is given the file descriptor it is associated with,
+ * that is called when a file descriptor event occurs.
+ * It is given the file descriptor it is associated with,
* a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT),
* and the data pointer that was originally supplied.
*
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index bfc80db..74be4e0 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -122,12 +122,12 @@
// request routing change if necessary
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
+ checkA2dpSuspend();
checkOutputForAllStrategies();
// A2DP outputs must be closed after checkOutputForAllStrategies() is executed
if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
closeA2dpOutputs();
}
- checkA2dpSuspend();
#endif
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
@@ -269,8 +269,8 @@
// check for device and output changes triggered by new phone state
newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
- checkOutputForAllStrategies();
checkA2dpSuspend();
+ checkOutputForAllStrategies();
#endif
updateDeviceForStrategy();
@@ -378,8 +378,8 @@
// check for device and output changes triggered by new phone state
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
- checkOutputForAllStrategies();
checkA2dpSuspend();
+ checkOutputForAllStrategies();
#endif
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
@@ -1624,7 +1624,7 @@
if (device) break;
#ifdef WITH_A2DP
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
- if (!isInCall()) {
+ if (!isInCall() && !mA2dpSuspended) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
@@ -1647,7 +1647,7 @@
#ifdef WITH_A2DP
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
// A2DP speaker when forcing to speaker output
- if (!isInCall()) {
+ if (!isInCall() && !mA2dpSuspended) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
if (device) break;
}
@@ -1687,7 +1687,7 @@
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
#ifdef WITH_A2DP
- if ((mA2dpOutput != 0) &&
+ if ((mA2dpOutput != 0) && !mA2dpSuspended &&
(strategy != STRATEGY_SONIFICATION || a2dpUsedForSonification())) {
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index a865d9f..c3c143c 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -399,6 +399,17 @@
}
}
+void InputReader::fadePointer() {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ device->fadePointer();
+ }
+ } // release device registry reader lock
+}
+
void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
{ // acquire state lock
AutoMutex _l(mStateLock);
@@ -695,6 +706,14 @@
return result;
}
+void InputDevice::fadePointer() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->fadePointer();
+ }
+}
+
// --- InputMapper ---
@@ -739,6 +758,9 @@
return 0;
}
+void InputMapper::fadePointer() {
+}
+
void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
const RawAbsoluteAxisInfo& axis, const char* name) {
if (axis.valid) {
@@ -967,6 +989,10 @@
getContext()->updateGlobalMetaState();
}
+ if (down && !isMetaKey(keyCode)) {
+ getContext()->fadePointer();
+ }
+
if (policyFlags & POLICY_FLAG_FUNCTION) {
newMetaState |= AMETA_FUNCTION_ON;
}
@@ -1348,6 +1374,9 @@
} else {
hscroll = 0;
}
+ if (hscroll != 0 || vscroll != 0) {
+ mPointerController->unfade();
+ }
} // release lock
int32_t metaState = mContext->getGlobalMetaState();
@@ -1376,6 +1405,13 @@
}
}
+void CursorInputMapper::fadePointer() {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ mPointerController->fade();
+ } // release lock
+}
+
// --- TouchInputMapper ---
@@ -2275,7 +2311,6 @@
uint32_t policyFlags = 0;
// Preprocess pointer data.
-
if (mParameters.useBadTouchFilter) {
if (applyBadTouchFilter()) {
havePointerIds = false;
@@ -2303,8 +2338,12 @@
savedTouch = & mCurrentTouch;
}
- // Process touches and virtual keys.
+ // Hide the pointer on an initial down.
+ if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
+ getContext()->fadePointer();
+ }
+ // Process touches and virtual keys.
TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
if (touchResult == DISPATCH_TOUCH) {
detectGestures(when);
@@ -2312,7 +2351,6 @@
}
// Copy current touch to last touch in preparation for the next cycle.
-
if (touchResult == DROP_STROKE) {
mLastTouch.clear();
} else {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index cf41535..b344ffe 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -157,6 +157,8 @@
virtual bool shouldDropVirtualKey(nsecs_t now,
InputDevice* device, int32_t keyCode, int32_t scanCode) = 0;
+ virtual void fadePointer() = 0;
+
virtual InputReaderPolicyInterface* getPolicy() = 0;
virtual InputDispatcherInterface* getDispatcher() = 0;
virtual EventHubInterface* getEventHub() = 0;
@@ -241,6 +243,8 @@
virtual void updateGlobalMetaState();
virtual int32_t getGlobalMetaState();
+ virtual void fadePointer();
+
InputConfiguration mInputConfiguration;
void updateInputConfiguration();
@@ -299,6 +303,8 @@
int32_t getMetaState();
+ void fadePointer();
+
inline const PropertyMap& getConfiguration() {
return mConfiguration;
}
@@ -351,6 +357,8 @@
virtual int32_t getMetaState();
+ virtual void fadePointer();
+
protected:
InputDevice* mDevice;
InputReaderContext* mContext;
@@ -459,6 +467,8 @@
virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual void fadePointer();
+
private:
// Amount that trackball needs to move in order to generate a key event.
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index 92af51e..954872b 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -35,8 +35,22 @@
// --- PointerController ---
-PointerController::PointerController(int32_t pointerLayer) :
- mPointerLayer(pointerLayer) {
+// Time to wait before starting the fade when the pointer is inactive.
+static const nsecs_t INACTIVITY_FADE_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
+static const nsecs_t INACTIVITY_FADE_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
+
+// Time to spend fading out the pointer completely.
+static const nsecs_t FADE_DURATION = 500 * 1000000LL; // 500 ms
+
+// Time to wait between frames.
+static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60;
+
+// Amount to subtract from alpha per frame.
+static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION;
+
+
+PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) :
+ mLooper(looper), mPointerLayer(pointerLayer) {
AutoMutex _l(mLock);
mLocked.displayWidth = -1;
@@ -51,12 +65,19 @@
mLocked.iconHotSpotX = 0;
mLocked.iconHotSpotY = 0;
+ mLocked.fadeAlpha = 1;
+ mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL;
+
mLocked.wantVisible = false;
mLocked.visible = false;
mLocked.drawn = false;
+
+ mHandler = new WeakMessageHandler(this);
}
PointerController::~PointerController() {
+ mLooper->removeMessages(mHandler);
+
if (mSurfaceControl != NULL) {
mSurfaceControl->clear();
mSurfaceControl.clear();
@@ -120,7 +141,7 @@
if (mLocked.buttonState != buttonState) {
mLocked.buttonState = buttonState;
- mLocked.wantVisible = true;
+ unfadeBeforeUpdateLocked();
updateLocked();
}
}
@@ -157,7 +178,7 @@
} else {
mLocked.pointerY = y;
}
- mLocked.wantVisible = true;
+ unfadeBeforeUpdateLocked();
updateLocked();
}
}
@@ -169,6 +190,29 @@
*outY = mLocked.pointerY;
}
+void PointerController::fade() {
+ AutoMutex _l(mLock);
+
+ startFadeLocked();
+}
+
+void PointerController::unfade() {
+ AutoMutex _l(mLock);
+
+ if (unfadeBeforeUpdateLocked()) {
+ updateLocked();
+ }
+}
+
+void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay) {
+ AutoMutex _l(mLock);
+
+ if (mLocked.inactivityFadeDelay != inactivityFadeDelay) {
+ mLocked.inactivityFadeDelay = inactivityFadeDelay;
+ startInactivityFadeDelayLocked();
+ }
+}
+
void PointerController::updateLocked() {
bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap;
@@ -201,6 +245,12 @@
goto CloseTransaction;
}
+ status = mSurfaceControl->setAlpha(mLocked.fadeAlpha);
+ if (status) {
+ LOGE("Error %d setting pointer surface alpha.", status);
+ goto CloseTransaction;
+ }
+
if (!mLocked.visible) {
status = mSurfaceControl->setLayer(mPointerLayer);
if (status) {
@@ -412,4 +462,64 @@
return true;
}
+void PointerController::handleMessage(const Message& message) {
+ switch (message.what) {
+ case MSG_FADE_STEP: {
+ AutoMutex _l(mLock);
+ fadeStepLocked();
+ break;
+ }
+ }
+}
+
+bool PointerController::unfadeBeforeUpdateLocked() {
+ sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+
+ if (isFadingLocked()) {
+ mLocked.wantVisible = true;
+ mLocked.fadeAlpha = 1;
+ return true; // update required to effect the unfade
+ }
+ return false; // update not required
+}
+
+void PointerController::startFadeLocked() {
+ if (!isFadingLocked()) {
+ sendFadeStepMessageDelayedLocked(0);
+ }
+}
+
+void PointerController::startInactivityFadeDelayLocked() {
+ if (!isFadingLocked()) {
+ sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+ }
+}
+
+void PointerController::fadeStepLocked() {
+ if (mLocked.wantVisible) {
+ mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME;
+ if (mLocked.fadeAlpha < 0) {
+ mLocked.fadeAlpha = 0;
+ mLocked.wantVisible = false;
+ } else {
+ sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL);
+ }
+ updateLocked();
+ }
+}
+
+bool PointerController::isFadingLocked() {
+ return !mLocked.wantVisible || mLocked.fadeAlpha != 1;
+}
+
+nsecs_t PointerController::getInactivityFadeDelayTimeLocked() {
+ return mLocked.inactivityFadeDelay == INACTIVITY_FADE_DELAY_SHORT
+ ? INACTIVITY_FADE_DELAY_TIME_SHORT : INACTIVITY_FADE_DELAY_TIME_NORMAL;
+}
+
+void PointerController::sendFadeStepMessageDelayedLocked(nsecs_t delayTime) {
+ mLooper->removeMessages(mHandler, MSG_FADE_STEP);
+ mLooper->sendMessageDelayed(delayTime, mHandler, Message(MSG_FADE_STEP));
+}
+
} // namespace android
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index a2a9955..e28dd7d 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -18,7 +18,9 @@
#define _UI_POINTER_CONTROLLER_H
#include <ui/DisplayInfo.h>
+#include <ui/Input.h>
#include <utils/RefBase.h>
+#include <utils/Looper.h>
#include <utils/String8.h>
#include <surfaceflinger/Surface.h>
@@ -64,6 +66,12 @@
/* Gets the absolute location of the pointer. */
virtual void getPosition(float* outX, float* outY) const = 0;
+
+ /* Fades the pointer out now. */
+ virtual void fade() = 0;
+
+ /* Makes the pointer visible if it has faded out. */
+ virtual void unfade() = 0;
};
@@ -72,12 +80,17 @@
*
* Handles pointer acceleration and animation.
*/
-class PointerController : public PointerControllerInterface {
+class PointerController : public PointerControllerInterface, public MessageHandler {
protected:
virtual ~PointerController();
public:
- PointerController(int32_t pointerLayer);
+ enum InactivityFadeDelay {
+ INACTIVITY_FADE_DELAY_NORMAL = 0,
+ INACTIVITY_FADE_DELAY_SHORT = 1,
+ };
+
+ PointerController(const sp<Looper>& looper, int32_t pointerLayer);
virtual bool getBounds(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const;
@@ -86,14 +99,22 @@
virtual uint32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
+ virtual void fade();
+ virtual void unfade();
void setDisplaySize(int32_t width, int32_t height);
void setDisplayOrientation(int32_t orientation);
void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
+ void setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay);
private:
+ enum {
+ MSG_FADE_STEP = 0,
+ };
+
mutable Mutex mLock;
+ sp<Looper> mLooper;
int32_t mPointerLayer;
sp<SurfaceComposerClient> mSurfaceComposerClient;
sp<SurfaceControl> mSurfaceControl;
@@ -111,17 +132,31 @@
float iconHotSpotX;
float iconHotSpotY;
+ float fadeAlpha;
+ InactivityFadeDelay inactivityFadeDelay;
+
bool wantVisible;
bool visible;
bool drawn;
} mLocked;
+ sp<WeakMessageHandler> mHandler;
+
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
void updateLocked();
bool createSurfaceIfNeededLocked();
bool drawPointerIfNeededLocked();
bool resizeSurfaceLocked(int32_t width, int32_t height);
+
+ void handleMessage(const Message& message);
+ bool unfadeBeforeUpdateLocked();
+ void startFadeLocked();
+ void startInactivityFadeDelayLocked();
+ void fadeStepLocked();
+ bool isFadingLocked();
+ nsecs_t getInactivityFadeDelayTimeLocked();
+ void sendFadeStepMessageDelayedLocked(nsecs_t delayTime);
};
} // namespace android
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index fac71bb..864241e 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -79,6 +79,12 @@
*outX = 0;
*outY = 0;
}
+
+ virtual void fade() {
+ }
+
+ virtual void unfade() {
+ }
};
@@ -743,6 +749,9 @@
InputDevice* device, int32_t keyCode, int32_t scanCode) {
return false;
}
+
+ virtual void fadePointer() {
+ }
};
@@ -875,6 +884,9 @@
virtual int32_t getMetaState() {
return mMetaState;
}
+
+ virtual void fadePointer() {
+ }
};
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 52f84eb..15f22c0 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -26,6 +26,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.net.SntpClient;
import android.os.Handler;
import android.os.HandlerThread;
@@ -58,6 +60,7 @@
private static final int EVENT_AUTO_TIME_CHANGED = 1;
private static final int EVENT_POLL_NETWORK_TIME = 2;
+ private static final int EVENT_WIFI_CONNECTED = 3;
/** Normal polling frequency */
private static final long POLLING_INTERVAL_MS = 24L * 60 * 60 * 1000; // 24 hrs
@@ -113,6 +116,7 @@
registerForTelephonyIntents();
registerForAlarms();
+ registerForConnectivityIntents();
mThread = new HandlerThread(TAG);
mThread.start();
@@ -162,6 +166,12 @@
}, new IntentFilter(ACTION_POLL));
}
+ private void registerForConnectivityIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiver(mConnectivityReceiver, intentFilter);
+ }
+
private void onPollNetworkTime(int event) {
// If Automatic time is not set, don't bother.
if (!isAutomaticTimeRequested()) return;
@@ -253,6 +263,27 @@
}
};
+ /** Receiver for ConnectivityManager events */
+ private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
+ // There is connectivity
+ NetworkInfo netInfo = (NetworkInfo)intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ if (netInfo != null) {
+ // Verify that it's a WIFI connection
+ if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
+ netInfo.getType() == ConnectivityManager.TYPE_WIFI ) {
+ mHandler.obtainMessage(EVENT_WIFI_CONNECTED).sendToTarget();
+ }
+ }
+ }
+ }
+ };
+
/** Handler to do the network accesses on */
private class MyHandler extends Handler {
@@ -265,6 +296,7 @@
switch (msg.what) {
case EVENT_AUTO_TIME_CHANGED:
case EVENT_POLL_NETWORK_TIME:
+ case EVENT_WIFI_CONNECTED:
onPollNetworkTime(msg.what);
break;
}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 80a2a96..326eca7 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -19,7 +19,6 @@
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -31,6 +30,8 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Environment;
+import android.os.Looper;
+import android.os.MessageQueue;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.Xml;
@@ -41,13 +42,10 @@
import android.view.Surface;
import android.view.WindowManager;
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -63,7 +61,7 @@
private final Context mContext;
private final WindowManagerService mWindowManagerService;
- private static native void nativeInit(Callbacks callbacks);
+ private static native void nativeInit(Callbacks callbacks, MessageQueue messageQueue);
private static native void nativeStart();
private static native void nativeSetDisplaySize(int displayId, int width, int height);
private static native void nativeSetDisplayOrientation(int displayId, int rotation);
@@ -83,6 +81,7 @@
int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+ private static native void nativeSetSystemUiVisibility(int visibility);
private static native void nativeSetFocusedApplication(InputApplication application);
private static native InputDevice nativeGetInputDevice(int deviceId);
private static native void nativeGetInputConfiguration(Configuration configuration);
@@ -120,15 +119,12 @@
public InputManager(Context context, WindowManagerService windowManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
-
this.mCallbacks = new Callbacks();
-
- init();
- }
-
- private void init() {
+
+ Looper looper = windowManagerService.mH.getLooper();
+
Slog.i(TAG, "Initializing input manager");
- nativeInit(mCallbacks);
+ nativeInit(mCallbacks, looper.getQueue());
}
public void start() {
@@ -338,7 +334,11 @@
public void setInputDispatchMode(boolean enabled, boolean frozen) {
nativeSetInputDispatchMode(enabled, frozen);
}
-
+
+ public void setSystemUiVisibility(int visibility) {
+ nativeSetSystemUiVisibility(visibility);
+ }
+
/**
* Atomically transfers touch focus from one window to another as identified by
* their input channels. It is possible for multiple windows to have
@@ -361,7 +361,7 @@
}
return nativeTransferTouchFocus(fromChannel, toChannel);
}
-
+
public void dump(PrintWriter pw) {
String dumpStr = nativeDump();
if (dumpStr != null) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e3218c8..15269dc 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -8412,6 +8412,7 @@
@Override
public void statusBarVisibilityChanged(int visibility) {
+ mInputManager.setSystemUiVisibility(visibility);
synchronized (mWindowMap) {
final int N = mWindows.size();
for (int i = 0; i < N; i++) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0a50ff8..5ef234a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -31,11 +31,13 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
+#include <utils/Looper.h>
#include <utils/threads.h>
#include <input/InputManager.h>
#include <input/PointerController.h>
+#include <android_os_MessageQueue.h>
#include <android_view_KeyEvent.h>
#include <android_view_MotionEvent.h>
#include <android_view_InputChannel.h>
@@ -136,7 +138,7 @@
virtual ~NativeInputManager();
public:
- NativeInputManager(jobject callbacksObj);
+ NativeInputManager(jobject callbacksObj, const sp<Looper>& looper);
inline sp<InputManager> getInputManager() const { return mInputManager; }
@@ -152,6 +154,7 @@
void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
void setFocusedApplication(JNIEnv* env, jobject applicationObj);
void setInputDispatchMode(bool enabled, bool frozen);
+ void setSystemUiVisibility(int32_t visibility);
/* --- InputReaderPolicyInterface implementation --- */
@@ -188,6 +191,7 @@
sp<InputManager> mInputManager;
jobject mCallbacksObj;
+ sp<Looper> mLooper;
// Cached filtering policies.
int32_t mFilterTouchEvents;
@@ -203,10 +207,15 @@
int32_t displayWidth, displayHeight; // -1 when initialized
int32_t displayOrientation;
+ // System UI visibility.
+ int32_t systemUiVisibility;
+
// Pointer controller singleton, created and destroyed as needed.
wp<PointerController> pointerController;
} mLocked;
+ void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
+
// Power manager interactions.
bool isScreenOn();
bool isScreenBright();
@@ -220,9 +229,10 @@
-NativeInputManager::NativeInputManager(jobject callbacksObj) :
- mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
- mMaxEventsPerSecond(-1) {
+NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& looper) :
+ mLooper(looper),
+ mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
+ mMaxEventsPerSecond(-1) {
JNIEnv* env = jniEnv();
mCallbacksObj = env->NewGlobalRef(callbacksObj);
@@ -232,6 +242,8 @@
mLocked.displayWidth = -1;
mLocked.displayHeight = -1;
mLocked.displayOrientation = ROTATION_0;
+
+ mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
}
sp<EventHub> eventHub = new EventHub();
@@ -408,7 +420,7 @@
layer = -1;
}
- controller = new PointerController(layer);
+ controller = new PointerController(mLooper, layer);
mLocked.pointerController = controller;
controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
@@ -428,6 +440,8 @@
}
env->DeleteLocalRef(iconObj);
}
+
+ updateInactivityFadeDelayLocked(controller);
}
return controller;
}
@@ -571,6 +585,26 @@
mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
}
+void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
+ AutoMutex _l(mLock);
+
+ if (mLocked.systemUiVisibility != visibility) {
+ mLocked.systemUiVisibility = visibility;
+
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != NULL) {
+ updateInactivityFadeDelayLocked(controller);
+ }
+ }
+}
+
+void NativeInputManager::updateInactivityFadeDelayLocked(const sp<PointerController>& controller) {
+ bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
+ controller->setInactivityFadeDelay(lightsOut
+ ? PointerController::INACTIVITY_FADE_DELAY_SHORT
+ : PointerController::INACTIVITY_FADE_DELAY_NORMAL);
+}
+
bool NativeInputManager::isScreenOn() {
return android_server_PowerManagerService_isScreenOn();
}
@@ -751,9 +785,10 @@
}
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
- jobject callbacks) {
+ jobject callbacks, jobject messageQueueObj) {
if (gNativeInputManager == NULL) {
- gNativeInputManager = new NativeInputManager(callbacks);
+ sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
+ gNativeInputManager = new NativeInputManager(callbacks, looper);
} else {
LOGE("Input manager already initialized.");
jniThrowRuntimeException(env, "Input manager already initialized.");
@@ -972,6 +1007,15 @@
gNativeInputManager->setInputDispatchMode(enabled, frozen);
}
+static void android_server_InputManager_nativeSetSystemUiVisibility(JNIEnv* env,
+ jclass clazz, jint visibility) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setSystemUiVisibility(visibility);
+}
+
static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env,
jclass clazz, jint deviceId) {
if (checkInputManagerUnitialized(env)) {
@@ -1079,7 +1123,7 @@
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
- { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;)V",
+ { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
(void*) android_server_InputManager_nativeInit },
{ "nativeStart", "()V",
(void*) android_server_InputManager_nativeStart },
@@ -1108,6 +1152,8 @@
(void*) android_server_InputManager_nativeSetFocusedApplication },
{ "nativeSetInputDispatchMode", "(ZZ)V",
(void*) android_server_InputManager_nativeSetInputDispatchMode },
+ { "nativeSetSystemUiVisibility", "(I)V",
+ (void*) android_server_InputManager_nativeSetSystemUiVisibility },
{ "nativeGetInputDevice", "(I)Landroid/view/InputDevice;",
(void*) android_server_InputManager_nativeGetInputDevice },
{ "nativeGetInputDeviceIds", "()[I",
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index a6b54d8..20c8969 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -16,41 +16,47 @@
package android.net.rtp;
+import android.media.AudioManager;
+
import java.util.HashMap;
import java.util.Map;
/**
- * An AudioGroup acts as a router connected to the speaker, the microphone, and
- * {@link AudioStream}s. Its execution loop consists of four steps. First, for
- * each AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its
- * incoming packets and stores in its buffer. Then, if the microphone is
- * enabled, processes the recorded audio and stores in its buffer. Third, if the
- * speaker is enabled, mixes and playbacks buffers of all AudioStreams. Finally,
- * for each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all
- * other buffers and sends back the encoded packets. An AudioGroup does nothing
- * if there is no AudioStream in it.
+ * An AudioGroup is an audio hub for the speaker, the microphone, and
+ * {@link AudioStream}s. Each of these components can be logically turned on
+ * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}.
+ * The AudioGroup will go through these components and process them one by one
+ * within its execution loop. The loop consists of four steps. First, for each
+ * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
+ * packets and stores in its buffer. Then, if the microphone is enabled,
+ * processes the recorded audio and stores in its buffer. Third, if the speaker
+ * is enabled, mixes all AudioStream buffers and plays back. Finally, for each
+ * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
+ * buffers and sends back the encoded packets. An AudioGroup does nothing if
+ * there is no AudioStream in it.
*
* <p>Few things must be noticed before using these classes. The performance is
* highly related to the system load and the network bandwidth. Usually a
* simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
- * bandwidth, and vise versa. Using two AudioStreams at the same time not only
- * doubles the load but also the bandwidth. The condition varies from one device
- * to another, and developers must choose the right combination in order to get
- * the best result.
+ * bandwidth, and vise versa. Using two AudioStreams at the same time doubles
+ * not only the load but also the bandwidth. The condition varies from one
+ * device to another, and developers should choose the right combination in
+ * order to get the best result.</p>
*
* <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
* example, a Voice over IP (VoIP) application might want to put a conference
* call on hold in order to make a new call but still allow people in the
- * previous call to talk to each other. This can be done easily using two
+ * conference call talking to each other. This can be done easily using two
* AudioGroups, but there are some limitations. Since the speaker and the
- * microphone are shared globally, only one AudioGroup is allowed to run in
- * modes other than {@link #MODE_ON_HOLD}. In addition, before adding an
- * AudioStream into an AudioGroup, one should always put all other AudioGroups
- * into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly
- * initialized.</p>
+ * microphone are globally shared resources, only one AudioGroup at a time is
+ * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will
+ * be unable to acquire these resources and fail silently.</p>
*
* <p class="note">Using this class requires
- * {@link android.Manifest.permission#RECORD_AUDIO} permission.</p>
+ * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers
+ * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION}
+ * using {@link AudioManager#setMode(int)} and change it back when none of
+ * the AudioGroups is in use.</p>
*
* @see AudioStream
* @hide
@@ -58,13 +64,13 @@
public class AudioGroup {
/**
* This mode is similar to {@link #MODE_NORMAL} except the speaker and
- * the microphone are disabled.
+ * the microphone are both disabled.
*/
public static final int MODE_ON_HOLD = 0;
/**
* This mode is similar to {@link #MODE_NORMAL} except the microphone is
- * muted.
+ * disabled.
*/
public static final int MODE_MUTED = 1;
@@ -137,20 +143,18 @@
private native void nativeSetMode(int mode);
// Package-private method used by AudioStream.join().
- void add(AudioStream stream, AudioCodec codec, int dtmfType) {
- synchronized (this) {
- if (!mStreams.containsKey(stream)) {
- try {
- int socket = stream.dup();
- String codecSpec = String.format("%d %s %s", codec.type,
- codec.rtpmap, codec.fmtp);
- nativeAdd(stream.getMode(), socket,
- stream.getRemoteAddress().getHostAddress(),
- stream.getRemotePort(), codecSpec, dtmfType);
- mStreams.put(stream, socket);
- } catch (NullPointerException e) {
- throw new IllegalStateException(e);
- }
+ synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) {
+ if (!mStreams.containsKey(stream)) {
+ try {
+ int socket = stream.dup();
+ String codecSpec = String.format("%d %s %s", codec.type,
+ codec.rtpmap, codec.fmtp);
+ nativeAdd(stream.getMode(), socket,
+ stream.getRemoteAddress().getHostAddress(),
+ stream.getRemotePort(), codecSpec, dtmfType);
+ mStreams.put(stream, socket);
+ } catch (NullPointerException e) {
+ throw new IllegalStateException(e);
}
}
}
@@ -159,12 +163,10 @@
int remotePort, String codecSpec, int dtmfType);
// Package-private method used by AudioStream.join().
- void remove(AudioStream stream) {
- synchronized (this) {
- Integer socket = mStreams.remove(stream);
- if (socket != null) {
- nativeRemove(socket);
- }
+ synchronized void remove(AudioStream stream) {
+ Integer socket = mStreams.remove(stream);
+ if (socket != null) {
+ nativeRemove(socket);
}
}
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index 0edae6b..b45cc5e 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -27,8 +27,8 @@
* configured {@link AudioCodec}. On the other side, An {@link AudioGroup}
* represents a local endpoint which mixes all the AudioStreams and optionally
* interacts with the speaker and the microphone at the same time. The simplest
- * usage includes one for each endpoints. For other combinations, users should
- * be aware of the limitations described in {@link AudioGroup}.
+ * usage includes one for each endpoints. For other combinations, developers
+ * should be aware of the limitations described in {@link AudioGroup}.
*
* <p>An AudioStream becomes busy when it joins an AudioGroup. In this case most
* of the setter methods are disabled. This is designed to ease the task of
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 169b2d6..6b79210 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -152,6 +152,26 @@
return state != UNINITIALIZED && state != INVALID;
}
+ static boolean isConnecting(SupplicantState state) {
+ switch(state) {
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ return true;
+ case DISCONNECTED:
+ case INACTIVE:
+ case SCANNING:
+ case DORMANT:
+ case UNINITIALIZED:
+ case INVALID:
+ return false;
+ default:
+ throw new IllegalArgumentException("Unknown supplicant state");
+ }
+ }
+
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
return 0;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index e89858c9..589d88c 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -344,6 +344,9 @@
*/
private static final long DEFAULT_SCAN_INTERVAL_MS = 60 * 1000; /* 1 minute */
+ private static final int MIN_RSSI = -200;
+ private static final int MAX_RSSI = 256;
+
/* Default parent state */
private HierarchicalState mDefaultState = new DefaultState();
/* Temporary initial state */
@@ -1239,7 +1242,7 @@
*/
private void fetchRssiAndLinkSpeedNative() {
int newRssi = WifiNative.getRssiCommand();
- if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+ if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
/* some implementations avoid negative values by adding 256
* so we need to adjust for that here.
*/
@@ -1264,7 +1267,7 @@
}
mLastSignalLevel = newSignalLevel;
} else {
- mWifiInfo.setRssi(-200);
+ mWifiInfo.setRssi(MIN_RSSI);
}
int newLinkSpeed = WifiNative.getLinkSpeedCommand();
if (newLinkSpeed != -1) {
@@ -1386,15 +1389,17 @@
/* Disable interface */
NetworkUtils.disableInterface(mInterfaceName);
- /* send event to CM & network change broadcast */
- setNetworkDetailedState(DetailedState.DISCONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
/* Reset data structures */
mWifiInfo.setInetAddress(null);
mWifiInfo.setBSSID(null);
mWifiInfo.setSSID(null);
mWifiInfo.setNetworkId(-1);
+ mWifiInfo.setRssi(MIN_RSSI);
+ mWifiInfo.setLinkSpeed(-1);
+
+ /* send event to CM & network change broadcast */
+ setNetworkDetailedState(DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
/* Clear network properties */
mLinkProperties.clear();
@@ -2363,7 +2368,10 @@
// 50023 supplicant_state_changed (custom|1|5)
EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
mWifiInfo.setSupplicantState(state);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ // Network id is only valid when we start connecting
+ if (SupplicantState.isConnecting(state)) {
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ }
if (state == SupplicantState.ASSOCIATING) {
/* BSSID is valid only in ASSOCIATING state */
mWifiInfo.setBSSID(stateChangeResult.BSSID);
@@ -2741,6 +2749,15 @@
}
return HANDLED;
}
+ @Override
+ public void exit() {
+ /* If a scan result is pending in connected state, the supplicant
+ * is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
+ */
+ if (mScanResultIsPending) {
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ }
+ }
}
class DisconnectingState extends HierarchicalState {