diff options
38 files changed, 2491 insertions, 667 deletions
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 17f0a97..b7d20b4 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1884,6 +1884,7 @@ final class WebViewCore { int mScrollX; int mScrollY; boolean mMobileSite; + boolean mIsRestored; } static class DrawData { @@ -2285,6 +2286,7 @@ final class WebViewCore { mInitialViewState.mScrollY = mRestoredY; mInitialViewState.mMobileSite = (0 == mViewportWidth); if (mRestoredScale > 0) { + mInitialViewState.mIsRestored = true; mInitialViewState.mViewScale = mRestoredScale / 100.0f; if (mRestoredTextWrapScale > 0) { mInitialViewState.mTextWrapScale = mRestoredTextWrapScale / 100.0f; diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 69db6b2..b94dc3b 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -260,12 +260,7 @@ class ZoomManager { public final float getReadingLevelScale() { // The reading scale is at least 0.5f apart from the overview scale. final float MIN_SCALE_DIFF = 0.5f; - final float zoomOverviewScale = getZoomOverviewScale(); - if (zoomOverviewScale > DEFAULT_READING_LEVEL_SCALE) { - return Math.min(DEFAULT_READING_LEVEL_SCALE, - zoomOverviewScale - MIN_SCALE_DIFF); - } - return Math.max(zoomOverviewScale + MIN_SCALE_DIFF, + return Math.max(getZoomOverviewScale() + MIN_SCALE_DIFF, DEFAULT_READING_LEVEL_SCALE); } @@ -864,32 +859,33 @@ class ZoomManager { if (!mWebView.drawHistory()) { float scale; - final boolean reflowText; - WebSettings settings = mWebView.getSettings(); + final float overviewScale = getZoomOverviewScale(); if (mInitialScale > 0) { scale = mInitialScale; - reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale); } else if (viewState.mViewScale > 0) { mTextWrapScale = viewState.mTextWrapScale; scale = viewState.mViewScale; - reflowText = false; } else { - scale = getZoomOverviewScale(); - if (settings.getUseWideViewPort() - && settings.getLoadWithOverviewMode()) { - mInitialZoomOverview = true; - } else { + scale = overviewScale; + WebSettings settings = mWebView.getSettings(); + if (!settings.getUseWideViewPort() + || !settings.getLoadWithOverviewMode()) { scale = Math.max(viewState.mTextWrapScale, scale); - mInitialZoomOverview = !exceedsMinScaleIncrement(scale, getZoomOverviewScale()); } if (settings.isNarrowColumnLayout() && settings.getUseFixedViewport()) { // When first layout, reflow using the reading level scale to avoid // reflow when double tapped. mTextWrapScale = getReadingLevelScale(); } + } + boolean reflowText = false; + if (!viewState.mIsRestored) { + scale = Math.max(scale, overviewScale); + mTextWrapScale = Math.max(mTextWrapScale, overviewScale); reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale); } + mInitialZoomOverview = !exceedsMinScaleIncrement(scale, overviewScale); setZoomScale(scale, reflowText); // update the zoom buttons as the scale can be changed diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 88d3f7a..75ba704 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -7024,8 +7024,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener getInsertionController().show(); } } - } else if (hasSelection() && hasSelectionController()) { - getSelectionController().show(); } } @@ -7103,7 +7101,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener handled |= mMovement.onTouchEvent(this, (Spannable) mText, event); } - if (isTextEditable()) { + if (isTextEditable() || mTextIsSelectable) { if (mScrollX != oldScrollX || mScrollY != oldScrollY) { // Hide insertion anchor while scrolling. Leave selection. hideInsertionPointCursorController(); @@ -7153,7 +7151,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mInsertionControllerEnabled = windowSupportsHandles && isTextEditable() && mCursorVisible && - mLayout != null && !mTextIsSelectable; + mLayout != null; mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() && mLayout != null; @@ -7172,8 +7170,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * a selectable TextView. */ private boolean isTextEditable() { - return (mText instanceof Editable && onCheckIsTextEditor() && isEnabled()) - || mTextIsSelectable; + return mText instanceof Editable && onCheckIsTextEditor() && isEnabled(); } /** @@ -7748,7 +7745,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (clip != null) { - clipboard.setPrimaryClip(clip); + setPrimaryClip(clip); } } return true; @@ -7839,26 +7836,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } - if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) { + if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) && + mInsertionControllerEnabled) { + // Long press in empty space moves cursor and shows the Paste affordance if available. final int offset = getOffset(mLastDownPositionX, mLastDownPositionY); Selection.setSelection((Spannable)mText, offset); - if (canPaste()) { - getInsertionController().showWithPaste(); - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } else { - getInsertionController().show(); - } + getInsertionController().show(0); mEatTouchRelease = true; return true; } - if (mSelectionActionMode != null && touchPositionIsInSelection()) { - final int start = getSelectionStart(); - final int end = getSelectionEnd(); - CharSequence selectedText = mTransformed.subSequence(start, end); - ClipData data = ClipData.newPlainText(null, null, selectedText); - startDrag(data, getTextThumbnailBuilder(selectedText), false); - stopSelectionActionMode(); + if (mSelectionActionMode != null) { + if (touchPositionIsInSelection()) { + // Start a drag + final int start = getSelectionStart(); + final int end = getSelectionEnd(); + CharSequence selectedText = mTransformed.subSequence(start, end); + ClipData data = ClipData.newPlainText(null, null, selectedText); + startDrag(data, getTextThumbnailBuilder(selectedText), false); + stopSelectionActionMode(); + } else { + selectCurrentWord(); + getSelectionController().show(); + } performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); mEatTouchRelease = true; return true; @@ -7950,10 +7950,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Paste clipboard content between min and max positions. - * - * @param clipboard getSystemService(Context.CLIPBOARD_SERVICE) */ - private void paste(ClipboardManager clipboard, int min, int max) { + private void paste(int min, int max) { + ClipboardManager clipboard = + (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = clipboard.getPrimaryClip(); if (clip != null) { boolean didfirst = false; @@ -7973,9 +7973,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } stopSelectionActionMode(); + sLastCutOrCopyTime = 0; } } + private void setPrimaryClip(ClipData clip) { + ClipboardManager clipboard = (ClipboardManager) getContext(). + getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(clip); + sLastCutOrCopyTime = SystemClock.uptimeMillis(); + } + private class SelectionActionModeCallback implements ActionMode.Callback { @Override @@ -8061,9 +8069,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } - ClipboardManager clipboard = (ClipboardManager) getContext(). - getSystemService(Context.CLIPBOARD_SERVICE); - int min = 0; int max = mText.length(); @@ -8077,18 +8082,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (item.getItemId()) { case ID_PASTE: - paste(clipboard, min, max); + paste(min, max); return true; case ID_CUT: - clipboard.setPrimaryClip(ClipData.newPlainText(null, null, + setPrimaryClip(ClipData.newPlainText(null, null, mTransformed.subSequence(min, max))); ((Editable) mText).delete(min, max); stopSelectionActionMode(); return true; case ID_COPY: - clipboard.setPrimaryClip(ClipData.newPlainText(null, null, + setPrimaryClip(ClipData.newPlainText(null, null, mTransformed.subSequence(min, max))); stopSelectionActionMode(); return true; @@ -8211,9 +8216,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onClick(View v) { if (canPaste()) { - ClipboardManager clipboard = - (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); - paste(clipboard, getSelectionStart(), getSelectionEnd()); + paste(getSelectionStart(), getSelectionEnd()); } hide(); } @@ -8502,6 +8505,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } case MotionEvent.ACTION_UP: if (mPastePopupWindow != null) { + // Will show the paste popup after a delay. + mController.show(); + /* TEMP USER TEST: Display Paste as soon as handle is draggged long delay = SystemClock.uptimeMillis() - mTouchTimer; if (delay < ViewConfiguration.getTapTimeout()) { final float touchOffsetX = ev.getRawX() - mPositionX; @@ -8515,7 +8521,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (distanceSquared < slopSquared) { showPastePopupWindow(); } - } + }*/ } mIsDragging = false; break; @@ -8561,6 +8567,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private class InsertionPointCursorController implements CursorController { private static final int DELAY_BEFORE_FADE_OUT = 4100; + private static final int DELAY_BEFORE_PASTE = 2000; + private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // The cursor controller image. Lazily created. private HandleView mHandle; @@ -8571,14 +8579,27 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } }; + private final Runnable mPastePopupShower = new Runnable() { + public void run() { + getHandle().showPastePopupWindow(); + } + }; + public void show() { - updatePosition(); - getHandle().show(); + show(DELAY_BEFORE_PASTE); } - void showWithPaste() { - show(); - getHandle().showPastePopupWindow(); + public void show(int delayBeforePaste) { + updatePosition(); + hideDelayed(); + getHandle().show(); + removeCallbacks(mPastePopupShower); + if (canPaste()) { + final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime; + if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) + delayBeforePaste = 0; + postDelayed(mPastePopupShower, delayBeforePaste); + } } public void hide() { @@ -8586,6 +8607,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mHandle.hide(); } removeCallbacks(mHider); + removeCallbacks(mPastePopupShower); } private void hideDelayed() { @@ -8618,7 +8640,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } - // updatePosition is called only when isShowing. Handle has been created at this point. getHandle().positionAtCursor(offset, true); } @@ -8681,6 +8702,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEndHandle.show(); hideInsertionPointCursorController(); + hideDelayed(); } public void hide() { @@ -8735,6 +8757,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.setSelection((Spannable) mText, selectionStart, selectionEnd); updatePosition(); + hideDelayed(); } public void updatePosition() { @@ -8755,13 +8778,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // The handles have been created since the controller isShowing(). mStartHandle.positionAtCursor(selectionStart, true); mEndHandle.positionAtCursor(selectionEnd, true); - hideDelayed(); } public boolean onTouchEvent(MotionEvent event) { // This is done even when the View does not have focus, so that long presses can start // selection and tap can move cursor from this tap position. - if (isTextEditable()) { + if (isTextEditable() || mTextIsSelectable) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: final int x = (int) event.getX(); @@ -9143,4 +9165,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private InputFilter[] mFilters = NO_FILTERS; private static final Spanned EMPTY_SPANNED = new SpannedString(""); private static int DRAG_THUMBNAIL_MAX_TEXT_LENGTH = 20; + // System wide time for last cut or copy action. + private static long sLastCutOrCopyTime; } diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 9a85edc..206e320 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -32,6 +32,7 @@ #include <SkCanvas.h> #include <SkBitmap.h> #include <SkRegion.h> +#include <SkPixelRef.h> #include "jni.h" #include <android_runtime/AndroidRuntime.h> @@ -437,9 +438,15 @@ static void Surface_unfreezeDisplay( } } -class ScreenshotBitmap : public SkBitmap { +class ScreenshotPixelRef : public SkPixelRef { public: - ScreenshotBitmap() { + ScreenshotPixelRef(SkColorTable* ctable) { + fCTable = ctable; + ctable->safeRef(); + setImmutable(); + } + virtual ~ScreenshotPixelRef() { + SkSafeUnref(fCTable); } status_t update(int width, int height) { @@ -450,40 +457,71 @@ public: return res; } - void const* base = mScreenshot.getPixels(); - uint32_t w = mScreenshot.getWidth(); - uint32_t h = mScreenshot.getHeight(); - uint32_t s = mScreenshot.getStride(); - uint32_t f = mScreenshot.getFormat(); + return NO_ERROR; + } - ssize_t bpr = s * android::bytesPerPixel(f); - setConfig(convertPixelFormat(f), w, h, bpr); - if (f == PIXEL_FORMAT_RGBX_8888) { - setIsOpaque(true); - } - if (w > 0 && h > 0) { - setPixels((void*)base); - } else { - // be safe with an empty bitmap. - setPixels(NULL); - } + uint32_t getWidth() const { + return mScreenshot.getWidth(); + } - return NO_ERROR; + uint32_t getHeight() const { + return mScreenshot.getHeight(); + } + + uint32_t getStride() const { + return mScreenshot.getStride(); + } + + uint32_t getFormat() const { + return mScreenshot.getFormat(); + } + +protected: + // overrides from SkPixelRef + virtual void* onLockPixels(SkColorTable** ct) { + *ct = fCTable; + return (void*)mScreenshot.getPixels(); + } + + virtual void onUnlockPixels() { } private: ScreenshotClient mScreenshot; + SkColorTable* fCTable; + + typedef SkPixelRef INHERITED; }; static jobject Surface_screenshot(JNIEnv* env, jobject clazz, jint width, jint height) { - ScreenshotBitmap* bitmap = new ScreenshotBitmap(); - - if (bitmap->update(width, height) != NO_ERROR) { - delete bitmap; + ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL); + if (pixels->update(width, height) != NO_ERROR) { + delete pixels; return 0; } + uint32_t w = pixels->getWidth(); + uint32_t h = pixels->getHeight(); + uint32_t s = pixels->getStride(); + uint32_t f = pixels->getFormat(); + ssize_t bpr = s * android::bytesPerPixel(f); + + SkBitmap* bitmap = new SkBitmap(); + bitmap->setConfig(convertPixelFormat(f), w, h, bpr); + if (f == PIXEL_FORMAT_RGBX_8888) { + bitmap->setIsOpaque(true); + } + + if (w > 0 && h > 0) { + bitmap->setPixelRef(pixels)->unref(); + bitmap->lockPixels(); + } else { + // be safe with an empty bitmap. + delete pixels; + bitmap->setPixels(NULL); + } + return GraphicsJNI::createBitmap(env, bitmap, false, NULL); } diff --git a/data/keyboards/Apple_Wireless_Keyboard.kl b/data/keyboards/Apple_Wireless_Keyboard.kl new file mode 100644 index 0000000..9262a03 --- /dev/null +++ b/data/keyboards/Apple_Wireless_Keyboard.kl @@ -0,0 +1,119 @@ +# Copyright (C) 2010 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. + +# +# Apple Wireless Keyboard +# +# Notes: +# - Keys such as PAGE_UP and FORWARD_DEL are produced using the +# function key. +# - Special function keys for brightness control, etc. are not +# implemented here. + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 87 F11 +key 88 F12 +key 100 ALT_RIGHT +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 NUMPAD_ENTER +key 111 FORWARD_DEL +key 113 VALUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +# key 120 switch applications +key 125 META_LEFT +key 126 META_RIGHT +key 161 MEDIA_EJECT +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +# key 204 show gadgets +# key 224 reduce brightness +# key 225 increase brightness +# key 229 blank special function on F5 +# key 230 blank special function on F6 +key 464 FUNCTION diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index e98000d..1ee121e 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -175,7 +175,7 @@ key 150 EXPLORER # key 153 "KEY_DIRECTION" # key 154 "KEY_CYCLEWINDOWS" key 155 ENVELOPE -# key 156 "KEY_BOOKMARKS" +key 156 BOOKMARK # key 157 "KEY_COMPUTER" key 158 BACK WAKE_DROPPED key 159 FORWARD @@ -289,11 +289,11 @@ key 318 BUTTON_THUMBR # key 359 "KEY_TIME" # key 360 "KEY_VENDOR" # key 361 "KEY_ARCHIVE" -# key 362 "KEY_PROGRAM" +key 362 GUIDE # key 363 "KEY_CHANNEL" # key 364 "KEY_FAVORITES" # key 365 "KEY_EPG" -# key 366 "KEY_PVR" +key 366 DVR # key 367 "KEY_MHP" # key 368 "KEY_LANGUAGE" # key 369 "KEY_TITLE" @@ -304,7 +304,7 @@ key 318 BUTTON_THUMBR # key 374 "KEY_KEYBOARD" # key 375 "KEY_SCREEN" # key 376 "KEY_PC" -# key 377 "KEY_TV" +key 377 TV # key 378 "KEY_TV2" # key 379 "KEY_VCR" # key 380 "KEY_VCR2" @@ -329,8 +329,8 @@ key 318 BUTTON_THUMBR # key 399 "KEY_GREEN" # key 400 "KEY_YELLOW" # key 401 "KEY_BLUE" -# key 402 "KEY_CHANNELUP" -# key 403 "KEY_CHANNELDOWN" +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN # key 404 "KEY_FIRST" # key 405 "KEY_LAST" # key 406 "KEY_AB" diff --git a/data/keyboards/Logitech_USB_Receiver.kl b/data/keyboards/Logitech_USB_Receiver.kl new file mode 100644 index 0000000..23a8f54 --- /dev/null +++ b/data/keyboards/Logitech_USB_Receiver.kl @@ -0,0 +1,133 @@ +# Copyright (C) 2010 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. + +# +# Logitech Revue keyboard +# +# Notes: +# - The GRAVE key is emulated by the keyboard. +# ALT + LEFT_BRACKET produces GRAVE. +# ALT + RIGHT_BRACKET produces SHIFT + GRAVE. +# - FORWARD_DEL is produced by fn + BACKSPACE +# - PAGE_UP / PAGE_DOWN is produced by fn + CHANNEL_UP / CHANNEL_DOWN +# - The AVR / STB / TV power and input buttons seem to be non-functional +# as well as several of the other fn buttons and the PIP button? + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 56 ALT_RIGHT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 87 F11 +key 88 F12 +key 96 DPAD_CENTER +key 97 CTRL_RIGHT +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 NUMPAD_ENTER +key 111 FORWARD_DEL +key 113 VALUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 119 MEDIA_PAUSE +key 125 SEARCH +key 127 MENU +key 156 BOOKMARK +key 158 BACK +key 163 MEDIA_NEXT +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 172 HOME +key 207 MEDIA_PLAY +key 208 MEDIA_FAST_FORWARD +# key 288 left mouse button +# key 289 right mouse button (fn + button) +key 362 GUIDE +key 366 DVR +key 377 TV +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN +key 418 ZOOM_IN +key 419 ZOOM_OUT + diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm deleted file mode 100644 index 73a04d0..0000000 --- a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm +++ /dev/null @@ -1,370 +0,0 @@ -# Copyright (C) 2010 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. - -# -# Generic key character map for full alphabetic US English PC style external keyboards. -# -# This file is intentionally very generic and is intended to support a broad rang of keyboards. -# Do not edit the generic key character map to support a specific keyboard; instead, create -# a new key character map file with the required keyboard configuration. -# - -type FULL - -key A { - label: 'A' - base: 'a' - shift, capslock: 'A' - ctrl, alt, meta: none -} - -key B { - label: 'B' - base: 'b' - shift, capslock: 'B' - ctrl, alt, meta: none -} - -key C { - label: 'C' - base: 'c' - shift, capslock: 'C' - ctrl, alt, meta: none -} - -key D { - label: 'D' - base: 'd' - shift, capslock: 'D' - ctrl, alt, meta: none -} - -key E { - label: 'E' - base: 'e' - shift, capslock: 'E' - ctrl, alt, meta: none -} - -key F { - label: 'F' - base: 'f' - shift, capslock: 'F' - ctrl, alt, meta: none -} - -key G { - label: 'G' - base: 'g' - shift, capslock: 'G' - ctrl, alt, meta: none -} - -key H { - label: 'H' - base: 'h' - shift, capslock: 'H' - ctrl, alt, meta: none -} - -key I { - label: 'I' - base: 'i' - shift, capslock: 'I' - ctrl, alt, meta: none -} - -key J { - label: 'J' - base: 'j' - shift, capslock: 'J' - ctrl, alt, meta: none -} - -key K { - label: 'K' - base: 'k' - shift, capslock: 'K' - ctrl, alt, meta: none -} - -key L { - label: 'L' - base: 'l' - shift, capslock: 'L' - ctrl, alt, meta: none -} - -key M { - label: 'M' - base: 'm' - shift, capslock: 'M' - ctrl, alt, meta: none -} - -key N { - label: 'N' - base: 'n' - shift, capslock: 'N' - ctrl, alt, meta: none -} - -key O { - label: 'O' - base: 'o' - shift, capslock: 'O' - ctrl, alt, meta: none -} - -key P { - label: 'P' - base: 'p' - shift, capslock: 'P' - ctrl, alt, meta: none -} - -key Q { - label: 'Q' - base: 'q' - shift, capslock: 'Q' - ctrl, alt, meta: none -} - -key R { - label: 'R' - base: 'r' - shift, capslock: 'R' - ctrl, alt, meta: none -} - -key S { - label: 'S' - base: 's' - shift, capslock: 'S' - ctrl, alt, meta: none -} - -key T { - label: 'T' - base: 't' - shift, capslock: 'T' - ctrl, alt, meta: none -} - -key U { - label: 'U' - base: 'u' - shift, capslock: 'U' - ctrl, alt, meta: none -} - -key V { - label: 'V' - base: 'v' - shift, capslock: 'V' - ctrl, alt, meta: none -} - -key W { - label: 'W' - base: 'w' - shift, capslock: 'W' - ctrl, alt, meta: none -} - -key X { - label: 'X' - base: 'x' - shift, capslock: 'X' - ctrl, alt, meta: none -} - -key Y { - label: 'Y' - base: 'y' - shift, capslock: 'Y' - ctrl, alt, meta: none -} - -key Z { - label: 'Z' - base: 'z' - shift, capslock: 'Z' - ctrl, alt, meta: none -} - -key 0 { - label, number: '0' - base: '0' - shift: ')' - ctrl, alt, meta: none -} - -key 1 { - label, number: '1' - base: '1' - shift: '!' - ctrl, alt, meta: none -} - -key 2 { - label, number: '2' - base: '2' - shift: '@' - ctrl, alt, meta: none -} - -key 3 { - label, number: '3' - base: '3' - shift: '#' - ctrl, alt, meta: none -} - -key 4 { - label, number: '4' - base: '4' - shift: '$' - ctrl, alt, meta: none -} - -key 5 { - label, number: '5' - base: '5' - shift: '%' - ctrl, alt, meta: none -} - -key 6 { - label, number: '6' - base: '6' - shift: '^' - ctrl, alt, meta: none -} - -key 7 { - label, number: '7' - base: '7' - shift: '&' - ctrl, alt, meta: none -} - -key 8 { - label, number: '8' - base: '8' - shift: '*' - ctrl, alt, meta: none -} - -key 9 { - label, number: '9' - base: '9' - shift: '(' - ctrl, alt, meta: none -} - -key SPACE { - label: ' ' - base: ' ' - ctrl, alt, meta: none -} - -key ENTER { - label: '\n' - base: '\n' - ctrl, alt, meta: none -} - -key TAB { - label: '\t' - base: '\t' - ctrl, alt, meta: none -} - -key COMMA { - label, number: ',' - base: ',' - shift: '<' - ctrl, alt, meta: none -} - -key PERIOD { - label, number: '.' - base: '.' - shift: '>' - ctrl, alt, meta: none -} - -key SLASH { - label, number: '/' - base: '/' - shift: '?' - ctrl, alt, meta: none -} - -key GRAVE { - label, number: '`' - base: '`' - shift: '~' - ctrl, alt, meta: none -} - -key MINUS { - label, number: '-' - base: '-' - shift: '_' - ctrl, alt, meta: none -} - -key EQUALS { - label, number: '=' - base: '=' - shift: '+' - ctrl, alt, meta: none -} - -key LEFT_BRACKET { - label, number: '[' - base: '[' - shift: '{' - ctrl, alt, meta: none -} - -key RIGHT_BRACKET { - label, number: ']' - base: ']' - shift: '}' - ctrl, alt, meta: none -} - -key BACKSLASH { - label, number: '\\' - base: '\\' - shift: '|' - ctrl, alt, meta: none -} - -key SEMICOLON { - label, number: ';' - base: ';' - shift: ':' - ctrl, alt, meta: none -} - -key APOSTROPHE { - label, number: '\'' - base: '\'' - shift: '"' - ctrl, alt, meta: none -} diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl index eab78a0..87b3c32 100644 --- a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl +++ b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl @@ -66,6 +66,7 @@ key 51 COMMA key 52 PERIOD key 53 SLASH key 54 SHIFT_RIGHT +key 56 ALT_LEFT key 57 SPACE key 58 CAPS_LOCK key 59 F1 @@ -77,6 +78,9 @@ key 64 F6 key 65 F7 key 66 F8 key 67 F9 +key 68 F10 +key 87 F11 +key 88 F12 key 97 CTRL_RIGHT key 102 HOME key 103 DPAD_UP @@ -84,7 +88,6 @@ key 105 DPAD_LEFT key 106 DPAD_RIGHT key 107 MOVE_END key 108 DPAD_DOWN -key 110 INSERT key 111 FORWARD_DEL key 113 VOLUME_MUTE key 114 VOLUME_DOWN @@ -95,3 +98,4 @@ key 163 MEDIA_NEXT key 164 MEDIA_PLAY_PAUSE key 165 MEDIA_PREVIOUS key 166 MEDIA_STOP +# key 226 tbd reserved key diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk index 3f05edb..5c2a75d 100644 --- a/data/keyboards/common.mk +++ b/data/keyboards/common.mk @@ -16,15 +16,16 @@ # Used by Android.mk and keyboards.mk. keylayouts := \ + Apple_Wireless_Keyboard.kl \ AVRCP.kl \ Generic.kl \ + Logitech_USB_Receiver.kl \ Motorola_Bluetooth_Wireless_Keyboard.kl \ qwerty.kl \ qwerty2.kl keycharmaps := \ Generic.kcm \ - Virtual.kcm \ - Motorola_Bluetooth_Wireless_Keyboard.kcm \ qwerty.kcm \ - qwerty2.kcm + qwerty2.kcm \ + Virtual.kcm diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java index 7174e2b..484f6e7 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java @@ -25,11 +25,6 @@ import com.android.mediaframeworktest.MediaNames; import com.android.mediaframeworktest.MediaProfileReader; import android.test.suitebuilder.annotation.*; -/** - * WARNING: - * Currently, captureFrame() does not work, due to hardware access permission problem. - * We are currently only testing the metadata/album art retrieval features. - */ public class MediaMetadataRetrieverTest extends AndroidTestCase { private static final String TAG = "MediaMetadataRetrieverTest"; @@ -101,6 +96,7 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { } catch (Exception e) { Log.e(TAG, "Fails to convert the bitmap to a JPEG file for " + MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i]); hasFailed = true; + Log.e(TAG, e.toString()); } } catch(Exception e) { Log.e(TAG, "Fails to setDataSource for file " + MediaNames.THUMBNAIL_CAPTURE_TEST_FILES[i]); @@ -148,11 +144,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { public static void testBasicNormalMethodCallSequence() throws Exception { boolean hasFailed = false; MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY); try { retriever.setDataSource(MediaNames.TEST_PATH_1); - /* - * captureFrame() fails due to lack of permission to access hardware decoder devices Bitmap bitmap = retriever.captureFrame(); assertTrue(bitmap != null); try { @@ -162,7 +155,6 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { } catch (Exception e) { throw new Exception("Fails to convert the bitmap to a JPEG file for " + MediaNames.TEST_PATH_1, e); } - */ extractAllSupportedMetadataValues(retriever); } catch(Exception e) { Log.e(TAG, "Fails to setDataSource for " + MediaNames.TEST_PATH_1, e); @@ -251,17 +243,14 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { assertTrue(!hasFailed); } - // Due to the lack of permission to access hardware decoder, any calls - // attempting to capture a frame will fail. These are commented out for now - // until we find a solution to this access permission problem. @MediumTest public static void testIntendedUsage() { // By default, capture frame and retrieve metadata MediaMetadataRetriever retriever = new MediaMetadataRetriever(); boolean hasFailed = false; - // retriever.setDataSource(MediaNames.TEST_PATH_1); - // assertTrue(retriever.captureFrame() != null); - // assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null); + retriever.setDataSource(MediaNames.TEST_PATH_1); + assertTrue(retriever.captureFrame() != null); + assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null); // Do not capture frame or retrieve metadata retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY & MediaMetadataRetriever.MODE_GET_METADATA_ONLY); @@ -276,9 +265,9 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { } // Capture frame only - // retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY); - // retriever.setDataSource(MediaNames.TEST_PATH_1); - // assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) == null); + retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY); + retriever.setDataSource(MediaNames.TEST_PATH_1); + assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) == null); // Retriever metadata only retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY); @@ -289,10 +278,10 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { } // Capture frame and retrieve metadata - // retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY | MediaMetadataRetriever.MODE_GET_METADATA_ONLY); - // retriever.setDataSource(MediaNames.TEST_PATH_1); - // assertTrue(retriever.captureFrame() != null); - // assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null); + retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY | MediaMetadataRetriever.MODE_GET_METADATA_ONLY); + retriever.setDataSource(MediaNames.TEST_PATH_1); + assertTrue(retriever.captureFrame() != null); + assertTrue(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS) != null); retriever.release(); assertTrue(!hasFailed); } diff --git a/opengl/tests/hwc/Android.mk b/opengl/tests/hwc/Android.mk new file mode 100644 index 0000000..743dbf1 --- /dev/null +++ b/opengl/tests/hwc/Android.mk @@ -0,0 +1,27 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= hwc_stress.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libEGL \ + libGLESv2 \ + libui \ + libhardware \ + +LOCAL_STATIC_LIBRARIES := \ + libtestUtil \ + +LOCAL_C_INCLUDES += \ + system/extras/tests/include \ + hardware/libhardware/include \ + +LOCAL_MODULE:= hwc_stress +LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativestresstest + +LOCAL_MODULE_TAGS := tests + +LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +include $(BUILD_NATIVE_TEST) diff --git a/opengl/tests/hwc/hwc_stress.cpp b/opengl/tests/hwc/hwc_stress.cpp new file mode 100644 index 0000000..d119734 --- /dev/null +++ b/opengl/tests/hwc/hwc_stress.cpp @@ -0,0 +1,1193 @@ +/* + * Copyright (C) 2010 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. + * + */ + +/* + * Hardware Composer stress test + * + * Performs a pseudo-random (prandom) sequence of operations to the + * Hardware Composer (HWC), for a specified number of passes or for + * a specified period of time. By default the period of time is FLT_MAX, + * so that the number of passes will take precedence. + * + * The passes are grouped together, where (pass / passesPerGroup) specifies + * which group a particular pass is in. This causes every passesPerGroup + * worth of sequential passes to be within the same group. Computationally + * intensive operations are performed just once at the beginning of a group + * of passes and then used by all the passes in that group. This is done + * so as to increase both the average and peak rate of graphic operations, + * by moving computationally intensive operations to the beginning of a group. + * In particular, at the start of each group of passes a set of + * graphic buffers are created, then used by the first and remaining + * passes of that group of passes. + * + * The per-group initialization of the graphic buffers is performed + * by a function called initFrames. This function creates an array + * of smart pointers to the graphic buffers, in the form of a vector + * of vectors. The array is accessed in row major order, so each + * row is a vector of smart pointers. All the pointers of a single + * row point to graphic buffers which use the same pixel format and + * have the same dimension, although it is likely that each one is + * filled with a different color. This is done so that after doing + * the first HWC prepare then set call, subsequent set calls can + * be made with each of the layer handles changed to a different + * graphic buffer within the same row. Since the graphic buffers + * in a particular row have the same pixel format and dimension, + * additional HWC set calls can be made, without having to perform + * an HWC prepare call. + * + * This test supports the following command-line options: + * + * -v Verbose + * -s num Starting pass + * -e num Ending pass + * -p num Execute the single pass specified by num + * -n num Number of set operations to perform after each prepare operation + * -t float Maximum time in seconds to execute the test + * -d float Delay in seconds performed after each set operation + * -D float Delay in seconds performed after the last pass is executed + * + * Typically the test is executed for a large range of passes. By default + * passes 0 through 99999 (100,000 passes) are executed. Although this test + * does not validate the generated image, at times it is useful to reexecute + * a particular pass and leave the displayed image on the screen for an + * extended period of time. This can be done either by setting the -s + * and -e options to the desired pass, along with a large value for -D. + * This can also be done via the -p option, again with a large value for + * the -D options. + * + * So far this test only contains code to create graphic buffers with + * a continuous solid color. Although this test is unable to validate the + * image produced, any image that contains other than rectangles of a solid + * color are incorrect. Note that the rectangles may use a transparent + * color and have a blending operation that causes the color in overlapping + * rectangles to be mixed. In such cases the overlapping portions may have + * a different color from the rest of the rectangle. + */ + +#include <algorithm> +#include <assert.h> +#include <cerrno> +#include <cmath> +#include <cstdlib> +#include <ctime> +#include <libgen.h> +#include <sched.h> +#include <sstream> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <vector> + +#include <arpa/inet.h> // For ntohl() and htonl() + +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <ui/FramebufferNativeWindow.h> +#include <ui/GraphicBuffer.h> +#include <ui/EGLUtils.h> + +#define LOG_TAG "hwcStressTest" +#include <utils/Log.h> +#include <testUtil.h> + +#include <hardware/hwcomposer.h> + +using namespace std; +using namespace android; + +const float maxSizeRatio = 1.3; // Graphic buffers can be upto this munch + // larger than the default screen size +const unsigned int passesPerGroup = 10; // A group of passes all use the same + // graphic buffers +const float rareRatio = 0.1; // Ratio at which rare conditions are produced. + +// Defaults for command-line options +const bool defaultVerbose = false; +const unsigned int defaultStartPass = 0; +const unsigned int defaultEndPass = 99999; +const unsigned int defaultPerPassNumSet = 10; +const float defaultPerPassDelay = 0.1; +const float defaultEndDelay = 2.0; // Default delay between completion of + // final pass and restart of framework +const float defaultDuration = FLT_MAX; // A fairly long time, so that + // range of passes will have + // precedence + +// Command-line option settings +static bool verbose = defaultVerbose; +static unsigned int startPass = defaultStartPass; +static unsigned int endPass = defaultEndPass; +static unsigned int numSet = defaultPerPassNumSet; +static float perSetDelay = defaultPerPassDelay; +static float endDelay = defaultEndDelay; +static float duration = defaultDuration; + +// Command-line mutual exclusion detection flags. +// Corresponding flag set true once an option is used. +bool eFlag, sFlag, pFlag; + +#define MAXSTR 100 +#define MAXCMD 200 +#define BITSPERBYTE 8 // TODO: Obtain from <values.h>, once + // it has been added + +#define CMD_STOP_FRAMEWORK "stop 2>&1" +#define CMD_START_FRAMEWORK "start 2>&1" + +#define NUMA(a) (sizeof(a) / sizeof(a [0])) +#define MEMCLR(addr, size) do { \ + memset((addr), 0, (size)); \ + } while (0) + +// Represent RGB color as fraction of color components. +// Each of the color components are expected in the range [0.0, 1.0] +class RGBColor { + public: + RGBColor(): _r(0.0), _g(0.0), _b(0.0) {}; + RGBColor(float f): _r(f), _g(f), _b(f) {}; // Gray + RGBColor(float r, float g, float b): _r(r), _g(g), _b(b) {}; + float r(void) const { return _r; } + float g(void) const { return _g; } + float b(void) const { return _b; } + + private: + float _r; + float _g; + float _b; +}; + +// Represent YUV color as fraction of color components. +// Each of the color components are expected in the range [0.0, 1.0] +class YUVColor { + public: + YUVColor(): _y(0.0), _u(0.0), _v(0.0) {}; + YUVColor(float f): _y(f), _u(0.0), _v(0.0) {}; // Gray + YUVColor(float y, float u, float v): _y(y), _u(u), _v(v) {}; + float y(void) const { return _y; } + float u(void) const { return _u; } + float v(void) const { return _v; } + + private: + float _y; + float _u; + float _v; +}; + +// File scope constants +static const struct { + unsigned int format; + const char *desc; +} graphicFormat[] = { + {HAL_PIXEL_FORMAT_RGBA_8888, "RGBA8888"}, + {HAL_PIXEL_FORMAT_RGBX_8888, "RGBX8888"}, +// {HAL_PIXEL_FORMAT_RGB_888, "RGB888"}, // Known issue: 3198458 + {HAL_PIXEL_FORMAT_RGB_565, "RGB565"}, + {HAL_PIXEL_FORMAT_BGRA_8888, "BGRA8888"}, + {HAL_PIXEL_FORMAT_RGBA_5551, "RGBA5551"}, + {HAL_PIXEL_FORMAT_RGBA_4444, "RGBA4444"}, +// {HAL_PIXEL_FORMAT_YV12, "YV12"}, // Currently not supported by HWC +}; +const unsigned int blendingOps[] = { + HWC_BLENDING_NONE, + HWC_BLENDING_PREMULT, + HWC_BLENDING_COVERAGE, +}; +const unsigned int layerFlags[] = { + HWC_SKIP_LAYER, +}; +const vector<unsigned int> vecLayerFlags(layerFlags, + layerFlags + NUMA(layerFlags)); + +const unsigned int transformFlags[] = { + HWC_TRANSFORM_FLIP_H, + HWC_TRANSFORM_FLIP_V, + HWC_TRANSFORM_ROT_90, + // ROT_180 & ROT_270 intentionally not listed, because they + // they are formed from combinations of the flags already listed. +}; +const vector<unsigned int> vecTransformFlags(transformFlags, + transformFlags + NUMA(transformFlags)); + +// File scope globals +static const int texUsage = GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_SW_WRITE_RARELY; +static hw_module_t const *hwcModule; +static hwc_composer_device_t *hwcDevice; +static vector <vector <sp<GraphicBuffer> > > frames; +static EGLDisplay dpy; +static EGLContext context; +static EGLSurface surface; +static EGLint width, height; + +// File scope prototypes +static void execCmd(const char *cmd); +static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE); +static void checkGlError(const char* op); +static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config); +static void printGLString(const char *name, GLenum s); +static hwc_layer_list_t *createLayerList(size_t numLayers); +static void freeLayerList(hwc_layer_list_t *list); +static void fillColor(GraphicBuffer *gBuf, RGBColor color, float trans); +static void fillColor(GraphicBuffer *gBuf, YUVColor color, float trans); +void init(void); +void initFrames(unsigned int seed); +void displayList(hwc_layer_list_t *list); +void displayListPrepareModifiable(hwc_layer_list_t *list); +void displayListHandles(hwc_layer_list_t *list); +const char *graphicFormat2str(unsigned int format); +template <class T> vector<T> vectorRandSelect(const vector<T>& vec, size_t num); +template <class T> T vectorOr(const vector<T>& vec); + +/* + * Main + * + * Performs the following high-level sequence of operations: + * + * 1. Command-line parsing + * + * 2. Initialization + * + * 3. For each pass: + * + * a. If pass is first pass or in a different group from the + * previous pass, initialize the array of graphic buffers. + * + * b. Create a HWC list with room to specify a prandomly + * selected number of layers. + * + * c. Select a subset of the rows from the graphic buffer array, + * such that there is a unique row to be used for each + * of the layers in the HWC list. + * + * d. Prandomly fill in the HWC list with handles + * selected from any of the columns of the selected row. + * + * e. Pass the populated list to the HWC prepare call. + * + * f. Pass the populated list to the HWC set call. + * + * g. If additional set calls are to be made, then for each + * additional set call, select a new set of handles and + * perform the set call. + */ +int +main(int argc, char *argv[]) +{ + int rv, opt; + char *chptr; + unsigned int pass; + char cmd[MAXCMD]; + struct timeval startTime, currentTime, delta; + + testSetLogCatTag(LOG_TAG); + + // Parse command line arguments + while ((opt = getopt(argc, argv, "vp:d:D:n:s:e:t:?h")) != -1) { + switch (opt) { + case 'd': // Delay after each set operation + perSetDelay = strtod(optarg, &chptr); + if ((*chptr != '\0') || (perSetDelay < 0.0)) { + testPrintE("Invalid command-line specified per pass delay of: " + "%s", optarg); + exit(1); + } + break; + + case 'D': // End of test delay + // Delay between completion of final pass and restart + // of framework + endDelay = strtod(optarg, &chptr); + if ((*chptr != '\0') || (endDelay < 0.0)) { + testPrintE("Invalid command-line specified end of test delay " + "of: %s", optarg); + exit(2); + } + break; + + case 't': // Duration + duration = strtod(optarg, &chptr); + if ((*chptr != '\0') || (duration < 0.0)) { + testPrintE("Invalid command-line specified duration of: %s", + optarg); + exit(3); + } + break; + + case 'n': // Num set operations per pass + numSet = strtoul(optarg, &chptr, 10); + if (*chptr != '\0') { + testPrintE("Invalid command-line specified num set per pass " + "of: %s", optarg); + exit(4); + } + break; + + case 's': // Starting Pass + sFlag = true; + if (pFlag) { + testPrintE("Invalid combination of command-line options."); + testPrintE(" The -p option is mutually exclusive from the"); + testPrintE(" -s and -e options."); + exit(5); + } + startPass = strtoul(optarg, &chptr, 10); + if (*chptr != '\0') { + testPrintE("Invalid command-line specified starting pass " + "of: %s", optarg); + exit(6); + } + break; + + case 'e': // Ending Pass + eFlag = true; + if (pFlag) { + testPrintE("Invalid combination of command-line options."); + testPrintE(" The -p option is mutually exclusive from the"); + testPrintE(" -s and -e options."); + exit(7); + } + endPass = strtoul(optarg, &chptr, 10); + if (*chptr != '\0') { + testPrintE("Invalid command-line specified ending pass " + "of: %s", optarg); + exit(8); + } + break; + + case 'p': // Run a single specified pass + pFlag = true; + if (sFlag || eFlag) { + testPrintE("Invalid combination of command-line options."); + testPrintE(" The -p option is mutually exclusive from the"); + testPrintE(" -s and -e options."); + exit(9); + } + startPass = endPass = strtoul(optarg, &chptr, 10); + if (*chptr != '\0') { + testPrintE("Invalid command-line specified pass of: %s", + optarg); + exit(10); + } + break; + + case 'v': // Verbose + verbose = true; + break; + + case 'h': // Help + case '?': + default: + testPrintE(" %s [options]", basename(argv[0])); + testPrintE(" options:"); + testPrintE(" -p Execute specified pass"); + testPrintE(" -s Starting pass"); + testPrintE(" -e Ending pass"); + testPrintE(" -t Duration"); + testPrintE(" -d Delay after each set operation"); + testPrintE(" -D End of test delay"); + testPrintE(" -n Num set operations per pass"); + testPrintE(" -v Verbose"); + exit(((optopt == 0) || (optopt == '?')) ? 0 : 11); + } + } + if (endPass < startPass) { + testPrintE("Unexpected ending pass before starting pass"); + testPrintE(" startPass: %u endPass: %u", startPass, endPass); + exit(12); + } + if (argc != optind) { + testPrintE("Unexpected command-line postional argument"); + testPrintE(" %s [-s start_pass] [-e end_pass] [-t duration]", + basename(argv[0])); + exit(13); + } + testPrintI("duration: %g", duration); + testPrintI("startPass: %u", startPass); + testPrintI("endPass: %u", endPass); + testPrintI("numSet: %u", numSet); + + // Stop framework + rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK); + if (rv >= (signed) sizeof(cmd) - 1) { + testPrintE("Command too long for: %s", CMD_STOP_FRAMEWORK); + exit(14); + } + execCmd(cmd); + testDelay(1.0); // TODO - needs means to query whether asyncronous stop + // framework operation has completed. For now, just wait + // a long time. + + init(); + + // For each pass + gettimeofday(&startTime, NULL); + for (pass = startPass; pass <= endPass; pass++) { + // Stop if duration of work has already been performed + gettimeofday(¤tTime, NULL); + delta = tvDelta(&startTime, ¤tTime); + if (tv2double(&delta) > duration) { break; } + + // Regenerate a new set of test frames when this pass is + // either the first pass or is in a different group then + // the previous pass. A group of passes are passes that + // all have the same quotient when their pass number is + // divided by passesPerGroup. + if ((pass == startPass) + || ((pass / passesPerGroup) != ((pass - 1) / passesPerGroup))) { + initFrames(pass / passesPerGroup); + } + + testPrintI("==== Starting pass: %u", pass); + + // Cause deterministic sequence of prandom numbers to be + // generated for this pass. + srand48(pass); + + hwc_layer_list_t *list; + list = createLayerList(testRandMod(frames.size()) + 1); + if (list == NULL) { + testPrintE("createLayerList failed"); + exit(20); + } + + // Prandomly select a subset of frames to be used by this pass. + vector <vector <sp<GraphicBuffer> > > selectedFrames; + selectedFrames = vectorRandSelect(frames, list->numHwLayers); + + // Any transform tends to create a layer that the hardware + // composer is unable to support and thus has to leave for + // SurfaceFlinger. Place heavy bias on specifying no transforms. + bool noTransform = testRandFract() > rareRatio; + + for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) { + unsigned int idx = testRandMod(selectedFrames[n1].size()); + sp<GraphicBuffer> gBuf = selectedFrames[n1][idx]; + hwc_layer_t *layer = &list->hwLayers[n1]; + layer->handle = gBuf->handle; + + layer->blending = blendingOps[testRandMod(NUMA(blendingOps))]; + layer->flags = (testRandFract() > rareRatio) ? 0 + : vectorOr(vectorRandSelect(vecLayerFlags, + testRandMod(vecLayerFlags.size() + 1))); + layer->transform = (noTransform || testRandFract() > rareRatio) ? 0 + : vectorOr(vectorRandSelect(vecTransformFlags, + testRandMod(vecTransformFlags.size() + 1))); + layer->sourceCrop.left = testRandMod(gBuf->getWidth()); + layer->sourceCrop.top = testRandMod(gBuf->getHeight()); + layer->sourceCrop.right = layer->sourceCrop.left + + testRandMod(gBuf->getWidth() - layer->sourceCrop.left) + 1; + layer->sourceCrop.bottom = layer->sourceCrop.top + + testRandMod(gBuf->getHeight() - layer->sourceCrop.top) + 1; + layer->displayFrame.left = testRandMod(width); + layer->displayFrame.top = testRandMod(height); + layer->displayFrame.right = layer->displayFrame.left + + testRandMod(width - layer->displayFrame.left) + 1; + layer->displayFrame.bottom = layer->displayFrame.top + + testRandMod(height - layer->displayFrame.top) + 1; + layer->visibleRegionScreen.numRects = 1; + layer->visibleRegionScreen.rects = &layer->displayFrame; + } + + // Perform prepare operation + if (verbose) { testPrintI("Prepare:"); displayList(list); } + hwcDevice->prepare(hwcDevice, list); + if (verbose) { + testPrintI("Post Prepare:"); + displayListPrepareModifiable(list); + } + + // Turn off the geometry changed flag + list->flags &= ~HWC_GEOMETRY_CHANGED; + + // Perform the set operation(s) + if (verbose) {testPrintI("Set:"); } + for (unsigned int n1 = 0; n1 < numSet; n1++) { + if (verbose) {displayListHandles(list); } + hwcDevice->set(hwcDevice, dpy, surface, list); + + // Prandomly select a new set of handles + for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) { + unsigned int idx = testRandMod(selectedFrames[n1].size()); + sp<GraphicBuffer> gBuf = selectedFrames[n1][idx]; + hwc_layer_t *layer = &list->hwLayers[n1]; + layer->handle = (native_handle_t *) gBuf->handle; + } + + testDelay(perSetDelay); + } + + + freeLayerList(list); + testPrintI("==== Completed pass: %u", pass); + } + + testDelay(endDelay); + + // Start framework + rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK); + if (rv >= (signed) sizeof(cmd) - 1) { + testPrintE("Command too long for: %s", CMD_START_FRAMEWORK); + exit(21); + } + execCmd(cmd); + + testPrintI("Successfully completed %u passes", pass - startPass); + + return 0; +} + +/* + * Execute Command + * + * Executes the command pointed to by cmd. Output from the + * executed command is captured and sent to LogCat Info. Once + * the command has finished execution, it's exit status is captured + * and checked for an exit status of zero. Any other exit status + * causes diagnostic information to be printed and an immediate + * testcase failure. + */ +static void execCmd(const char *cmd) +{ + FILE *fp; + int rv; + int status; + char str[MAXSTR]; + + // Display command to be executed + testPrintI("cmd: %s", cmd); + + // Execute the command + fflush(stdout); + if ((fp = popen(cmd, "r")) == NULL) { + testPrintE("execCmd popen failed, errno: %i", errno); + exit(30); + } + + // Obtain and display each line of output from the executed command + while (fgets(str, sizeof(str), fp) != NULL) { + if ((strlen(str) > 1) && (str[strlen(str) - 1] == '\n')) { + str[strlen(str) - 1] = '\0'; + } + testPrintI(" out: %s", str); + } + + // Obtain and check return status of executed command. + // Fail on non-zero exit status + status = pclose(fp); + if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { + testPrintE("Unexpected command failure"); + testPrintE(" status: %#x", status); + if (WIFEXITED(status)) { + testPrintE("WEXITSTATUS: %i", WEXITSTATUS(status)); + } + if (WIFSIGNALED(status)) { + testPrintE("WTERMSIG: %i", WTERMSIG(status)); + } + exit(31); + } +} + +static void checkEglError(const char* op, EGLBoolean returnVal) { + if (returnVal != EGL_TRUE) { + testPrintE("%s() returned %d", op, returnVal); + } + + for (EGLint error = eglGetError(); error != EGL_SUCCESS; error + = eglGetError()) { + testPrintE("after %s() eglError %s (0x%x)", + op, EGLUtils::strerror(error), error); + } +} + +static void checkGlError(const char* op) { + for (GLint error = glGetError(); error; error + = glGetError()) { + testPrintE("after %s() glError (0x%x)", op, error); + } +} + +static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { + +#define X(VAL) {VAL, #VAL} + struct {EGLint attribute; const char* name;} names[] = { + X(EGL_BUFFER_SIZE), + X(EGL_ALPHA_SIZE), + X(EGL_BLUE_SIZE), + X(EGL_GREEN_SIZE), + X(EGL_RED_SIZE), + X(EGL_DEPTH_SIZE), + X(EGL_STENCIL_SIZE), + X(EGL_CONFIG_CAVEAT), + X(EGL_CONFIG_ID), + X(EGL_LEVEL), + X(EGL_MAX_PBUFFER_HEIGHT), + X(EGL_MAX_PBUFFER_PIXELS), + X(EGL_MAX_PBUFFER_WIDTH), + X(EGL_NATIVE_RENDERABLE), + X(EGL_NATIVE_VISUAL_ID), + X(EGL_NATIVE_VISUAL_TYPE), + X(EGL_SAMPLES), + X(EGL_SAMPLE_BUFFERS), + X(EGL_SURFACE_TYPE), + X(EGL_TRANSPARENT_TYPE), + X(EGL_TRANSPARENT_RED_VALUE), + X(EGL_TRANSPARENT_GREEN_VALUE), + X(EGL_TRANSPARENT_BLUE_VALUE), + X(EGL_BIND_TO_TEXTURE_RGB), + X(EGL_BIND_TO_TEXTURE_RGBA), + X(EGL_MIN_SWAP_INTERVAL), + X(EGL_MAX_SWAP_INTERVAL), + X(EGL_LUMINANCE_SIZE), + X(EGL_ALPHA_MASK_SIZE), + X(EGL_COLOR_BUFFER_TYPE), + X(EGL_RENDERABLE_TYPE), + X(EGL_CONFORMANT), + }; +#undef X + + for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) { + EGLint value = -1; + EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value); + EGLint error = eglGetError(); + if (returnVal && error == EGL_SUCCESS) { + testPrintI(" %s: %d (%#x)", names[j].name, value, value); + } + } + testPrintI(""); +} + +static void printGLString(const char *name, GLenum s) +{ + const char *v = (const char *) glGetString(s); + + if (v == NULL) { + testPrintI("GL %s unknown", name); + } else { + testPrintI("GL %s = %s", name, v); + } +} + +/* + * createLayerList + * dynamically creates layer list with numLayers worth + * of hwLayers entries. + */ +static hwc_layer_list_t *createLayerList(size_t numLayers) +{ + hwc_layer_list_t *list; + + size_t size = sizeof(hwc_layer_list) + numLayers * sizeof(hwc_layer_t); + if ((list = (hwc_layer_list_t *) calloc(1, size)) == NULL) { + return NULL; + } + list->flags = HWC_GEOMETRY_CHANGED; + list->numHwLayers = numLayers; + + return list; +} + +/* + * freeLayerList + * Frees memory previous allocated via createLayerList(). + */ +static void freeLayerList(hwc_layer_list_t *list) +{ + free(list); +} + +static void fillColor(GraphicBuffer *gBuf, RGBColor color, float trans) +{ + unsigned char* buf = NULL; + status_t err; + unsigned int numPixels = gBuf->getWidth() * gBuf->getHeight(); + uint32_t pixel; + + // RGB 2 YUV conversion ratios + const struct rgb2yuvRatios { + int format; + float weightRed; + float weightBlu; + float weightGrn; + } rgb2yuvRatios[] = { + { HAL_PIXEL_FORMAT_YV12, 0.299, 0.114, 0.587 }, + }; + + const struct rgbAttrib { + int format; + bool hostByteOrder; + size_t bytes; + size_t rOffset; + size_t rSize; + size_t gOffset; + size_t gSize; + size_t bOffset; + size_t bSize; + size_t aOffset; + size_t aSize; + } rgbAttributes[] = { + {HAL_PIXEL_FORMAT_RGBA_8888, false, 4, 0, 8, 8, 8, 16, 8, 24, 8}, + {HAL_PIXEL_FORMAT_RGBX_8888, false, 4, 0, 8, 8, 8, 16, 8, 0, 0}, + {HAL_PIXEL_FORMAT_RGB_888, false, 3, 0, 8, 8, 8, 16, 8, 0, 0}, + {HAL_PIXEL_FORMAT_RGB_565, true, 2, 0, 5, 5, 6, 11, 5, 0, 0}, + {HAL_PIXEL_FORMAT_BGRA_8888, false, 4, 16, 8, 8, 8, 0, 8, 24, 8}, + {HAL_PIXEL_FORMAT_RGBA_5551, true , 2, 0, 5, 5, 5, 10, 5, 15, 1}, + {HAL_PIXEL_FORMAT_RGBA_4444, false, 2, 12, 4, 0, 4, 4, 4, 8, 4}, + }; + + // If YUV format, convert color and pass work to YUV color fill + for (unsigned int n1 = 0; n1 < NUMA(rgb2yuvRatios); n1++) { + if (gBuf->getPixelFormat() == rgb2yuvRatios[n1].format) { + float wr = rgb2yuvRatios[n1].weightRed; + float wb = rgb2yuvRatios[n1].weightBlu; + float wg = rgb2yuvRatios[n1].weightGrn; + float y = wr * color.r() + wb * color.b() + wg * color.g(); + float u = 0.5 * ((color.b() - y) / (1 - wb)) + 0.5; + float v = 0.5 * ((color.r() - y) / (1 - wr)) + 0.5; + YUVColor yuvColor(y, u, v); + fillColor(gBuf, yuvColor, trans); + return; + } + } + + const struct rgbAttrib *attrib; + for (attrib = rgbAttributes; attrib < rgbAttributes + NUMA(rgbAttributes); + attrib++) { + if (attrib->format == gBuf->getPixelFormat()) { break; } + } + if (attrib >= rgbAttributes + NUMA(rgbAttributes)) { + testPrintE("fillColor rgb unsupported format of: %u", + gBuf->getPixelFormat()); + exit(50); + } + + pixel = htonl((uint32_t) (((1 << attrib->rSize) - 1) * color.r()) + << ((sizeof(pixel) * BITSPERBYTE) + - (attrib->rOffset + attrib->rSize))); + pixel |= htonl((uint32_t) (((1 << attrib->gSize) - 1) * color.g()) + << ((sizeof(pixel) * BITSPERBYTE) + - (attrib->gOffset + attrib->gSize))); + pixel |= htonl((uint32_t) (((1 << attrib->bSize) - 1) * color.b()) + << ((sizeof(pixel) * BITSPERBYTE) + - (attrib->bOffset + attrib->bSize))); + if (attrib->aSize) { + pixel |= htonl((uint32_t) (((1 << attrib->aSize) - 1) * trans) + << ((sizeof(pixel) * BITSPERBYTE) + - (attrib->aOffset + attrib->aSize))); + } + if (attrib->hostByteOrder) { + pixel = ntohl(pixel); + pixel >>= sizeof(pixel) * BITSPERBYTE - attrib->bytes * BITSPERBYTE; + } + + err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf)); + if (err != 0) { + testPrintE("fillColor rgb lock failed: %d", err); + exit(51); + } + + for (unsigned int n1 = 0; n1 < numPixels; n1++) { + memmove(buf, &pixel, attrib->bytes); + buf += attrib->bytes; + } + + err = gBuf->unlock(); + if (err != 0) { + testPrintE("fillColor rgb unlock failed: %d", err); + exit(52); + } +} + +static void fillColor(GraphicBuffer *gBuf, YUVColor color, float trans) +{ + unsigned char* buf = NULL; + status_t err; + unsigned int width = gBuf->getWidth(); + unsigned int height = gBuf->getHeight(); + + const struct yuvAttrib { + int format; + size_t padWidth; + bool planar; + unsigned int uSubSampX; + unsigned int uSubSampY; + unsigned int vSubSampX; + unsigned int vSubSampY; + } yuvAttributes[] = { + { HAL_PIXEL_FORMAT_YV12, 16, true, 2, 2, 2, 2}, + }; + + const struct yuvAttrib *attrib; + for (attrib = yuvAttributes; attrib < yuvAttributes + NUMA(yuvAttributes); + attrib++) { + if (attrib->format == gBuf->getPixelFormat()) { break; } + } + if (attrib >= yuvAttributes + NUMA(yuvAttributes)) { + testPrintE("fillColor yuv unsupported format of: %u", + gBuf->getPixelFormat()); + exit(60); + } + + assert(attrib->planar == true); // So far, only know how to handle planar + + // If needed round width up to pad size + if (width % attrib->padWidth) { + width += attrib->padWidth - (width % attrib->padWidth); + } + assert((width % attrib->padWidth) == 0); + + err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf)); + if (err != 0) { + testPrintE("fillColor lock failed: %d", err); + exit(61); + } + + // Fill in Y component + for (unsigned int x = 0; x < width; x++) { + for (unsigned int y = 0; y < height; y++) { + *buf++ = (x < gBuf->getWidth()) ? (255 * color.y()) : 0; + } + } + + // Fill in U component + for (unsigned int x = 0; x < width; x += attrib->uSubSampX) { + for (unsigned int y = 0; y < height; y += attrib->uSubSampY) { + *buf++ = (x < gBuf->getWidth()) ? (255 * color.u()) : 0; + } + } + + // Fill in V component + for (unsigned int x = 0; x < width; x += attrib->vSubSampX) { + for (unsigned int y = 0; y < height; y += attrib->vSubSampY) { + *buf++ = (x < gBuf->getWidth()) ? (255 * color.v()) : 0; + } + } + + err = gBuf->unlock(); + if (err != 0) { + testPrintE("fillColor unlock failed: %d", err); + exit(62); + } +} + +void init(void) +{ + int rv; + + EGLBoolean returnValue; + EGLConfig myConfig = {0}; + EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + EGLint sConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE }; + EGLint majorVersion, minorVersion; + + checkEglError("<init>"); + dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + checkEglError("eglGetDisplay"); + if (dpy == EGL_NO_DISPLAY) { + testPrintE("eglGetDisplay returned EGL_NO_DISPLAY"); + exit(70); + } + + returnValue = eglInitialize(dpy, &majorVersion, &minorVersion); + checkEglError("eglInitialize", returnValue); + testPrintI("EGL version %d.%d", majorVersion, minorVersion); + if (returnValue != EGL_TRUE) { + testPrintE("eglInitialize failed"); + exit(71); + } + + EGLNativeWindowType window = android_createDisplaySurface(); + if (window == NULL) { + testPrintE("android_createDisplaySurface failed"); + exit(72); + } + returnValue = EGLUtils::selectConfigForNativeWindow(dpy, + sConfigAttribs, window, &myConfig); + if (returnValue) { + testPrintE("EGLUtils::selectConfigForNativeWindow() returned %d", + returnValue); + exit(73); + } + checkEglError("EGLUtils::selectConfigForNativeWindow"); + + testPrintI("Chose this configuration:"); + printEGLConfiguration(dpy, myConfig); + + surface = eglCreateWindowSurface(dpy, myConfig, window, NULL); + checkEglError("eglCreateWindowSurface"); + if (surface == EGL_NO_SURFACE) { + testPrintE("gelCreateWindowSurface failed."); + exit(74); + } + + context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, contextAttribs); + checkEglError("eglCreateContext"); + if (context == EGL_NO_CONTEXT) { + testPrintE("eglCreateContext failed"); + exit(75); + } + returnValue = eglMakeCurrent(dpy, surface, surface, context); + checkEglError("eglMakeCurrent", returnValue); + if (returnValue != EGL_TRUE) { + testPrintE("eglMakeCurrent failed"); + exit(76); + } + eglQuerySurface(dpy, surface, EGL_WIDTH, &width); + checkEglError("eglQuerySurface"); + eglQuerySurface(dpy, surface, EGL_HEIGHT, &height); + checkEglError("eglQuerySurface"); + + fprintf(stderr, "Window dimensions: %d x %d", width, height); + + printGLString("Version", GL_VERSION); + printGLString("Vendor", GL_VENDOR); + printGLString("Renderer", GL_RENDERER); + printGLString("Extensions", GL_EXTENSIONS); + + if ((rv = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwcModule)) != 0) { + testPrintE("hw_get_module failed, rv: %i", rv); + errno = -rv; + perror(NULL); + exit(77); + } + if ((rv = hwc_open(hwcModule, &hwcDevice)) != 0) { + testPrintE("hwc_open failed, rv: %i", rv); + errno = -rv; + perror(NULL); + exit(78); + } + + testPrintI(""); +} + +/* + * Initialize Frames + * + * Creates an array of graphic buffers, within the global variable + * named frames. The graphic buffers are contained within a vector of + * verctors. All the graphic buffers in a particular row are of the same + * format and dimension. Each graphic buffer is uniformly filled with a + * prandomly selected color. It is likely that each buffer, even + * in the same row, will be filled with a unique color. + */ +void initFrames(unsigned int seed) +{ + const size_t maxRows = 5; + const size_t minCols = 2; // Need at least double buffering + const size_t maxCols = 4; // One more than triple buffering + + if (verbose) { testPrintI("initFrames seed: %u", seed); } + srand48(seed); + size_t rows = testRandMod(maxRows) + 1; + + frames.clear(); + frames.resize(rows); + + for (unsigned int row = 0; row < rows; row++) { + // All frames within a row have to have the same format and + // dimensions. Width and height need to be >= 1. + int format = graphicFormat[testRandMod(NUMA(graphicFormat))].format; + size_t w = (width * maxSizeRatio) * testRandFract(); + size_t h = (height * maxSizeRatio) * testRandFract(); + w = max(1u, w); + h = max(1u, h); + if (verbose) { + testPrintI(" frame %u width: %u height: %u format: %u %s", + row, w, h, format, graphicFormat2str(format)); + } + + size_t cols = testRandMod((maxCols + 1) - minCols) + minCols; + frames[row].resize(cols); + for (unsigned int col = 0; col < cols; col++) { + RGBColor color(testRandFract(), testRandFract(), testRandFract()); + float transp = testRandFract(); + + frames[row][col] = new GraphicBuffer(w, h, format, texUsage); + fillColor(frames[row][col].get(), color, transp); + if (verbose) { + testPrintI(" buf: %p handle: %p color: <%f, %f, %f> " + "transp: %f", + frames[row][col].get(), frames[row][col]->handle, + color.r(), color.g(), color.b(), transp); + } + } + } +} + +void displayList(hwc_layer_list_t *list) +{ + testPrintI(" flags: %#x%s", list->flags, + (list->flags & HWC_GEOMETRY_CHANGED) ? " GEOMETRY_CHANGED" : ""); + testPrintI(" numHwLayers: %u", list->numHwLayers); + + for (unsigned int layer = 0; layer < list->numHwLayers; layer++) { + testPrintI(" layer %u compositionType: %#x%s%s", layer, + list->hwLayers[layer].compositionType, + (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER) + ? " FRAMEBUFFER" : "", + (list->hwLayers[layer].compositionType == HWC_OVERLAY) + ? " OVERLAY" : ""); + + testPrintI(" hints: %#x", + list->hwLayers[layer].hints, + (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER) + ? " TRIPLE_BUFFER" : "", + (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB) + ? " CLEAR_FB" : ""); + + testPrintI(" flags: %#x%s", + list->hwLayers[layer].flags, + (list->hwLayers[layer].flags & HWC_SKIP_LAYER) + ? " SKIP_LAYER" : ""); + + testPrintI(" handle: %p", + list->hwLayers[layer].handle); + + // Intentionally skipped display of ROT_180 & ROT_270, + // which are formed from combinations of the other flags. + testPrintI(" transform: %#x%s%s%s", + list->hwLayers[layer].transform, + (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_H) + ? " FLIP_H" : "", + (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_V) + ? " FLIP_V" : "", + (list->hwLayers[layer].transform & HWC_TRANSFORM_ROT_90) + ? " ROT_90" : ""); + + testPrintI(" blending: %#x", + list->hwLayers[layer].blending, + (list->hwLayers[layer].blending == HWC_BLENDING_NONE) + ? " NONE" : "", + (list->hwLayers[layer].blending == HWC_BLENDING_PREMULT) + ? " PREMULT" : "", + (list->hwLayers[layer].blending == HWC_BLENDING_COVERAGE) + ? " COVERAGE" : ""); + + testPrintI(" sourceCrop: [%i, %i, %i, %i]", + list->hwLayers[layer].sourceCrop.left, + list->hwLayers[layer].sourceCrop.top, + list->hwLayers[layer].sourceCrop.right, + list->hwLayers[layer].sourceCrop.bottom); + + testPrintI(" displayFrame: [%i, %i, %i, %i]", + list->hwLayers[layer].displayFrame.left, + list->hwLayers[layer].displayFrame.top, + list->hwLayers[layer].displayFrame.right, + list->hwLayers[layer].displayFrame.bottom); + } +} + +/* + * Display List Prepare Modifiable + * + * Displays the portions of a list that are meant to be modified by + * a prepare call. + */ +void displayListPrepareModifiable(hwc_layer_list_t *list) +{ + for (unsigned int layer = 0; layer < list->numHwLayers; layer++) { + testPrintI(" layer %u compositionType: %#x%s%s", layer, + list->hwLayers[layer].compositionType, + (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER) + ? " FRAMEBUFFER" : "", + (list->hwLayers[layer].compositionType == HWC_OVERLAY) + ? " OVERLAY" : ""); + testPrintI(" hints: %#x%s%s", + list->hwLayers[layer].hints, + (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER) + ? " TRIPLE_BUFFER" : "", + (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB) + ? " CLEAR_FB" : ""); + } +} + +/* + * Display List Handles + * + * Displays the handles of all the graphic buffers in the list. + */ +void displayListHandles(hwc_layer_list_t *list) +{ + const unsigned int maxLayersPerLine = 6; + + ostringstream str(" layers:"); + for (unsigned int layer = 0; layer < list->numHwLayers; layer++) { + str << ' ' << list->hwLayers[layer].handle; + if (((layer % maxLayersPerLine) == (maxLayersPerLine - 1)) + && (layer != list->numHwLayers - 1)) { + testPrintI("%s", str.str().c_str()); + str.str(" "); + } + } + testPrintI("%s", str.str().c_str()); +} + +const char *graphicFormat2str(unsigned int format) +{ + const static char *unknown = "unknown"; + + for (unsigned int n1 = 0; n1 < NUMA(graphicFormat); n1++) { + if (format == graphicFormat[n1].format) { + return graphicFormat[n1].desc; + } + } + + return unknown; +} + +/* + * Vector Random Select + * + * Prandomly selects and returns num elements from vec. + */ +template <class T> +vector<T> vectorRandSelect(const vector<T>& vec, size_t num) +{ + vector<T> rv = vec; + + while (rv.size() > num) { + rv.erase(rv.begin() + testRandMod(rv.size())); + } + + return rv; +} + +/* + * Vector Or + * + * Or's togethen the values of each element of vec and returns the result. + */ +template <class T> +T vectorOr(const vector<T>& vec) +{ + T rv = 0; + + for (size_t n1 = 0; n1 < vec.size(); n1++) { + rv |= vec[n1]; + } + + return rv; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DoNotDisturb.java b/packages/SystemUI/src/com/android/systemui/statusbar/DoNotDisturb.java new file mode 100644 index 0000000..9e44e71 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DoNotDisturb.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 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.systemui.statusbar; + +import android.app.StatusBarManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +import com.android.systemui.statusbar.policy.Prefs; + +public class DoNotDisturb implements SharedPreferences.OnSharedPreferenceChangeListener { + private Context mContext; + private StatusBarManager mStatusBar; + SharedPreferences mPrefs; + private boolean mDoNotDisturb; + + public DoNotDisturb(Context context) { + mContext = context; + mStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE); + mPrefs = Prefs.read(context); + mPrefs.registerOnSharedPreferenceChangeListener(this); + mDoNotDisturb = mPrefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT); + updateDisableRecord(); + } + + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + final boolean val = prefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF, + Prefs.DO_NOT_DISTURB_DEFAULT); + if (val != mDoNotDisturb) { + mDoNotDisturb = val; + updateDisableRecord(); + } + } + + private void updateDisableRecord() { + final int disabled = StatusBarManager.DISABLE_NOTIFICATION_ICONS + | StatusBarManager.DISABLE_NOTIFICATION_ALERTS + | StatusBarManager.DISABLE_NOTIFICATION_TICKER; + mStatusBar.disable(mDoNotDisturb ? disabled : 0); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java index d7f3730..472a225 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java @@ -53,6 +53,8 @@ public abstract class StatusBar extends SystemUI implements CommandQueue.Callbac protected abstract View makeStatusBarView(); protected abstract int getStatusBarGravity(); + private DoNotDisturb mDoNotDisturb; + public void start() { // First set up our views and stuff. View sb = makeStatusBarView(); @@ -127,5 +129,7 @@ public abstract class StatusBar extends SystemUI implements CommandQueue.Callbac + " imeButton=" + switches[3] ); } + + mDoNotDisturb = new DoNotDisturb(mContext); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DoNotDisturbController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DoNotDisturbController.java new file mode 100644 index 0000000..94c8aa5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DoNotDisturbController.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 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.systemui.statusbar.policy; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.util.Slog; +import android.view.IWindowManager; +import android.widget.CompoundButton; + +public class DoNotDisturbController implements CompoundButton.OnCheckedChangeListener, + SharedPreferences.OnSharedPreferenceChangeListener { + private static final String TAG = "StatusBar.DoNotDisturbController"; + + SharedPreferences mPrefs; + private Context mContext; + private CompoundButton mCheckBox; + + private boolean mDoNotDisturb; + + public DoNotDisturbController(Context context, CompoundButton checkbox) { + mContext = context; + + mPrefs = Prefs.read(context); + mPrefs.registerOnSharedPreferenceChangeListener(this); + mDoNotDisturb = mPrefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF, Prefs.DO_NOT_DISTURB_DEFAULT); + + mCheckBox = checkbox; + checkbox.setOnCheckedChangeListener(this); + + checkbox.setChecked(!mDoNotDisturb); + } + + // The checkbox is ON for notifications coming in and OFF for Do not disturb, so we + // don't have a double negative. + public void onCheckedChanged(CompoundButton view, boolean checked) { + //Slog.d(TAG, "onCheckedChanged checked=" + checked + " mDoNotDisturb=" + mDoNotDisturb); + final boolean value = !checked; + if (value != mDoNotDisturb) { + SharedPreferences.Editor editor = Prefs.edit(mContext); + editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, value); + editor.apply(); + } + } + + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + final boolean val = prefs.getBoolean(Prefs.DO_NOT_DISTURB_PREF, + Prefs.DO_NOT_DISTURB_DEFAULT); + if (val != mDoNotDisturb) { + mDoNotDisturb = val; + mCheckBox.setChecked(!val); + } + } + + public void release() { + mPrefs.unregisterOnSharedPreferenceChangeListener(this); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java new file mode 100644 index 0000000..05eafe8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 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.systemui.statusbar.policy; + +import android.content.Context; +import android.content.SharedPreferences; + +public class Prefs { + private static final String SHARED_PREFS_NAME = "status_bar"; + + // a boolean + public static final String DO_NOT_DISTURB_PREF = "do_not_disturb"; + public static final boolean DO_NOT_DISTURB_DEFAULT = false; + + public static SharedPreferences read(Context context) { + return context.getSharedPreferences(Prefs.SHARED_PREFS_NAME, Context.MODE_PRIVATE); + } + + public static SharedPreferences.Editor edit(Context context) { + return context.getSharedPreferences(Prefs.SHARED_PREFS_NAME, Context.MODE_PRIVATE).edit(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java index f9ba908..d1f8dd0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java @@ -31,12 +31,14 @@ import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.statusbar.policy.AirplaneModeController; import com.android.systemui.statusbar.policy.AutoRotateController; +import com.android.systemui.statusbar.policy.DoNotDisturbController; public class SettingsView extends LinearLayout implements View.OnClickListener { static final String TAG = "SettingsView"; AirplaneModeController mAirplane; AutoRotateController mRotate; + DoNotDisturbController mDoNotDisturb; public SettingsView(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -57,6 +59,8 @@ public class SettingsView extends LinearLayout implements View.OnClickListener { findViewById(R.id.network).setOnClickListener(this); mRotate = new AutoRotateController(context, (CompoundButton)findViewById(R.id.rotate_checkbox)); + mDoNotDisturb = new DoNotDisturbController(context, + (CompoundButton)findViewById(R.id.do_not_disturb_checkbox)); findViewById(R.id.settings).setOnClickListener(this); } @@ -64,6 +68,7 @@ public class SettingsView extends LinearLayout implements View.OnClickListener { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mAirplane.release(); + mDoNotDisturb.release(); } public void onClick(View v) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 3cae088..5f4d542 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -580,12 +580,12 @@ public class TabletStatusBar extends StatusBar { if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); // synchronize with current shadow state - mShadowController.hideElement(mNotificationArea); + mShadowController.hideElement(mNotificationIconArea); mTicker.halt(); } else { Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); // synchronize with current shadow state - mShadowController.showElement(mNotificationArea); + mShadowController.showElement(mNotificationIconArea); } } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index cc01bc5..069e1b8 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -110,7 +110,7 @@ public abstract class DataConnectionTracker extends Handler { public static final int EVENT_CLEAN_UP_CONNECTION = 34; protected static final int EVENT_CDMA_OTA_PROVISION = 35; protected static final int EVENT_RESTART_RADIO = 36; - protected static final int EVENT_SET_MASTER_DATA_ENABLE = 37; + protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = 37; protected static final int EVENT_RESET_DONE = 38; /***** Constants *****/ @@ -126,8 +126,9 @@ public abstract class DataConnectionTracker extends Handler { protected static final int DISABLED = 0; protected static final int ENABLED = 1; - // responds to the setDataEnabled call - used independently from the APN requests - protected boolean mMasterDataEnabled = true; + // responds to the setInternalDataEnabled call - used internally to turn off data + // for example during emergency calls + protected boolean mInternalDataEnabled = true; protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; @@ -489,9 +490,9 @@ public abstract class DataConnectionTracker extends Handler { onCleanUpConnection(tearDown, (String) msg.obj); break; - case EVENT_SET_MASTER_DATA_ENABLE: + case EVENT_SET_INTERNAL_DATA_ENABLE: boolean enabled = (msg.arg1 == ENABLED) ? true : false; - onSetDataEnabled(enabled); + onSetInternalDataEnabled(enabled); break; case EVENT_RESET_DONE: @@ -505,23 +506,13 @@ public abstract class DataConnectionTracker extends Handler { } /** - * Report the current state of data connectivity (enabled or disabled) - * - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - public synchronized boolean getDataEnabled() { - return (mMasterDataEnabled && dataEnabled[APN_DEFAULT_ID]); - } - - /** * Report on whether data connectivity is enabled * * @return {@code false} if data connectivity has been explicitly disabled, * {@code true} otherwise. */ public synchronized boolean getAnyDataEnabled() { - return (mMasterDataEnabled && (enabledCount != 0)); + return (mInternalDataEnabled && (enabledCount != 0)); } protected abstract void startNetStatPoll(); @@ -676,7 +667,7 @@ public abstract class DataConnectionTracker extends Handler { */ protected boolean isDataPossible() { boolean possible = (isDataAllowed() - && !(getDataEnabled() && (mState == State.FAILED || mState == State.IDLE))); + && !(getAnyDataEnabled() && (mState == State.FAILED || mState == State.IDLE))); if (!possible && DBG && isDataAllowed()) { log("Data not possible. No coverage: dataState = " + mState); } @@ -847,20 +838,20 @@ public abstract class DataConnectionTracker extends Handler { * {@code false}) data * @return {@code true} if the operation succeeded */ - public boolean setDataEnabled(boolean enable) { + public boolean setInternalDataEnabled(boolean enable) { if (DBG) - log("setDataEnabled(" + enable + ")"); + log("setInternalDataEnabled(" + enable + ")"); - Message msg = obtainMessage(EVENT_SET_MASTER_DATA_ENABLE); + Message msg = obtainMessage(EVENT_SET_INTERNAL_DATA_ENABLE); msg.arg1 = (enable ? ENABLED : DISABLED); sendMessage(msg); return true; } - protected void onSetDataEnabled(boolean enable) { - if (mMasterDataEnabled != enable) { + protected void onSetInternalDataEnabled(boolean enable) { + if (mInternalDataEnabled != enable) { synchronized (this) { - mMasterDataEnabled = enable; + mInternalDataEnabled = enable; } if (enable) { mRetryMgr.resetRetryCount(); diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 69b7de6..25ad48d 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -1325,36 +1325,6 @@ public interface Phone { SimulatedRadioControl getSimulatedRadioControl(); /** - * Allow mobile data connections. - * @return {@code true} if the operation started successfully - * <br/>{@code false} if it - * failed immediately.<br/> - * Even in the {@code true} case, it may still fail later - * during setup, in which case an asynchronous indication will - * be supplied. - */ - boolean enableDataConnectivity(); - - /** - * Disallow mobile data connections, and terminate any that - * are in progress. - * @return {@code true} if the operation started successfully - * <br/>{@code false} if it - * failed immediately.<br/> - * Even in the {@code true} case, it may still fail later - * during setup, in which case an asynchronous indication will - * be supplied. - */ - boolean disableDataConnectivity(); - - /** - * Report the current state of data connectivity (enabled or disabled) - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - boolean isDataConnectivityEnabled(); - - /** * Enables the specified APN type. Only works for "special" APN types, * i.e., not the default APN. * @param type The desired APN type. Cannot be {@link #APN_TYPE_DEFAULT}. diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index fe4fdb3..fce7b9b 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -941,10 +941,6 @@ public abstract class PhoneBase extends Handler implements Phone { logUnexpectedCdmaMethodCall("unsetOnEcbModeExitResponse"); } - public boolean isDataConnectivityEnabled() { - return mDataConnection.getDataEnabled(); - } - public String[] getActiveApnTypes() { return mDataConnection.getActiveApnTypes(); } diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index 2e79762..219efbb 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -650,14 +650,6 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getSimulatedRadioControl(); } - public boolean enableDataConnectivity() { - return mActivePhone.enableDataConnectivity(); - } - - public boolean disableDataConnectivity() { - return mActivePhone.disableDataConnectivity(); - } - public int enableApnType(String type) { return mActivePhone.enableApnType(type); } @@ -666,10 +658,6 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.disableApnType(type); } - public boolean isDataConnectivityEnabled() { - return mActivePhone.isDataConnectivityEnabled(); - } - public boolean isDataConnectivityPossible() { return mActivePhone.isDataConnectivityPossible(); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 099bc30..1e77589 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -478,10 +478,6 @@ public class CDMAPhone extends PhoneBase { return mSST.cellLoc; } - public boolean disableDataConnectivity() { - return mDataConnection.setDataEnabled(false); - } - public CdmaCall getForegroundCall() { return mCT.foregroundCall; } @@ -761,21 +757,6 @@ public class CDMAPhone extends PhoneBase { return ret; } - public boolean enableDataConnectivity() { - - // block data activities when phone is in emergency callback mode - if (mIsPhoneInEcmState) { - Intent intent = new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS); - ActivityManagerNative.broadcastStickyIntent(intent, null); - return false; - } else if ((mCT.state == Phone.State.OFFHOOK) && mCT.isInEmergencyCall()) { - // Do not allow data call to be enabled when emergency call is going on - return false; - } else { - return mDataConnection.setDataEnabled(true); - } - } - public boolean getIccRecordsLoaded() { return mRuimRecords.getRecordsLoaded(); } @@ -921,7 +902,7 @@ public class CDMAPhone extends PhoneBase { // send an Intent sendEmergencyCallbackModeChange(); // Re-initiate data connection - mDataConnection.setDataEnabled(true); + mDataConnection.setInternalDataEnabled(true); } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java index 325c2e1..a89f783 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -1058,7 +1058,7 @@ public final class CdmaCallTracker extends CallTracker { if (PhoneNumberUtils.isEmergencyNumber(dialString)) { if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall"); mIsInEmergencyCall = true; - phone.disableDataConnectivity(); + phone.mDataConnection.setInternalDataEnabled(false); } } @@ -1075,8 +1075,7 @@ public final class CdmaCallTracker extends CallTracker { } if (inEcm.compareTo("false") == 0) { // Re-initiate data connection - // TODO - can this be changed to phone.enableDataConnectivity(); - phone.mDataConnection.setDataEnabled(true); + phone.mDataConnection.setInternalDataEnabled(true); } } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index f7664ca..b005cd3 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -204,7 +204,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { (mCdmaPhone.mSST.isConcurrentVoiceAndData() || mPhone.getState() == Phone.State.IDLE) && !roaming && - mMasterDataEnabled && + mInternalDataEnabled && desiredPowerState && !mPendingRestartRadio && !mCdmaPhone.needsOtaServiceProvisioning(); @@ -222,7 +222,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState(); } if (roaming) reason += " - Roaming"; - if (!mMasterDataEnabled) reason += " - mMasterDataEnabled= false"; + if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false"; if (!desiredPowerState) reason += " - desiredPowerState= false"; if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true"; if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning"; diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index fc03d1a..628f11a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -1084,14 +1084,6 @@ public class GSMPhone extends PhoneBase { mDataConnection.setDataOnRoamingEnabled(enable); } - public boolean enableDataConnectivity() { - return mDataConnection.setDataEnabled(true); - } - - public boolean disableDataConnectivity() { - return mDataConnection.setDataEnabled(false); - } - /** * Removes the given MMI from the pending list and notifies * registrants that it is complete. diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index b41402c..4713c24 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -284,7 +284,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) && mGsmPhone.mSIMRecords.getRecordsLoaded() && mPhone.getState() == Phone.State.IDLE && - mMasterDataEnabled && + mInternalDataEnabled && (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) && !mIsPsRestricted && desiredPowerState; @@ -297,7 +297,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (mPhone.getState() != Phone.State.IDLE) { reason += " - PhoneState= " + mPhone.getState(); } - if (!mMasterDataEnabled) reason += " - mMasterDataEnabled= false"; + if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false"; if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) { reason += " - Roaming and data roaming not enabled"; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 08f3c7a..b901e0d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -366,13 +366,59 @@ public class Canvas_Delegate { /*package*/ static void native_concat(int nCanvas, int nMatrix) { - // FIXME - throw new UnsupportedOperationException(); + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); + if (canvasDelegate == null) { + assert false; + return; + } + + Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); + if (matrixDelegate == null) { + assert false; + return; + } + + // get the current top graphics2D object. + Graphics2D g = canvasDelegate.getGraphics2d(); + + // get its current matrix + AffineTransform currentTx = g.getTransform(); + // get the AffineTransform of the given matrix + AffineTransform matrixTx = matrixDelegate.getAffineTransform(); + + // combine them so that the given matrix is applied after. + currentTx.preConcatenate(matrixTx); + + // give it to the graphics2D as a new matrix replacing all previous transform + g.setTransform(currentTx); } /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) { - // FIXME - throw new UnsupportedOperationException(); + // get the delegate from the native int. + Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); + if (canvasDelegate == null) { + assert false; + } + + Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); + if (matrixDelegate == null) { + assert false; + } + + // get the current top graphics2D object. + Graphics2D g = canvasDelegate.getGraphics2d(); + + // get the AffineTransform of the given matrix + AffineTransform matrixTx = matrixDelegate.getAffineTransform(); + + // give it to the graphics2D as a new matrix replacing all previous transform + g.setTransform(matrixTx); + + // FIXME: log +// if (mLogger != null && matrixDelegate.hasPerspective()) { +// mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor."); +// } } /*package*/ static boolean native_clipRect(int nCanvas, diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 6e80268..b2333f6 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -64,7 +64,7 @@ public final class Matrix_Delegate { return null; } - return getAffineTransform(delegate); + return delegate.getAffineTransform(); } public static boolean hasPerspective(Matrix m) { @@ -74,7 +74,7 @@ public final class Matrix_Delegate { return false; } - return (delegate.mValues[6] != 0 || delegate.mValues[7] != 0 || delegate.mValues[8] != 1); + return delegate.hasPerspective(); } /** @@ -106,6 +106,18 @@ public final class Matrix_Delegate { return true; } + /** + * Returns an {@link AffineTransform} matching the matrix. + */ + public AffineTransform getAffineTransform() { + return getAffineTransform(mValues); + } + + public boolean hasPerspective() { + return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1); + } + + // ---- native methods ---- @@ -599,7 +611,7 @@ public final class Matrix_Delegate { try { - AffineTransform affineTransform = getAffineTransform(d); + AffineTransform affineTransform = d.getAffineTransform(); AffineTransform inverseTransform = affineTransform.createInverse(); inv_mtx.mValues[0] = (float)inverseTransform.getScaleX(); inv_mtx.mValues[1] = (float)inverseTransform.getShearX(); @@ -713,10 +725,6 @@ public final class Matrix_Delegate { // ---- Private helper methods ---- - private static AffineTransform getAffineTransform(Matrix_Delegate d) { - return getAffineTransform(d.mValues); - } - /*package*/ static AffineTransform getAffineTransform(float[] matrix) { // the AffineTransform constructor takes the value in a different order // for a matrix [ 0 1 2 ] diff --git a/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java new file mode 100644 index 0000000..4d4ec7f --- /dev/null +++ b/tools/layoutlib/bridge/src/android/os/Handler_Delegate.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + + +/** + * Delegate overriding selected methods of android.os.Handler + * + * Through the layoutlib_create tool, selected methods of Handler have been replaced + * by calls to methods of the same name in this delegate class. + * + * + */ +public class Handler_Delegate { + + // -------- Delegate methods + + /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) { + // get the callback + IHandlerCallback callback = sCallbacks.get(); + if (callback != null) { + callback.sendMessageAtTime(handler, msg, uptimeMillis); + } + return true; + } + + // -------- Delegate implementation + + public interface IHandlerCallback { + void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis); + } + + private final static ThreadLocal<IHandlerCallback> sCallbacks = + new ThreadLocal<IHandlerCallback>(); + + public static void setCallback(IHandlerCallback callback) { + sCallbacks.set(callback); + } + +} diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java new file mode 100644 index 0000000..be222fc --- /dev/null +++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import com.android.layoutlib.bridge.impl.DelegateManager; + +/** + * Delegate implementing the native methods of android.os.SystemClock + * + * Through the layoutlib_create tool, the original native methods of SystemClock have been replaced + * by calls to methods of the same name in this delegate class. + * + * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager} + * around to map int to instance of the delegate. + * + */ +public class SystemClock_Delegate { + private static long sBootTime = System.currentTimeMillis(); + + /*package*/ static boolean setCurrentTimeMillis(long millis) { + return true; + } + + /** + * Returns milliseconds since boot, not counting time spent in deep sleep. + * <b>Note:</b> This value may get reset occasionally (before it would + * otherwise wrap around). + * + * @return milliseconds of non-sleep uptime since boot. + */ + /*package*/ static long uptimeMillis() { + return System.currentTimeMillis() - sBootTime; + } + + /** + * Returns milliseconds since boot, including time spent in sleep. + * + * @return elapsed milliseconds since boot. + */ + /*package*/ static long elapsedRealtime() { + return System.currentTimeMillis() - sBootTime; + } + + /** + * Returns milliseconds running in the current thread. + * + * @return elapsed milliseconds in the thread + */ + /*package*/ static long currentThreadTimeMillis() { + return System.currentTimeMillis(); + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 35ba73d..2de1cbb 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -40,6 +40,7 @@ import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; /** * Main entry point of the LayoutLib Bridge. @@ -57,6 +58,12 @@ public final class Bridge extends LayoutBridge { } /** + * Lock to ensure only one rendering/inflating happens at a time. + * This is due to some singleton in the Android framework. + */ + private final static ReentrantLock sLock = new ReentrantLock(); + + /** * Maps from id to resource name/type. This is for android.R only. */ private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>(); @@ -160,7 +167,6 @@ public final class Bridge extends LayoutBridge { BridgeAssetManager.initSystem(); - // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener // on static (native) methods which prints the signature on the console and // throws an exception. @@ -252,27 +258,6 @@ public final class Bridge extends LayoutBridge { } /** - * Sets a 9 patch chunk in a project cache or in the framework cache. - * @param value the path of the 9 patch - * @param ninePatch the 9 patch object - * @param projectKey the key of the project, or null to put the bitmap in the framework cache. - */ - public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) { - if (projectKey != null) { - Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey); - - if (map == null) { - map = new HashMap<String, SoftReference<NinePatchChunk>>(); - sProject9PatchCache.put(projectKey, map); - } - - map.put(value, new SoftReference<NinePatchChunk>(ninePatch)); - } else { - sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch)); - } - } - - /** * Starts a layout session by inflating and rendering it. The method returns a * {@link ILayoutScene} on which further actions can be taken. * @@ -306,27 +291,25 @@ public final class Bridge extends LayoutBridge { public BridgeLayoutScene createScene(SceneParams params) { try { SceneResult lastResult = SceneResult.SUCCESS; - LayoutSceneImpl scene = null; - synchronized (this) { - try { - scene = new LayoutSceneImpl(params); - - scene.prepare(); + LayoutSceneImpl scene = new LayoutSceneImpl(params); + try { + scene.prepareThread(); + lastResult = scene.init(params.getTimeout()); + if (lastResult == SceneResult.SUCCESS) { lastResult = scene.inflate(); if (lastResult == SceneResult.SUCCESS) { lastResult = scene.render(); } - } finally { - if (scene != null) { - scene.cleanup(); - } } + } finally { + scene.release(); + scene.cleanupThread(); } - return new BridgeLayoutScene(this, scene, lastResult); + return new BridgeLayoutScene(scene, lastResult); } catch (Throwable t) { t.printStackTrace(); - return new BridgeLayoutScene(this, null, new SceneResult("error!", t)); + return new BridgeLayoutScene(null, new SceneResult("error!", t)); } } @@ -343,6 +326,13 @@ public final class Bridge extends LayoutBridge { } /** + * Returns the lock for the bridge + */ + public static ReentrantLock getLock() { + return sLock; + } + + /** * Returns details of a framework resource from its integer value. * @param value the integer value * @return an array of 2 strings containing the resource name and type, or null if the id @@ -461,4 +451,27 @@ public final class Bridge extends LayoutBridge { return null; } + + /** + * Sets a 9 patch chunk in a project cache or in the framework cache. + * @param value the path of the 9 patch + * @param ninePatch the 9 patch object + * @param projectKey the key of the project, or null to put the bitmap in the framework cache. + */ + public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) { + if (projectKey != null) { + Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey); + + if (map == null) { + map = new HashMap<String, SoftReference<NinePatchChunk>>(); + sProject9PatchCache.put(projectKey, map); + } + + map.put(value, new SoftReference<NinePatchChunk>(ninePatch)); + } else { + sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch)); + } + } + + } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java index 8b67166..97bf857 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java @@ -17,6 +17,7 @@ package com.android.layoutlib.bridge; import com.android.layoutlib.api.LayoutScene; +import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; import com.android.layoutlib.api.ViewInfo; import com.android.layoutlib.bridge.impl.LayoutSceneImpl; @@ -33,7 +34,6 @@ import java.util.Map; */ public class BridgeLayoutScene extends LayoutScene { - private final Bridge mBridge; private final LayoutSceneImpl mScene; private SceneResult mLastResult; @@ -58,15 +58,34 @@ public class BridgeLayoutScene extends LayoutScene { } @Override - public SceneResult render() { - - synchronized (mBridge) { - try { - mScene.prepare(); + public SceneResult render(long timeout) { + try { + mScene.prepareThread(); + mLastResult = mScene.acquire(timeout); + if (mLastResult == SceneResult.SUCCESS) { mLastResult = mScene.render(); - } finally { - mScene.cleanup(); } + } finally { + mScene.release(); + mScene.cleanupThread(); + } + + return mLastResult; + } + + @Override + public SceneResult animate(Object targetObject, String animationName, + boolean isFrameworkAnimation, IAnimationListener listener) { + try { + mScene.prepareThread(); + mLastResult = mScene.acquire(SceneParams.DEFAULT_TIMEOUT); + if (mLastResult == SceneResult.SUCCESS) { + mLastResult = mScene.animate(targetObject, animationName, isFrameworkAnimation, + listener); + } + } finally { + mScene.release(); + mScene.cleanupThread(); } return mLastResult; @@ -78,8 +97,7 @@ public class BridgeLayoutScene extends LayoutScene { } - /*package*/ BridgeLayoutScene(Bridge bridge, LayoutSceneImpl scene, SceneResult lastResult) { - mBridge = bridge; + /*package*/ BridgeLayoutScene(LayoutSceneImpl scene, SceneResult lastResult) { mScene = scene; mLastResult = lastResult; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 1011173..affd1c6 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -261,6 +261,41 @@ public final class BridgeResources extends Resources { } @Override + public XmlResourceParser getAnimation(int id) throws NotFoundException { + IResourceValue value = getResourceValue(id, mPlatformResourceFlag); + + if (value != null) { + XmlPullParser parser = null; + + try { + File xml = new File(value.getValue()); + if (xml.isFile()) { + // we need to create a pull parser around the layout XML file, and then + // give that to our XmlBlockParser + parser = new KXmlParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new FileReader(xml)); + + return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); + } + } catch (XmlPullParserException e) { + mContext.getLogger().error(e); + // we'll return null below. + } catch (FileNotFoundException e) { + // this shouldn't happen since we check above. + } + + } + + // id was not found or not resolved. Throw a NotFoundException. + throwException(id); + + // this is not used since the method above always throws + return null; + } + + + @Override public TypedArray obtainAttributes(AttributeSet set, int[] attrs) { return mContext.obtainStyledAttributes(set, attrs); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java new file mode 100644 index 0000000..c20bdfd --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 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.layoutlib.bridge.impl; + +import com.android.layoutlib.api.SceneResult; +import com.android.layoutlib.api.LayoutScene.IAnimationListener; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.os.Handler; +import android.os.Handler_Delegate; +import android.os.Message; +import android.os.Handler_Delegate.IHandlerCallback; + +import java.util.LinkedList; +import java.util.Queue; + +public class AnimationThread extends Thread { + + private static class MessageBundle { + final Handler mTarget; + final Message mMessage; + final long mUptimeMillis; + + MessageBundle(Handler target, Message message, long uptimeMillis) { + mTarget = target; + mMessage = message; + mUptimeMillis = uptimeMillis; + } + } + + private final LayoutSceneImpl mScene; + private final Animator mAnimator; + + Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>(); + private final IAnimationListener mListener; + + public AnimationThread(LayoutSceneImpl scene, Animator animator, IAnimationListener listener) { + mScene = scene; + mAnimator = animator; + mListener = listener; + } + + @Override + public void run() { + mScene.prepareThread(); + try { + Handler_Delegate.setCallback(new IHandlerCallback() { + public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) { + if (msg.what == ValueAnimator.ANIMATION_START || + msg.what == ValueAnimator.ANIMATION_FRAME) { + mQueue.add(new MessageBundle(handler, msg, uptimeMillis)); + } else { + // just ignore. + } + } + }); + + // start the animation. This will send a message to the handler right away, so + // mQueue is filled when this method returns. + mAnimator.start(); + + // loop the animation + do { + // get the next message. + MessageBundle bundle = mQueue.poll(); + if (bundle == null) { + break; + } + + // sleep enough for this bundle to be on time + long currentTime = System.currentTimeMillis(); + if (currentTime < bundle.mUptimeMillis) { + try { + sleep(bundle.mUptimeMillis - currentTime); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // ready to do the work, acquire the scene. + SceneResult result = mScene.acquire(250); + if (result != SceneResult.SUCCESS) { + mListener.done(result); + return; + } + + // process the bundle. If the animation is not finished, this will enqueue + // the next message, so mQueue will have another one. + try { + bundle.mTarget.handleMessage(bundle.mMessage); + if (mScene.render() == SceneResult.SUCCESS) { + mListener.onNewFrame(mScene.getImage()); + } + } finally { + mScene.release(); + } + } while (mQueue.size() > 0); + + mListener.done(SceneResult.SUCCESS); + } finally { + Handler_Delegate.setCallback(null); + mScene.cleanupThread(); + } + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java index f7d249e..0859976 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java @@ -16,6 +16,10 @@ package com.android.layoutlib.bridge.impl; +import static com.android.layoutlib.api.SceneResult.SceneStatus.ERROR_LOCK_INTERRUPTED; +import static com.android.layoutlib.api.SceneResult.SceneStatus.ERROR_TIMEOUT; +import static com.android.layoutlib.api.SceneResult.SceneStatus.SUCCESS; + import com.android.internal.util.XmlUtils; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; @@ -25,7 +29,9 @@ import com.android.layoutlib.api.SceneParams; import com.android.layoutlib.api.SceneResult; import com.android.layoutlib.api.ViewInfo; import com.android.layoutlib.api.IDensityBasedResourceValue.Density; +import com.android.layoutlib.api.LayoutScene.IAnimationListener; import com.android.layoutlib.api.SceneParams.RenderingMode; +import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeInflater; @@ -33,6 +39,8 @@ import com.android.layoutlib.bridge.android.BridgeWindow; import com.android.layoutlib.bridge.android.BridgeWindowSession; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; +import android.animation.AnimatorInflater; +import android.animation.ObjectAnimator; import android.app.Fragment_Delegate; import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; @@ -59,6 +67,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; /** * Class managing a layout "scene". @@ -73,6 +83,12 @@ public class LayoutSceneImpl { private static final int DEFAULT_TITLE_BAR_HEIGHT = 25; private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; + /** + * The current context being rendered. This is set through {@link #acquire(long)} and + * {@link #init(long)}, and unset in {@link #release()}. + */ + private static BridgeContext sCurrentContext = null; + private final SceneParams mParams; // scene state @@ -98,22 +114,35 @@ public class LayoutSceneImpl { /** * Creates a layout scene with all the information coming from the layout bridge API. - * - * This also calls {@link LayoutSceneImpl#prepare()}. * <p> - * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + * This <b>must</b> be followed by a call to {@link LayoutSceneImpl#init()}, which act as a + * call to {@link LayoutSceneImpl#acquire(long)} * * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams) */ public LayoutSceneImpl(SceneParams params) { - // we need to make sure the Looper has been initialized for this thread. - // this is required for View that creates Handler objects. - if (Looper.myLooper() == null) { - Looper.prepare(); - } - // copy the params. mParams = new SceneParams(params); + } + + /** + * Initializes and acquires the scene, creating various Android objects such as context, + * inflater, and parser. + * + * @param timeout the time to wait if another rendering is happening. + * + * @return whether the scene was prepared + * + * @see #acquire(long) + * @see #release() + */ + public SceneResult init(long timeout) { + // acquire the lock. if the result is null, lock was just acquired, otherwise, return + // the result. + SceneResult result = acquireLock(timeout); + if (result != null) { + return result; + } // setup the display Metrics. DisplayMetrics metrics = new DisplayMetrics(); @@ -138,6 +167,9 @@ public class LayoutSceneImpl { mParams.getProjectResources(), mParams.getFrameworkResources(), styleParentMap, mParams.getProjectCallback(), mParams.getLogger()); + // set the current rendering context + sCurrentContext = mContext; + // make sure the Resources object references the context (and other objects) for this // scene mContext.initResources(); @@ -149,7 +181,8 @@ public class LayoutSceneImpl { mWindowBackground = mContext.findItemInStyle(mCurrentTheme, "windowBackground"); mWindowBackground = mContext.resolveResValue(mWindowBackground); - mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme, mContext); + mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme, + mContext); } // build the inflater and parser. @@ -159,44 +192,144 @@ public class LayoutSceneImpl { mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(), mContext, false /* platformResourceFlag */); + + return SceneResult.SUCCESS; } /** - * Prepares the scene for action. - * <p> - * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + * Prepares the current thread for rendering. + * + * Note that while this can be called several time, the first call to {@link #cleanupThread()} + * will do the clean-up, and make the thread unable to do further scene actions. */ - public void prepare() { + public void prepareThread() { // we need to make sure the Looper has been initialized for this thread. // this is required for View that creates Handler objects. if (Looper.myLooper() == null) { Looper.prepare(); } + } + + /** + * Cleans up thread-specific data. After this, the thread cannot be used for scene actions. + * <p> + * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single + * call to this will prevent the thread from doing further scene actions + */ + public void cleanupThread() { + // clean up the looper + Looper.sThreadLocal.remove(); + } + + /** + * Prepares the scene for action. + * <p> + * This call is blocking if another rendering/inflating is currently happening, and will return + * whether the preparation worked. + * + * The preparation can fail if another rendering took too long and the timeout was elapsed. + * + * More than one call to this from the same thread will have no effect and will return + * {@link SceneResult#SUCCESS}. + * + * After scene actions have taken place, only one call to {@link #release()} must be + * done. + * + * @param timeout the time to wait if another rendering is happening. + * + * @return whether the scene was prepared + * + * @see #release() + * + * @throws IllegalStateException if {@link #init(long)} was never called. + */ + public SceneResult acquire(long timeout) { + if (mContext == null) { + throw new IllegalStateException("After scene creation, #init() must be called"); + } + + // acquire the lock. if the result is null, lock was just acquired, otherwise, return + // the result. + SceneResult result = acquireLock(timeout); + if (result != null) { + return result; + } // make sure the Resources object references the context (and other objects) for this // scene mContext.initResources(); + sCurrentContext = mContext; + + return SUCCESS.getResult(); } /** - * Cleans up the scene after an action. + * Acquire the lock so that the scene can be acted upon. * <p> - * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + * This returns null if the lock was just acquired, otherwise it returns + * {@link SceneResult#SUCCESS} if the lock already belonged to that thread, or another + * instance (see {@link SceneResult#getStatus()}) if an error occurred. + * + * @param timeout the time to wait if another rendering is happening. + * @return null if the lock was just acquire or another result depending on the state. + * + * @throws IllegalStateException if the current context is different than the one owned by + * the scene. */ - public void cleanup() { - // clean up the looper - Looper.sThreadLocal.remove(); + private SceneResult acquireLock(long timeout) { + ReentrantLock lock = Bridge.getLock(); + if (lock.isHeldByCurrentThread() == false) { + try { + boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS); + + if (acquired == false) { + return ERROR_TIMEOUT.getResult(); + } + } catch (InterruptedException e) { + return ERROR_LOCK_INTERRUPTED.getResult(); + } + } else { + // This thread holds the lock already. Checks that this wasn't for a different context. + // If this is called by init, mContext will be null and so should sCurrentContext + // anyway + if (mContext != sCurrentContext) { + throw new IllegalStateException("Acquiring different scenes from same thread without releases"); + } + return SUCCESS.getResult(); + } - // Make sure to remove static references, otherwise we could not unload the lib - mContext.disposeResources(); + return null; + } + + /** + * Cleans up the scene after an action. + */ + public void release() { + ReentrantLock lock = Bridge.getLock(); + + // with the use of finally blocks, it is possible to find ourself calling this + // without a successful call to prepareScene. This test makes sure that unlock() will + // not throw IllegalMonitorStateException. + if (lock.isHeldByCurrentThread()) { + // Make sure to remove static references, otherwise we could not unload the lib + mContext.disposeResources(); + sCurrentContext = null; + + lock.unlock(); + } } /** * Inflates the layout. * <p> - * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + * {@link #acquire(long)} must have been called before this. + * + * @throws IllegalStateException if the current context is different than the one owned by + * the scene, or if {@link #init(long)} was not called. */ public SceneResult inflate() { + checkLock(); + try { mViewRoot = new FrameLayout(mContext); @@ -247,10 +380,16 @@ public class LayoutSceneImpl { /** * Renders the scene. * <p> - * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + * {@link #acquire(long)} must have been called before this. + * + * @throws IllegalStateException if the current context is different than the one owned by + * the scene, or if {@link #acquire(long)} was not called. */ public SceneResult render() { + checkLock(); + try { + long current = System.currentTimeMillis(); if (mViewRoot == null) { return new SceneResult("Layout has not been inflated!"); } @@ -329,6 +468,8 @@ public class LayoutSceneImpl { mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext); + System.out.println("rendering (ms): " + (System.currentTimeMillis() - current)); + // success! return SceneResult.SUCCESS; } catch (Throwable e) { @@ -346,6 +487,71 @@ public class LayoutSceneImpl { } /** + * Animate an object + * <p> + * {@link #acquire(long)} must have been called before this. + * + * @throws IllegalStateException if the current context is different than the one owned by + * the scene, or if {@link #acquire(long)} was not called. + */ + public SceneResult animate(Object targetObject, String animationName, + boolean isFrameworkAnimation, IAnimationListener listener) { + checkLock(); + + // find the animation file. + IResourceValue animationResource = null; + int animationId = 0; + if (isFrameworkAnimation) { + animationResource = mContext.getFrameworkResource("anim", animationName); + if (animationResource != null) { + animationId = Bridge.getResourceValue("anim", animationName); + } + } else { + animationResource = mContext.getProjectResource("anim", animationName); + if (animationResource != null) { + animationId = mContext.getProjectCallback().getResourceValue("anim", animationName); + } + } + + if (animationResource != null) { + try { + ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator( + mContext, animationId); + if (anim != null) { + anim.setTarget(targetObject); + + new AnimationThread(this, anim, listener).start(); + + return SceneResult.SUCCESS; + } + } catch (Exception e) { + e.printStackTrace(); + return new SceneResult("", e); + } + } + + return new SceneResult("Failed to find animation"); + } + + /** + * Checks that the lock is owned by the current thread and that the current context is the one + * from this scene. + * + * @throws IllegalStateException if the current context is different than the one owned by + * the scene, or if {@link #acquire(long)} was not called. + */ + private void checkLock() { + ReentrantLock lock = Bridge.getLock(); + if (lock.isHeldByCurrentThread() == false) { + throw new IllegalStateException("scene must be acquired first. see #acquire(long)"); + } + if (sCurrentContext != mContext) { + throw new IllegalStateException("Thread acquired a scene but is rendering a different one"); + } + } + + + /** * Compute style information from the given list of style for the project and framework. * @param themeName the name of the current theme. In order to differentiate project and * platform themes sharing the same name, all project themes must be prepended with diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index bb2e6b3..4440685 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -95,6 +95,7 @@ public final class CreateInfo implements ICreateInfo { */ private final static String[] DELEGATE_METHODS = new String[] { "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;", + "android.os.Handler#sendMessageAtTime", "android.view.View#isInEditMode", // TODO: comment out once DelegateClass is working // "android.content.res.Resources$Theme#obtainStyledAttributes", @@ -118,6 +119,7 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.SweepGradient", "android.graphics.Typeface", "android.graphics.Xfermode", + "android.os.SystemClock", "android.util.FloatMath", }; |